Turnstile protection

A public form and a whole-page gate protected by Cloudflare Turnstile.

Two examples with Cloudflare’s test keys: a form (mode: forms) and a whole-page gate (mode: gate). The test keys pass without a real widget; replace them before deploying.

Form protection

[examples/turnstile/routes.json](/examples/turnstile/routes.json)

{
  "turnstile": {
    "siteKey": "1x00000000000000000000AA",
    "secretEnv": "TURNSTILE_SECRET",
    "paths": ["/contact/*"],
    "forms": ["form#contact"]
  },
  "authProvider": {
    "type": "dev",
    "loginBaseUrl": "http://login.demo.localhost:3000",
    "cookieDomain": "demo.localhost"
  },
  "routes": [{
    "host": "app.demo.localhost", "path": "/*", "frontend": true,
    "backend": "http://127.0.0.1:8080"
  }]
}

Whole-page gate

[examples/turnstile-gate/routes.json](/examples/turnstile-gate/routes.json)

{
  "turnstile": {
    "mode": "gate",
    "siteKey": "1x00000000000000000000AA",
    "secretEnv": "TURNSTILE_SECRET",
    "paths": ["/*"],
    "exemptPaths": ["/healthz"],
    "clearanceTtlSeconds": 1800,
    "challenge": { "appearance": "interaction-only" }
  },
  "routes": [{
    "host": "app.demo.localhost", "path": "/*", "frontend": true,
    "backend": "http://127.0.0.1:8080"
  }]
}

Run it

export TURNSTILE_SECRET=1x0000000000000000000000000000000AA
gate-one -check -config routes.json
gate-one -config routes.json
  • forms: the widget is injected into form#contact; a POST without a token is refused by Gate One.

  • gate: the first visit shows "Checking your browser"; after the test challenge, the page is served and the clearance cookie lasts 30 minutes.

The source repository also ships a runnable demo (deploy/local/turnstile) with a backend, ./run.sh gate and ./run.sh api.

Production checklist

  • create the widget in the Cloudflare dashboard and declare every public hostname;

  • put the secret in the environment of every replica (TURNSTILE_SECRET), never in the file;

  • keep a rateLimit on the protected paths;

  • restrict the application’s network access to Gate One: this check only covers traffic that goes through it;

  • document that the client IP is sent to Cloudflare;

  • decide whether the site must stay indexable (a /* gate blocks crawlers).

Limits

  • the token is single-use: only Gate One verifies it;

  • a second ingress that reaches the application directly bypasses the check;

  • a gate on /* blocks search engines.