invalid-input-secret: siteverify rejected your secret key
Cloudflare's description is "Secret key is invalid or expired". In practice the key is usually correct and something happened to it between the dashboard and the request.
This is about your server, not the visitor
Unlike most Turnstile errors, this one has nothing to do with the browser, the widget or the person filling in the form. The token may be perfect. invalid-input-secret means the secret parameter your server sent to siteverify was not a secret Cloudflare recognises.
That makes it one of the easier codes to diagnose, because there are only a handful of ways a correct key becomes an incorrect one in transit.
The five things that actually cause it
| Cause | How to confirm it |
|---|---|
| You sent the sitekey | The sitekey is public and begins 0x4. The secret is a different value entirely. Copying the wrong one out of the dashboard is the single commonest cause, because both live on the same screen. |
| Whitespace in the environment variable | A trailing newline from an echo into a .env file, or a space picked up from a copy-paste, is sent verbatim. Print the length of the value your process actually holds and compare it with the dashboard. |
| The secret belongs to a different widget | Every widget has its own sitekey and secret pair. A secret from another widget is a valid secret and still the wrong one, which is why the error says invalid rather than unknown. |
| A testing key paired with a live response | Cloudflare's testing keys work only with each other. A testing secret validating a real token, or a live secret validating a testing token, fails here. |
| The widget was rotated or deleted | Regenerating a secret in the dashboard invalidates the previous one immediately. If this started without a deploy, check whether someone rotated it. |
Check the key your process is holding
Read the value your running process has, not the value in the file you think it read. Configuration is loaded by more layers than people expect, and any of them can trim, quote or override.
# length and a fingerprint — never print the key itself into a log printf '%s' "$TURNSTILE_SECRET_KEY" | wc -c printf '%s' "$TURNSTILE_SECRET_KEY" | sha256sum | cut -c1-12 # then confirm against siteverify directly curl -X POST https://challenges.cloudflare.com/turnstile/v0/siteverify \ -d secret="$TURNSTILE_SECRET_KEY" \ -d response="dummy" # invalid-input-response -> the secret is FINE, the token was the dummy # invalid-input-secret -> the secret is the problem
That second call is the useful one. Sending a deliberately bogus token isolates the secret: if the secret is good you get invalid-input-response, because Cloudflare got far enough to judge the token. Getting invalid-input-secret back proves the key never passed.
The full siteverify table
For context, the seven codes this endpoint returns:
| Code | Cloudflare's description |
|---|---|
| missing-input-secret | Secret parameter not provided |
| invalid-input-secret | Secret key is invalid or expired |
| missing-input-response | Response parameter was not provided |
| invalid-input-response | Token is invalid, malformed, or expired |
| bad-request | Request is malformed |
| timeout-or-duplicate | Token has already been validated |
| internal-error | Internal error occurred |
missing-input-secret is the neighbouring case and means something different: the parameter was not sent at all. If you see that one, the secret is not reaching your request builder — an unset variable rather than a wrong value.
Common questions
The sitekey is public, goes in your page's markup, and begins 0x4. The secret key is private, never leaves your server, and is what siteverify authenticates you with. They are issued together per widget and are not interchangeable — sending the sitekey as the secret is the commonest cause of this error.
They do not expire on a timer, but they are invalidated the moment you rotate them in the dashboard, and deleting a widget invalidates its pair immediately. Cloudflare's wording says "invalid or expired" because both arrive as the same code.
Almost always a different value in the two environments — a secret from another widget, a testing key that was never swapped for a live one, or a deployment variable that was never set and is falling back to an empty string. Fingerprint the key in both places and compare.
No, and the attempt produces this error. The testing sitekeys and secrets are documented to work only with each other, so a testing secret cannot validate a token from a real widget. They exist to make automated tests deterministic, not to bypass validation.
No. Log its length and a hash prefix instead — that is enough to tell a trailing newline from a wrong key without putting the credential in a log file, a crash report or an error tracker.
Rarely. A genuine outage on their side returns internal-error, not this. invalid-input-secret is a verdict about the value you sent, so it points at configuration on your side essentially every time.
Related
Sources
More in Turnstile errors
Automating a gate you own or are authorised to test?
// SolveGate clears Cloudflare Turnstile and WAF challenges through one REST call · first 1,000 solves free