Config
Using Turnstile is the same ground with the code around it, bot-gating a route is the walkthrough, and provisioning mints the keys these names point at. Refusals carry an ordinary error code.
| Key | What it is |
|---|---|
widgets.visible | The managed widget — a challenge where it should be seen |
widgets.invisible | The silent widget — a form that should not be interrupted |
protect | Action → widget mode. Which protected actions get a gate, at which mode |
tokenField | Where the token is read from, when not the default |
Up to two widgets per domain, one of each mode. Declare only the modes you need.
The widget secret is never in config. Only the public sitekeys are — the front end renders with them. The secret is stored and read through the secrets capability.
protect defaults to { login: "visible" }, and that default is built from the login-action constant rather than written out again — the key an action is configured under and the action label a token is solved for are the same string.
Middleware options
turnstile({ mode, field, header, action })| Option | Default | |
|---|---|---|
mode | Inferred | Which widget. Required only when both are declared |
field | cf-turnstile-response | The body field, form or JSON |
header | — | Read from a request header instead. For a JSON API or a mobile client sending the token out of band |
action | — | The expected action label. Denies on mismatch |
Error codes
| Code | Status | Cause |
|---|---|---|
turnstile/missing_token | No token where one was expected | |
turnstile/failed | 403 | The token did not pass, or the response was unusable |
turnstile/config | 500 | Cloudflare does not recognize the secret |
turnstile/failed covers a transport error, a non-OK status, a non-JSON body and an unrecognized response shape — every one of them denies. turnstile/config is separated out because it is a verdict about the deployment rather than about the caller.
What is sent to siteverify
secret, response, and remoteip when available. That is all.
Siteverify takes no action request parameter — the action is a response field the caller compares. A gate that tried to send it would be sending a parameter Cloudflare ignores and believing it had checked something.
What is read back
| Field | Used for |
|---|---|
success | The verdict |
error-codes | Which failure, including the two that name the secret |
action | Compared against the expected action |
metadata.result_with_testing_key | Cloudflare’s own flag that this verdict came from a test key |
Cloudflare may add fields; the schema strips them. Validating the body is part of the security boundary, not tidiness.
The two secret-fault codes
invalid-input-secret and missing-input-secret are the codes that name the secret rather than the token. Every other code is a verdict about the caller; these two are a verdict about the deployment, and they are what raises turnstile/config.
Environment vars provisioning writes
| Var | Holds |
|---|---|
TURNSTILE_SITEKEY_VISIBLE | The visible widget’s public sitekey |
TURNSTILE_SITEKEY_INVISIBLE | The invisible widget’s public sitekey |
Indexed to the environment the bundle was built for.