Configure Cloudflare Zero Trust

Goal

Require a valid Cloudflare Access JWT on every pool call before a concurrency slot is taken. Access decides who may authenticate. nthbouncer verifies the application token at the edge, then applies pool keys, HMAC, and the concurrency limit as usual.

Available on the Team plan and above. You can use Cloudflare Access or generic JWT validation on a pool, not both.

Reference for headers and check order: Access → Cloudflare Access.

Create an Access application

  1. In the Cloudflare Zero Trust dashboard, open Access controls → Applications.
  2. Add a self-hosted application for the hostname callers will hit (your custom domain, or another Access-protected app whose JWT you will forward).
  3. Attach Access policies for people and services that should reach the pool (IdP groups, emails, or Service Auth with service tokens).
  4. Save the application.

Copy team domain and AUD

  1. Note your team name. The team domain is https://‹team›.cloudflareaccess.com.
  2. Open the application → Overview (or Additional settings) and copy the Application Audience (AUD) tag.

Cloudflare's guide: Find your AUD tag.

Configure the pool

  1. Open Pools → Access.
  2. Enable Identity-aware access.
  3. Set Provider to Cloudflare Access.
  4. Enter the team name (or full https://‹team›.cloudflareaccess.com URL) and the AUD tag.
  5. Save. Changes usually take effect within a few seconds.

Keep a pool key (and optional HMAC / IP policy) as you already do. Access is an extra identity check, not a replacement for keys.

Call the pool

Send the Access application JWT plus your pool credentials:

curl -sS "https://‹pool›.nthpool.cloud/v1/resource" \
  -H "X-Nthpool-Key: $POOL_KEY" \
  -H "Cf-Access-Jwt-Assertion: $ACCESS_JWT"

X-Nthpool-Jwt is accepted when a trusted hop forwards the same application JWT. Prefer Cf-Access-Jwt-Assertion when you control the client.

Where the JWT comes from

  • Browser or Access-proxied hostname. After Access authenticates the user, Cloudflare attaches Cf-Access-Jwt-Assertion on the request toward the origin. If the pool's custom domain is that Access application, callers get the header automatically once they pass Access.
  • Service tokens. Authenticate to the Access application with CF-Access-Client-Id / CF-Access-Client-Secret (Service Auth policy). Use the resulting application JWT on pool calls the same way.
  • Forwarding from another Access app. A service that already holds a JWT for this application's AUD can send it as Cf-Access-Jwt-Assertion or X-Nthpool-Jwt. The aud claim must match the AUD you configured on the pool.

nthbouncer verifies RS256 against https://‹team›.cloudflareaccess.com/cdn-cgi/access/certs, and checks issuer, audience, and expiry. The Access assertion is stripped before the request is sent upstream.

Troubleshooting

Symptom Likely cause
401 missing Cloudflare Access JWT No Cf-Access-Jwt-Assertion or X-Nthpool-Jwt
401 audience mismatch AUD on the pool does not match the Access application that issued the token
401 JWT rejected (issuer / signature) Wrong team domain, expired token, or token from a different Zero Trust team
Cannot enable Identity-aware access Plan below Team, or generic JWT already configured on the pool

Auth failures never take a concurrency slot. See Understand admission errors.

See also