Skip to content

Turnstile token passes frontend, but fails backend verification with strict CSP3 and Nuxt module (ie strict-dynamic not supported #577

@pjr94

Description

@pjr94

🐛 The bug

When using the Nuxt Turnstile module with a strict Content Security Policy (CSP3) that uses nonces and 'strict-dynamic', the Turnstile widget loads and displays “success” on the frontend. However, when the token is submitted to the backend and verified via the official Cloudflare /siteverify endpoint, verification fails and the backend returns an error (e.g., 400/401).

This means the token generated by the widget is not valid for backend verification, even though the widget appears to work for the user. The issue is not with the CSP blocking the widget, but with the validity of the token itself.

🛠️ To reproduce

1 Set up a Nuxt project using the latest Nuxt Turnstile module.
2 Configure a strict Content Security Policy (CSP) with nonces and 'strict-dynamic' (nonce is generated per request and injected into all script tags).
3 Add the component to a form.
4 Deploy the app to production with the correct Turnstile site key and secret, and ensure your domain is allowed in the Turnstile dashboard.
5 Load the form page in the browser. The Turnstile widget should render and display “success.”
6 Submit the form, sending the tsToken to a backend endpoint that verifies the token using the official Cloudflare /siteverify API.
7 Observe that the backend verification fails, even though the widget shows success on the frontend.

🌈 Expected behaviour

When the Turnstile widget displays “success” and issues a token, that token should be valid and verifiable by the backend using the official Cloudflare /siteverify endpoint, provided the correct secret and site keys are used.es not provide a way to set a nonce on the injected Turnstile script tag (https://challenges.cloudflare.com/turnstile/v0/api.js). As a result, the script is blocked by the browser’s CSP, even though Cloudflare officially supports nonce-based CSP integration.

ℹ️ Additional context

The Nuxt Turnstile module does not provide a way to set a nonce on the injected script tag.
The frontend receives a token and submits it to the backend.
The backend uses the following code to verify the token:
const formData = new FormData() formData.append("secret", env.NUXT_TURNSTILE_SECRET_KEY) formData.append("response", token) const url = "https://challenges.cloudflare.com/turnstile/v0/siteverify" const result = await fetch(url, { body: formData, method: "POST" }) const resultJson = await result.json()
The verification fails, even though the widget shows success.
All environment variables and domain settings are correct.
This may be related to the inability to set a nonce on the Turnstile script tag, or to how the module injects the script under strict CSP.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions