CHALLENGE action, Anubis issues a computational puzzle that is trivial for browsers but expensive for scrapers at scale.
Challenge Metadata
Every challenge issued by Anubis contains structured metadata:UUID v7 identifier for the challenge. Used for lookups and validation.
Challenge algorithm:
fast or slow (deprecated).64 bytes of random data (hex-encoded) that the client must process.
Number of leading zero bits required in the proof-of-work hash (0-64).
Hash of the bot rule that issued this challenge. Used to invalidate tokens when policy changes.
Whether this challenge has been solved. Prevents double-spend attacks.
Challenge Types
Anubis supports multiple challenge algorithms, registered in the challenge registry:Proof-of-Work (Recommended)
Meta Refresh
The
metarefresh challenge uses HTTP meta refresh tags to redirect browsers. This is a passive challenge that doesn’t require JavaScript or computation.metarefresh in the challenge registry. Useful for detecting scrapers that don’t process HTML meta tags.
Preact (Interactive)
Thepreact challenge renders an interactive UI component using Preact. This challenge type verifies JavaScript execution and DOM manipulation capabilities.
Proof-of-Work Mechanism
Validation Algorithm
When a client submits a solution, Anubis validates it in constant time:Client-Side Solving
The challenge page includes JavaScript that brute-forces the nonce:Difficulty Settings
Difficulty determines how many leading zero hex characters (4 bits each) are required:Valid range: 0-64
- 0: No proof-of-work required (instant pass)
- 1-3: Light verification (milliseconds)
- 4-6: Moderate difficulty (seconds)
- 7-10: Heavy computation (tens of seconds)
- 11+: Extreme difficulty (minutes to hours)
Difficulty Performance Table
| Difficulty | Expected Attempts | Average Time (Modern CPU) | Use Case |
|---|---|---|---|
0 | 1 | Instant | Testing only |
1 | 16 | less than 10ms | Very light verification |
2 | 256 | ~50ms | Light bot deterrent |
3 | 4,096 | ~500ms | Recommended default |
4 | 65,536 | ~5s | Moderate protection |
5 | 1,048,576 | ~1min | Heavy scraper deterrent |
6 | 16,777,216 | ~15min | Extreme protection |
These times are approximate and vary based on client hardware and JavaScript engine performance.
Configuring Difficulty
Challenge Lifecycle
Storage Requirements
Challenges are stored with a 30-minute TTL:- JSON serialization: Challenges are stored as
store.JSON[challenge.Challenge] - TTL/Expiration: Automatic cleanup after 30 minutes
- Atomic updates: For marking challenges as spent
Challenge Metrics
Anubis exposes Prometheus metrics for monitoring:Error Handling
Challenge validation can fail with specific errors:ErrMissingField
ErrMissingField
The client didn’t submit required parameters (
nonce, elapsedTime, or response).HTTP Status: 400 Bad RequestErrInvalidFormat
ErrInvalidFormat
Parameters have wrong type (e.g., non-numeric
nonce).HTTP Status: 400 Bad RequestErrFailed
ErrFailed
The solution is incorrect or doesn’t meet difficulty requirements.HTTP Status: 403 Forbidden
Best Practices
Start Low
Begin with difficulty 2-3 and increase only if you observe scraper persistence.
Monitor Solve Times
Use
anubis_challenge_time_taken_seconds to ensure challenges aren’t frustrating legitimate users.Different Difficulty by Context
Use lower difficulty for public pages, higher for sensitive endpoints.
Test Cookie Support
Anubis automatically checks cookie support. Failed cookie tests appear in logs.
Custom Challenge Implementation
You can implement custom challenges by satisfying thechallenge.Impl interface:
Next Steps
Policies
Learn how to configure bot detection rules that trigger challenges
How It Works
Understand the complete request flow and JWT validation