Anubis + Nginx in Docker
posted Jun 07, 2025 by huskee
This could have easily been a gist, but I haven't posted anything in a while over here.
Anubis is a great piece of software written by Xe Iaso that "weighs the souls of requests" made towards your web server. Cool description aside, it is a pretty well made AI bot/scraper/aggresive crawler blocker, that uses fun Proof of Work stuff that I can't even begin to comprehend.
I wanted to set up Anubis on hackerspace.gr's new server, but ran into some issues, so here I am writing an article on the configuration that helped me overcome them.
Small rant
The project is great, but the documentation doesn't cover much, and even the covered topics can be difficult to make sense. Just to be clear, I am well aware that it's a one-person effort that went viral overnight, and I'm not throwing ANY shade towards the author. (may be throwing some shade towards the big ass companies/orgs that have been using Anubis (to save their asses from AI bots essentially DDoSing them just cause the sky is blue) and have not contributed even a bit)
Setting up shop
The directory structure looks something like this:
web
├── compose.yml
└── nginx
├── etc
│ ├── conf.d
│ │ └── default.conf
│ └── includes
│ └── anubis.conf
└── run
└── nginx.sock
Docker compose
Our compose file is simple: create the nginx container, open ports 80 and 443, and attach ./nginx/etc
to nginx's config location and ./nginx/run
to /tmp, where we'll open a socket later.
Anubis just needs some envvars set (most important of them being TARGET
, which we set to our Nginx socket), and the same volume that we attached to the Nginx container for the socket.
compose.yml
:
services:
nginx:
image: nginx:mainline-alpine
container_name: nginx
volumes:
- ./nginx/etc:/etc/nginx
- ./nginx/run:/tmp
{various other volumes}
ports:
- "80:80"
- "443:443"
- "443:443/tcp"
depends_on:
{all services reverse proxied by nginx}
restart: always
anubis:
image: ghcr.io/techarohq/anubis:latest
container_name: anubis
environment:
COOKIE_DOMAIN: "{your domain name}"
TARGET: "unix:/tmp/nginx.sock"
OG_PASSTHROUGH: "true"
OG_EXPIRY_TIME: "1h"
OG_CACHE_CONSIDER_HOST": "true"
volumes:
- ./nginx/run:/tmp
Nginx config
The flow we're trying to achieve looks like this:
flowchart LR Cl(client) subgraph nginx http(80/tcp) https(443/tcp) unix(unix:/tmp/nginx) end An(anubis) Ba(backend) Ac(accept request) Re(reject request) Cl-->|connect to http site|http http-->https Cl-->|connect to https site|https https-->|proxy to anubis container|An An-->|pass challenge|Ac An--->|fail challenge|Re Ac-->|proxy back to nginx container|unix unix-->Ba
Basically, nginx handles all the webserver-y stuff, and then passes the request to anubis. If the client passes the challenge, Anubis passes the request back to Nginx through a unix socket, which has been set up to serve our content/reverse proxy some other container.
First of all, to save some time, we create anubis.conf
in the nginx/etc/includes
directory. We'll include this in every site's configuration (instead of adding the full config to each site manually).
nginx/etc/includes/anubis.conf
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://anubis:8923;
}
This tells Nginx to pass all requests to Anubis, which listens on port 8923 by default. If you'd like, you could define an upstream (with a backup in case Anubis fails for some reason) like this:
upstream anubis {
server anubis:8923;
# fall back to unix socket in case anubis fails
server unix:/tmp/nginx.sock backup;
}
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://anubis;
}
Then, we need to modify our site's configuration. We redirect all http traffic to https, include includes/anubis.conf
in the https section, and move all other directives to a separate section that's going to listen on /tmp/nginx.sock
for connections.
nginx/etc/conf.d/default.conf
server {
listen 80;
listen [::]:80;
server_name {your domain name};
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name {your domain name};
ssl_certificate {path to ssl cert};
ssl_certificate_key {path to ssl cert key};
include includes/anubis.conf;
}
server {
listen unix:/tmp/nginx.sock;
server_name {your domain name};
set_real_ip_from unix:;
{rest of your configuration}
}
The highlighted line is pretty important. If you leave this out, anything that tries to determine the client's real IP address (e.g. MediaWiki) will fall flat on its face, as $REMOTE_ADDR
will always be set to unix:
.
Testing
After all this, you should be good to go. Reload nginx (in this example, this could be done with docker exec nginx nginx -s reload
), and test if Anubis is working by accessing your website. If everything works, you'll probably see this:

And then you'll probably get your webpage.
Closing remarks
The whole ordeal took me around 3-4 days. I strongly believe it shouldn't have.
I'm kinda disappointed in the community (and therefore also in myself) for the lack of comprehensible documentation for this really awesome project. There's absolutely no way nobody else had the same problems as I did, but they either abandoned their efforts or found a way to make it work and just kept it to themselves. That isn't so great. A big part of the whole open-source thing is contributing to a project instead of just using it (provided you have the time, tools and knowledge).