How to Use Anubis: The Beginner's Guide to Proof-of-Work Bot Protection

CodexFuse Team · 2026-11-01T15:43:20.445Z · 15 min read

How to Use Anubis: The Beginner's Guide to Proof-of-Work Bot Protection

Anubis is an open-source reverse proxy that stops AI scrapers and abusive bots with a proof-of-work challenge — not the Egyptian god, not a Hades 2 weapon, and not the houseplant your search results may have suggested. Every visitor's browser solves a small SHA-256 puzzle in under a second, a signed cookie is set, and the request passes through. Bots that don't run JavaScript — which is how most LLM crawlers still operate — can never complete the challenge. This guide shows you how to use Anubis end to end: the mental model, a Docker deploy, the TARGET lesson, where it sits in your proxy chain, the DIFFICULTY dial, good-bot allowlists, TLS/cookies, metrics, and the honest Cloudflare question. If you want to run a quick website security scanner first to see what your public surface looks like, do that before you start; it takes under a minute.

What Is Anubis?

Anubis is a small, stateless reverse proxy written by Xe Iaso of Techaro (23,868 GitHub stars at write time, MIT license), created after relentless AI-crawler traffic started causing DDoS-like outages on his Git server. Instead of blocking bots by IP or user-agent — both trivially spoofed — it puts a cryptographic toll booth in front of your site: a proof-of-work (PoW) challenge page that real browsers solve transparently while scriptless crawlers are stopped cold. Because it is a reverse proxy, it works in front of any HTTP service — a blog, a Git server, a dashboard — with no changes to the app itself.

How a request flows through Anubis:

  1. A visitor requests your site through Anubis.
  2. Anubis serves a challenge page containing a SHA-256 proof-of-work puzzle.
  3. The visitor's browser solves the puzzle in JavaScript — typically well under a second — and submits the answer.
  4. Anubis verifies the solution, sets a signed cookie, and proxies the request to your upstream.
  5. Later requests carrying a valid cookie pass straight through with no delay — challenge cookies are EdDSA-signed and last 7 days by default, so solved visitors don't see the page again for a week.

No database, no session store: the cookie is cryptographically verified, so you can run multiple Anubis instances with no coordination. The default configuration is tuned for most use cases out of the box.

What Does Anubis Actually Stop — and What Doesn't It?

Anubis stops the exact class of abuser that is hurting small sites right now: aggressive AI scrapers and abusive crawlers that fetch pages without executing JavaScript. LLM training bots historically have not run JavaScript, so any JavaScript check — including Anubis' challenge — stops them. That is the whole mechanism; there is no magic in the puzzle beyond making JS-less access impossible.

What it does not do is equally important. A bot that runs a full headless browser can solve the challenge — the puzzle only makes scraping computationally expensive, not impossible. As one widely shared counter-piece puts it, the cost for an LLM company to solve Anubis' puzzles at scale is effectively zero; it works today only because the bots simply don't run JavaScript. Treat Anubis as a strong deterrent against the current generation of scrapers, not as a firewall against a determined, well-funded adversary. It is also a legitimate DDoS-cost-raiser: proof-of-work bot protection makes floods expensive per-request. If your goal is specifically to block AI scrapers and stop LLM scrapers from draining your bandwidth, this is the right tool — with the caveats in this section.

The honest framing matters because it changes what you expect. Anubis is a filter, not a wall — and that filter happens to be exactly what most small sites need.

How Do I Install Anubis With Docker?

Installing Anubis takes about five minutes with Docker Compose — this is the anubis docker path this anubis tutorial uses, and the one almost every beginner should pick for their first anubis install. You need a server with Docker and a domain pointed at it. (Prefer packages? Official .deb/.rpm builds and tarballs with a systemd unit ship from the GitHub releases — same settings, no container.)

  1. Create a directory for the deployment, e.g. ~/anubis.
  2. Create docker-compose.yml with one service using the official image ghcr.io/techarohq/anubis:latest, exposing the challenge port and setting TARGET to your upstream app (e.g. http://192.168.1.10:3000).
  3. Add a bind mount for the bot policy file (see the allowlist section): ./botPolicy.yaml:/data/cfg/botPolicy.yaml:ro.
  4. Run docker compose up -d and confirm the container is healthy with docker compose ps.
  5. Point your reverse proxy (Caddy or nginx) at Anubis' listen port instead of directly at your app.
  6. Visit your site in a fresh browser tab: you should see the challenge page flash by once, then your site loads normally.

If Docker is new to you, read this Docker beginner guide first — everything below assumes you can run docker compose. Pin the image tag to a verified release (see the version note below) rather than tracking :latest blindly on production.

Version note (verified 2026-11-01): current stable is v1.28.2 (announced 2026-10-23; v1.28.1 on Oct 9, v1.28.0 on Oct 8 — which added opt-in WASM challenges, off by default, and fixed a search-engine reverse-DNS spoofing bypass). Confirm the current release on the project's GitHub before deploying, because env-var names and defaults move between releases.

What Should I Set TARGET To? (The One-Instance-Per-Upstream Lesson)

TARGET is the single most important setting — the anubis target is the URL of the upstream service Anubis protects, and the docs are explicit — "one instance of Anubis must be used per service you are protecting." Point it at the internal address of your app — for example http://my-app:3000 on the same Docker network, or http://192.168.1.50:3000 for a LAN service. (Default TARGET is http://localhost:3923 — change it.)

This is the lesson beginners trip over: you cannot point one Anubis container at three different websites. Each protected site gets its own Anubis instance (they are tiny — tens of megabytes of RAM). The documented workaround for many sites on one host is a two-layer Caddy sandwich: one outer Caddy terminates TLS and routes every domain into a single Anubis that fronts a second, no-TLS Caddy doing final per-domain routing — a community-tested pattern that avoids running dozens of Anubis containers.

Where Does Anubis Sit in My Proxy Chain?

Anubis lives between your TLS-terminating reverse proxy and your app: visitor → Caddy/nginx (TLS) → Anubis (challenge) → your app. It never terminates TLS itself in the common setup; your existing proxy keeps handling certificates and hands plain HTTP to Anubis.

The two classic placements are the Caddy sandwich (Caddy on ports 80/443 → Anubis → a second Caddy or the app directly) and the nginx sandwich from an early deploy diary: SSL terminates at nginx, traffic flows nginx → Anubis → back through nginx to the origin. Either way, the rule is simple — Anubis must see every request before your app does, and nothing may route around it. If you self-host with Caddy, this Caddy beginner guide covers the reverse-proxy half of the setup; Anubis covers the other half.

One subtlety for the nginx sandwich: if you use nginx's auth_request subrequests, a quirk in older Anubis versions rejected requests whose Content-Type was multipart/form-data with an empty body — verify you run a current release.

What Does the DIFFICULTY Setting Do?

DIFFICULTY (default 4, counted in leading zero nibbles) controls how hard the SHA-256 puzzle is — each extra nibble multiplies the client's hashing work roughly sixteenfold. The browser solves it multithreaded with WebCrypto (the fast algorithm, default). Higher values mean more CPU work for the client — every real visitor, not just bots. The dial is the central tradeoff of Anubis: difficulty above 4 makes real visitors wait noticeably, especially on phones.

Anubis DIFFICULTY settings and their effect
DIFFICULTYChallenge feel (typical laptop)Good for
1–2Instant, invisibleTesting only — too weak to deter anything
3Brief flickerVery low-traffic personal sites
4 (default)Under a secondMost sites — the recommended starting point
5–6Several secondsSites under active scraping attack; expect complaints
7+Long wait, hot phonesEmergency DDoS posture only — not a daily setting (the docs label 16 "impossible": intentional CPU waste for bad bots, never for visitors)

Start at the default 4 and raise it only while you are actively being scraped, then dial back down. Because the cost falls on your visitors' devices, a permanent high difficulty is a UX tax you pay on every page load — the puzzle never stops being solved, it just gets slower.

How Do I Keep Googlebot and Other Good Bots Working?

The default policy aggressively challenges anything browser-like — including Googlebot, and the project calls lost indexing "a feature, not a bug." If search-engine crawlers can't pass the challenge, your site will de-index. The fix is a bot policy file: a YAML allowlist (mounted read-only at /data/cfg/botPolicy.yaml, pointed to by POLICY_FNAME) that declares which crawlers pass through untouched. Rules match path, user-agent regex, headers, and IP ranges, with ALLOW / DENY / CHALLENGE / WEIGH actions — and anything unmatched is allowed through, so a missing rule fails open rather than breaking your site.

Anubis ships default policies covering well-behaved crawlers, and community edge variants explicitly whitelist SEO bots so rankings survive. The rule is: allowlist search-engine bots before you go live, or the site de-indexes. The docs even show verifying real Googlebot via forward-confirmed reverse DNS (verifyFCrDNS) so spoofed user-agents can't sneak through the allowlist — v1.28.0 closed a bypass where attackers faked search-engine reverse-DNS records. Verify the policy file's bot list against the current release — bot names and the policy format evolve, and a stale file can silently block the crawlers you meant to allow. Bonus: Anubis can also serve a robots.txt that disallows AI scrapers (SERVE_ROBOTS_TXT, off by default) — polite crawlers obey it, which shrinks the challenge workload.

Bot categories and how Anubis should treat them
CategoryExamplesAnubis treatment
Search-engine crawlersGooglebot, BingbotAllowlist — never challenge, or indexing dies
Well-behaved fetchersFeed readers, uptime monitors you ownAllowlist by user-agent or IP
AI training scrapersClaudeBot, GPTBot, CCBotChallenge — they don't run JS, so they fail
Unknown / no user-agentBlank user-agentsDeny — empty user-agents are rejected outright as spec violations

Note the honest boundary again: allowlisting is by user-agent and behavior, and user-agents are spoofable — so prefer the docs' verifyFCrDNS forward-confirmed reverse-DNS check for search engines over bare user-agent matches. Anubis raises the cost of scraping; it does not authenticate crawlers.

How Do TLS, COOKIE_SECURE, and Real Client IPs Work?

Anubis sets its pass cookie with the Secure flag (COOKIE_SECURE defaults to true — keep it that way when TLS terminates at your outer proxy). Because TLS usually terminates at your proxy (Caddy/nginx), Anubis itself sees plain HTTP; if the secure flag were ever off behind HTTPS, browsers would refuse to store the cookie and every visitor would get challenged on every request.

Related: client IPs. Anubis runs behind a reverse proxy in production (USE_REMOTE_ADDRESS unset), reading the client IP from headers rather than the socket. It flattens multi-IP X-Forwarded-For right-to-left and strips private ranges automatically — but if your front proxy is itself a public IP (e.g. Cloudflare), configure the real-IP header (CF-Connecting-IP or CUSTOM_REAL_IP_HEADER) so Anubis sees visitors, not the proxy. Getting this wrong breaks IP-based bot rules and rate limits silently. If your server is directly exposed to the internet, keep Anubis bound to localhost and let the outer proxy be the only public listener — this Tailscale beginner guide covers an even safer pattern: never exposing the raw service at all.

How Do I Monitor Anubis?

Anubis exposes Prometheus metrics on a separate bind address (METRICS_BIND, default :9090, served at /metrics), so your dashboards never mix with public traffic. Scrape it with your existing Prometheus setup and alert on challenge-pass vs. challenge-fail rates: a sudden spike in failures is your early warning that a scraping wave started. Keep the metrics port off the public internet — it is operational data, not a public page.

Anubis vs Cloudflare: Which Should I Use?

This is the question the project's own README answers for you, and honesty requires quoting it: "In most cases, you should not need this and can probably get by using Cloudflare to protect a given origin. However, for circumstances where you can't or won't use Cloudflare, Anubis is there for you." Anubis exists for the can't-or-won't cases — and there are plenty of them.

Anubis vs Cloudflare vs a plain JS-cookie check
AnubisCloudflare (free tier)DIY JS-cookie check
Stops JS-less scrapersYesYes (Bot Fight Mode)Yes
Stops JS-capable scrapersOnly via costBetter heuristicsNo
Self-hosted / no third partyYes — your server onlyNo — traffic via CloudflareYes
Visitor friction<1s puzzle at defaultUsually invisibleNear-zero
DDoS cost-raisingYes (proof-of-work)Yes (much stronger)No
Works without a serverNo — needs a reverse proxyYesEdge variant possible

Use Anubis when you can't or won't route through Cloudflare: homelabs, services that must stay on your own metal, or origins where you want zero third-party traffic inspection. Use Cloudflare when you just want the problem handled — its bot management is the more reliable answer for pure bot protection. Note the crowning irony the counter-piece raises: for the narrow case of "only ClaudeBot bothers me," even a 12-line JS-cookie check in your existing proxy works as well as Anubis, invisibly — Anubis earns its keep when the threat is broader than one well-behaved crawler.

One more lane to name: behavior-based blocking. CrowdSec watches signals and IP reputation rather than challenging every visitor with a puzzle — a different security layer from Anubis' proof-of-work, and the two can complement rather than replace each other.

7 Beginner Mistakes With Anubis

Common Anubis beginner mistakes and fixes
MistakeWhy it hurtsFix
Pointing one instance at several sitesOnly the TARGET is protected; other sites are wide openOne Anubis per upstream, or the two-proxy sandwich pattern
Forgetting COOKIE_SECURE behind TLSBrowsers drop the cookie; every visitor re-challenges foreverKeep COOKIE_SECURE=true (the default) when TLS terminates upstream
Skipping the bot allowlistGooglebot gets challenged → pages de-indexAllowlist search crawlers in botPolicy.yaml before going live
Setting DIFFICULTY to 7 "to be safe"Real visitors on phones wait many seconds per visitStart at 4; raise only during active attacks, then lower
Leaving the metrics port publicOperational data exposed to the internetBind METRICS_BIND to localhost / internal network
Tracking :latest on productionBreaking changes in env vars or policy formatPin the image to the verified release tag
Expecting it to stop determined scrapersJS-capable bots can always solve the puzzleThreat-model honestly: deterrent + DDoS cost-raiser, not a wall

Sources

  • Anubis project — TecharoHQ/anubis on GitHub (23,868 stars, MIT; release history, README, docs at anubis.techaro.lol)
  • Anubis Deploy Guide — zeabur.com/templates/8PTVM3 (TARGET/DIFFICULTY envs, proof-of-work explainer)
  • Anubis — community-scripts.org/scripts/anubis (Proxmox helper: v1.28.2, multi-TARGET, COOKIE_SECURE/X-Forwarded-For, updated 2026-10-16)
  • Deploying Anubis to Block AI Bots — bentasker.co.uk (the nginx-sandwich deploy diary)
  • "you don't need anubis" — img.sauf.ca (flexagoon; the honest counter-piece: LLM bots don't run JS; DDoS-vs-scraper distinction; README Cloudflare quote)
  • How to Protect Your Site from Aggressive AI Bots — hackyourmom.com (AI-bot explainer: SHA-256 challenge, ~13k GitHub stars)
  • Anubis-inspired PoW for static sites — dev.to/51511 (the Albireo edge variant: Cloudflare Pages/Netlify/Vercel, SEO bots whitelisted)
  • Anubis + Caddy — blog.simplicy.info (the two-Caddy sandwich: one Anubis, many services, botPolicy.yaml)
  • Battle Against AI Crawlers — myworldfix.com (the Xe Iaso origin story: AmazonBot, DDoS-like outages, the FOSS community response)
  • Protecting Against AI Bot Scraping — undercodetesting.com (Cloudflare AI Labyrinth + Anubis overview)

Frequently Asked Questions

Does Anubis stop bots that run JavaScript?

No. A headless browser can solve the SHA-256 challenge — the puzzle only makes scraping computationally expensive. Anubis reliably stops the current generation of LLM scrapers because they don't execute JavaScript, and it raises the cost of DDoS floods. Against a determined, well-resourced scraper running real browsers, treat it as a deterrent, not a wall.

Will Anubis hurt my Google rankings?

Only if you misconfigure it. The default policy aggressively challenges anything browser-like — including Googlebot — so you must allowlist search-engine crawlers in your bot policy file before going live. With a correct allowlist, SEO bots pass through untouched and your indexing is unaffected. Community edge variants whitelist SEO bots by default for exactly this reason.

What DIFFICULTY should I set on Anubis?

Start at the default of 4, which solves in well under a second on a typical laptop. Raise it only while you are under an active scraping attack, and lower it afterward — difficulty above 4 makes real visitors, especially on phones, wait noticeably on every visit. The difficulty dial is a UX tradeoff, not a security dial.

Should I use Anubis or Cloudflare?

Use Cloudflare when you just want bot protection handled — the Anubis README itself says most people can get by with Cloudflare. Use Anubis when you can't or won't route traffic through Cloudflare: homelabs, self-hosted services on your own hardware, or setups where no third party may inspect your traffic. For the narrow case of one well-behaved AI crawler, even a tiny JS-cookie check in your existing proxy works invisibly.

Can one Anubis instance protect multiple websites?

No — one instance protects exactly one TARGET upstream. For several sites, run one Anubis container per site (they are very light), or use the community two-proxy sandwich: one outer proxy routes all domains into a single Anubis, which fronts a second internal proxy that fans traffic out to each service.

Featured on Twelve Tools Launch Llama Tools Launch your MCP