Reverse Proxy
In production, Sentinel typically runs behind a reverse proxy, CDN, API gateway, or load balancer — nginx and Cloudflare are common choices. Getting client IP and header forwarding right matters here, since Sentinel's detection features depend on them.
Public vs. internal endpoints
Sentinel serves the web dashboard, the full management API, and the widget-facing endpoints from the same origin — but only two need to be reachable from the public internet, since they're called directly by the browser:
/v1/challenge(including/v1/challenge/audio/{token}) — issues challenges to the<altcha-widget>./v1/verify— verifies the solved challenge the widget submits.
Everything else should be reachable only from your internal network or VPN — the dashboard, /v1/docs, and the management API (/v1/accounts, /v1/users, /v1/admin/*, /v1/security-groups, and so on). Restrict it at the reverse proxy rather than relying on authentication alone; it keeps credential-stuffing and scanning traffic off the admin surface entirely.
/v1/verify/signature depends on who calls it: it verifies the server signature from a POST /v1/verify payload, and that call is always server-to-server, never from the browser. Keep it internal-only if the services doing that verification all live on your internal network. Expose it publicly only if an external service — a form handler or backend outside your network — needs to call it directly to verify a signature.
Forwarding the client IP
Sentinel reads the client IP from the standard X-Forwarded-For header. Set X_FORWARDED_FOR_TRUSTED to your proxy's IP addresses so Sentinel only trusts X-Forwarded-For values coming from a proxy you actually control — values from any other source are rejected, preventing a client from simply spoofing the header itself.
X_FORWARDED_FOR_TRUSTED=10.0.0.1,10.0.0.2See ENV Variables and IP Resolvers.
Forwarding headers
Forward all client request headers where feasible. At minimum, these feed Sentinel's browser/platform/device detection and shouldn't be stripped by your proxy:
AcceptAccept-LanguageSec-CH-UASec-CH-UA-MobileSec-CH-UA-PlatformUser-Agent
Dropping these reduces the accuracy of Detection Signals like header entropy and language detection.
Preserving the response
/v1/challenge and /v1/verify both return application/json — the <altcha-widget> parses that response directly, so the proxy must leave it alone on the way back:
- Forward the
Content-Typeresponse header as-is, or set it toapplication/jsonyourself if your proxy or gateway strips/rewrites it (some do, especially API gateways with a default content-type policy). A missing or wrongContent-Typecan make the widget — or an intermediate cache/WAF — treat the body as something other than JSON. - Return the JSON body byte-for-byte, without modification. No reformatting, minifying, key reordering, compression that isn't transparently reversed, or WAF/gateway "response sanitization" — any of that can invalidate the signed challenge/verification payload or simply fail to parse. Treat these two endpoints as pass-through, not something to be transformed in flight.
- Forward Sentinel's own cache-control headers (
Cache-Control) unmodified rather than letting the proxy or CDN apply its own caching policy. Every/v1/challengeresponse is single-use and every/v1/verifyresponse is specific to one solve — a proxy or edge cache serving either from cache hands out a stale or already-consumed challenge/result, breaking verification for whoever gets the cached copy.
Related
- ENV Variables —
X_FORWARDED_FOR_TRUSTEDand other networking variables. - IP Resolvers — configuring how Sentinel resolves the real client IP.
- Install with Docker Compose — HTTPS termination options for a VPS deployment.