Edit history

Earlier versions of [Self-hosted] Cloudflare Turnstile captcha is blocked by a CSP that only allows hCaptcha, newest first.

Current version | Edited by Rex
Changes
Details- Happy to provide a redacted HAR / full CSP header if useful.Removed: ### EnvironmentRemoved: Removed: _No response_Removed: ### Logs or screenshotsBrowser Console: "RestTransport.ts:501 Loading the script 'https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback&render=explicit' violates the following Content Security Policy directive: "script-src 'self' 'nonce-93b7c18a12fdfe08b50ceb0c7cbf3c24' 'wasm-unsafe-eval' blob: https://*.fluxer.app https://hcaptcha.com https://*.hcaptcha.com https://example.domain". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback. The action has been blocked"Removed: Removed: ### ChecksRemoved: Removed: - ☑ I searched existing issues.Removed: - ☑ I wrote this report in my own words, except for direct translation if needed.
Show

[Self-hosted] Cloudflare Turnstile captcha is blocked by a CSP that only allows hCaptcha

Observed behaviour

  • The bootstrap JSON delivered by the app correctly contains:
      "captcha":{
        "provider":"turnstile",
        "turnstile_site_key":"<redacted>",
        "hcaptcha_site_key":null
      }
  • No widget appears on the registration form.
  • The browser console shows a CSP violation along the lines of:and a matching frame-src violation for the iframe.
Expected behaviour A Cloudflare Turnstile widget renders on the registration form, just like hCaptcha does when hcaptcha is selected as the provider.

Reproduction steps

  1. Deploy the self-hosting stack.
  2. In the admin console (/admin), under Bot protection, select Cloudflare Turnstile as the provider and enter a valid site key + secret key.
  3. Set the environment variables:
  4. Open the public site in a fresh browser tab and start the registration flow.

Details

In Short

When FLUXER_CAPTCHA_PROVIDER=turnstile is configured on a self-hosted instance, the bootstrap payload correctly advertises the Turnstile site key, but the Content-Security-Policy served by the app shell only whitelists hCaptcha. As a result, the browser refuses to load https://challenges.cloudflare.com/turnstile/v0/api.js and no captcha widget is rendered on the registration page. In effect, Turnstile is selectable in the admin UI but non-functional out of the box.

Environment

  • Deployment: self-hosted via Docker Compose (Coolify, but the issue is not Coolify-specific — the CSP is set by the app, not the proxy)
  • Image tag: ghcr.io/fluxerapp/fluxer-app-proxy-self-hosted:v1
  • Caddy (caddy:2.10-alpine) used as the documented reverse proxy
  • Browser: Firefox / Chromium (current versions)

Root cause (best guess)

The CSP emitted by app-proxy is hardcoded with hCaptcha origins:
  • script-src includes https://hcaptcha.com https://*.hcaptcha.com
  • frame-src includes https://hcaptcha.com https://*.hcaptcha.com
but does not include the Turnstile origins:
  • https://challenges.cloudflare.com (script + iframe + connect)
  • https://static.cloudflareinsights.com (sometimes used by Turnstile telemetry)
Because the captcha provider is selectable at runtime, the CSP should reflect the active provider — either by emitting a different allowlist depending on FLUXER_CAPTCHA_PROVIDER, or by including both providers' origins unconditionally.

Suggested fix

When FLUXER_CAPTCHA_PROVIDER=turnstile, extend the CSP served by app-proxy (and any other component that emits CSP for the web client) to include at least:
  • script-src → https://challenges.cloudflare.com
  • frame-src → https://challenges.cloudflare.com
  • connect-src → https://challenges.cloudflare.com
Alternatively, allow operators to configure additional CSP sources via env vars (e.g. FLUXER_CSP_EXTRA_SCRIPT_SRC, FLUXER_CSP_EXTRA_FRAME_SRC) so the same kind of issue can be solved for other future providers without code changes.

Workaround for other self-hosters reading this

Switch the provider to hCaptcha in the admin console (FLUXER_CAPTCHA_PROVIDER=hcaptcha). It is already whitelisted in the default CSP and works without further configuration.

Additional notes

  • A minor unrelated nit in deploy/self-hosting/docker-compose.yml: the captcha defaults appear in some forks/edits as :true / :turnstile instead of :-false / :-none. The current upstream is correct, but it might be worth a brief comment near those lines explaining that the operator should set FLUXER_CAPTCHA_PROVIDER via env, since people copy-paste these blocks and easily drop the -.
  • Happy to provide a redacted HAR / full CSP header if useful.
Logs or screenshots
Browser Console: "RestTransport.ts:501 Loading the script 'https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback&render=explicit%27 violates the following Content Security Policy directive: "script-src 'self' 'nonce-93b7c18a12fdfe08b50ceb0c7cbf3c24' 'wasm-unsafe-eval' blob: https://*.fluxer.app/ https://hcaptcha.com/ https://*.hcaptcha.com/ https://example.domain/". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback. The action has been blocked"
Edited by Rex
Changes
Removed: [Self-hosted] Unable to use Cloudlfare Turnstile as Captcha | Site key is delivered but blocked by hardcoded CSP (only hCaptcha is whitelisted)Added: [Self-hosted] Cloudflare Turnstile captcha is blocked by a CSP that only allows hCaptcha
Show

[Self-hosted] Cloudflare Turnstile captcha is blocked by a CSP that only allows hCaptcha

Observed behaviour

  • The bootstrap JSON delivered by the app correctly contains:
      "captcha":{
        "provider":"turnstile",
        "turnstile_site_key":"<redacted>",
        "hcaptcha_site_key":null
      }
  • No widget appears on the registration form.
  • The browser console shows a CSP violation along the lines of:and a matching frame-src violation for the iframe.
Expected behaviour A Cloudflare Turnstile widget renders on the registration form, just like hCaptcha does when hcaptcha is selected as the provider.

Reproduction steps

  1. Deploy the self-hosting stack.
  2. In the admin console (/admin), under Bot protection, select Cloudflare Turnstile as the provider and enter a valid site key + secret key.
  3. Set the environment variables:
  4. Open the public site in a fresh browser tab and start the registration flow.

Details

In Short

When FLUXER_CAPTCHA_PROVIDER=turnstile is configured on a self-hosted instance, the bootstrap payload correctly advertises the Turnstile site key, but the Content-Security-Policy served by the app shell only whitelists hCaptcha. As a result, the browser refuses to load https://challenges.cloudflare.com/turnstile/v0/api.js and no captcha widget is rendered on the registration page. In effect, Turnstile is selectable in the admin UI but non-functional out of the box.

Environment

  • Deployment: self-hosted via Docker Compose (Coolify, but the issue is not Coolify-specific — the CSP is set by the app, not the proxy)
  • Image tag: ghcr.io/fluxerapp/fluxer-app-proxy-self-hosted:v1
  • Caddy (caddy:2.10-alpine) used as the documented reverse proxy
  • Browser: Firefox / Chromium (current versions)

Root cause (best guess)

The CSP emitted by app-proxy is hardcoded with hCaptcha origins:
  • script-src includes https://hcaptcha.com https://*.hcaptcha.com
  • frame-src includes https://hcaptcha.com https://*.hcaptcha.com
but does not include the Turnstile origins:
  • https://challenges.cloudflare.com (script + iframe + connect)
  • https://static.cloudflareinsights.com (sometimes used by Turnstile telemetry)
Because the captcha provider is selectable at runtime, the CSP should reflect the active provider — either by emitting a different allowlist depending on FLUXER_CAPTCHA_PROVIDER, or by including both providers' origins unconditionally.

Suggested fix

When FLUXER_CAPTCHA_PROVIDER=turnstile, extend the CSP served by app-proxy (and any other component that emits CSP for the web client) to include at least:
  • script-src → https://challenges.cloudflare.com
  • frame-src → https://challenges.cloudflare.com
  • connect-src → https://challenges.cloudflare.com
Alternatively, allow operators to configure additional CSP sources via env vars (e.g. FLUXER_CSP_EXTRA_SCRIPT_SRC, FLUXER_CSP_EXTRA_FRAME_SRC) so the same kind of issue can be solved for other future providers without code changes.

Workaround for other self-hosters reading this

Switch the provider to hCaptcha in the admin console (FLUXER_CAPTCHA_PROVIDER=hcaptcha). It is already whitelisted in the default CSP and works without further configuration.

Additional notes

  • A minor unrelated nit in deploy/self-hosting/docker-compose.yml: the captcha defaults appear in some forks/edits as :true / :turnstile instead of :-false / :-none. The current upstream is correct, but it might be worth a brief comment near those lines explaining that the operator should set FLUXER_CAPTCHA_PROVIDER via env, since people copy-paste these blocks and easily drop the -.
  • Happy to provide a redacted HAR / full CSP header if useful.
Environment
No response
Logs or screenshots
Browser Console: "RestTransport.ts:501 Loading the script 'https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback&render=explicit%27 violates the following Content Security Policy directive: "script-src 'self' 'nonce-93b7c18a12fdfe08b50ceb0c7cbf3c24' 'wasm-unsafe-eval' blob: https://*.fluxer.app/ https://hcaptcha.com/ https://*.hcaptcha.com/ https://example.domain/". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback. The action has been blocked"
Checks
  • ☑ I searched existing issues.
  • ☑ I wrote this report in my own words, except for direct translation if needed.
Original by Rex
Show

[Self-hosted] Unable to use Cloudlfare Turnstile as Captcha | Site key is delivered but blocked by hardcoded CSP (only hCaptcha is whitelisted)

Observed behaviour

  • The bootstrap JSON delivered by the app correctly contains:
      "captcha":{
        "provider":"turnstile",
        "turnstile_site_key":"<redacted>",
        "hcaptcha_site_key":null
      }
  • No widget appears on the registration form.
  • The browser console shows a CSP violation along the lines of:and a matching frame-src violation for the iframe.
Expected behaviour A Cloudflare Turnstile widget renders on the registration form, just like hCaptcha does when hcaptcha is selected as the provider.

Reproduction steps

  1. Deploy the self-hosting stack.
  2. In the admin console (/admin), under Bot protection, select Cloudflare Turnstile as the provider and enter a valid site key + secret key.
  3. Set the environment variables:
  4. Open the public site in a fresh browser tab and start the registration flow.

Details

In Short

When FLUXER_CAPTCHA_PROVIDER=turnstile is configured on a self-hosted instance, the bootstrap payload correctly advertises the Turnstile site key, but the Content-Security-Policy served by the app shell only whitelists hCaptcha. As a result, the browser refuses to load https://challenges.cloudflare.com/turnstile/v0/api.js and no captcha widget is rendered on the registration page. In effect, Turnstile is selectable in the admin UI but non-functional out of the box.

Environment

  • Deployment: self-hosted via Docker Compose (Coolify, but the issue is not Coolify-specific — the CSP is set by the app, not the proxy)
  • Image tag: ghcr.io/fluxerapp/fluxer-app-proxy-self-hosted:v1
  • Caddy (caddy:2.10-alpine) used as the documented reverse proxy
  • Browser: Firefox / Chromium (current versions)

Root cause (best guess)

The CSP emitted by app-proxy is hardcoded with hCaptcha origins:
  • script-src includes https://hcaptcha.com https://*.hcaptcha.com
  • frame-src includes https://hcaptcha.com https://*.hcaptcha.com
but does not include the Turnstile origins:
  • https://challenges.cloudflare.com (script + iframe + connect)
  • https://static.cloudflareinsights.com (sometimes used by Turnstile telemetry)
Because the captcha provider is selectable at runtime, the CSP should reflect the active provider — either by emitting a different allowlist depending on FLUXER_CAPTCHA_PROVIDER, or by including both providers' origins unconditionally.

Suggested fix

When FLUXER_CAPTCHA_PROVIDER=turnstile, extend the CSP served by app-proxy (and any other component that emits CSP for the web client) to include at least:
  • script-src → https://challenges.cloudflare.com
  • frame-src → https://challenges.cloudflare.com
  • connect-src → https://challenges.cloudflare.com
Alternatively, allow operators to configure additional CSP sources via env vars (e.g. FLUXER_CSP_EXTRA_SCRIPT_SRC, FLUXER_CSP_EXTRA_FRAME_SRC) so the same kind of issue can be solved for other future providers without code changes.

Workaround for other self-hosters reading this

Switch the provider to hCaptcha in the admin console (FLUXER_CAPTCHA_PROVIDER=hcaptcha). It is already whitelisted in the default CSP and works without further configuration.

Additional notes

  • A minor unrelated nit in deploy/self-hosting/docker-compose.yml: the captcha defaults appear in some forks/edits as :true / :turnstile instead of :-false / :-none. The current upstream is correct, but it might be worth a brief comment near those lines explaining that the operator should set FLUXER_CAPTCHA_PROVIDER via env, since people copy-paste these blocks and easily drop the -.
  • Happy to provide a redacted HAR / full CSP header if useful.
Environment
No response
Logs or screenshots
Browser Console: "RestTransport.ts:501 Loading the script 'https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback&render=explicit%27 violates the following Content Security Policy directive: "script-src 'self' 'nonce-93b7c18a12fdfe08b50ceb0c7cbf3c24' 'wasm-unsafe-eval' blob: https://*.fluxer.app/ https://hcaptcha.com/ https://*.hcaptcha.com/ https://example.domain/". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback. The action has been blocked"
Checks
  • ☑ I searched existing issues.
  • ☑ I wrote this report in my own words, except for direct translation if needed.