Cloudflare Turnstile

Protect XPAC Forms with a scoped Turnstile widget, server-side verification, and fail-closed configuration checks.

Cloudflare Turnstile adds browser challenge protection to Forms. A protected form obtains a short-lived token in the browser, and the server verifies it with Cloudflare before accepting the submission.

Forms is required

This add-on does not register its settings, editor controls, block, or verification check unless XPAC Forms is active.

Set up Turnstile

Create a Turnstile widget in the Cloudflare dashboard. Allow every production and staging hostname that will serve a protected form.

Open XPAC → Integrations → Cloudflare Turnstile, enter the site and secret keys, choose the appearance, and save.

Edit a form, open Anti-spam protection, enable Cloudflare Turnstile, and update the form.

Optionally insert the Cloudflare Turnstile field block where the widget should appear. Without it, Forms creates a container after the Submit control.

The supported site-wide settings are:

SettingDefaultBehaviour
Site KeyemptyPublic key sent to the browser.
Secret KeyemptyPrivate key used only for server verification.
ThemeAutoAuto, Light, or Dark.
SizeNormalNormal or Compact.
Appearance modeAlwaysAlways or Interaction only. Interaction only requires a compatible Managed widget in Cloudflare.

The secret uses a masked password control. Existing option names and stored values are unchanged; masking the field does not encrypt the WordPress option at rest.

Enabled protection fails closed when keys are missing

The per-form toggle remains available when credentials are absent. If an enabled form has a missing site or secret key, the editor shows a non-dismissible warning with a settings link, and the server refuses submissions instead of silently disabling protection.

What the visitor sees

Depending on the widget and appearance settings, Turnstile may resolve without interaction or may ask the visitor to complete a challenge. While an interactive challenge is active, the form releases its busy state so the visitor can use the widget.

The browser handles success, error, expiry, and Turnstile timeout callbacks. It also has a 30-second execution guard. Any of those failure paths clears stale tokens, returns an actionable form error, and allows a retry instead of leaving the form busy indefinitely.

Only pages that render an enabled form with configured credentials enqueue Cloudflare's script and the front-end integration. Forms inserted after initial page startup are initialized too when those assets are present.

Server verification

The server sends the token, visitor IP, and a generated idempotency key to Cloudflare's siteverify endpoint. It accepts the submission only when the provider reports success and the returned action and hostname match the form request:

  • action must be exactly submit; and
  • hostname must match the site's WordPress home or site hostname.

The verification timeout defaults to five seconds and is clamped to 1–10 seconds. One transient transport, 429, or 5xx failure is retried with the same token and the same UUID idempotency key, as required for a safe Turnstile retry. Permanent failures, missing tokens, malformed responses, and action/hostname mismatches fail closed.

Refused submissions and monitoring

A refused submission does not run notification emails or integration deliveries. Forms marks it with the detector and reason. With the default Keep submissions marked as spam setting enabled, it is quarantined in the Spam view so an administrator can review and restore a false positive. If that Forms setting is disabled, the refusal is not retained.

The Turnstile token is removed before entry storage. Operator-side failures such as missing or invalid credentials, transport errors, malformed verdicts, and action/hostname mismatches are written to the WordPress/PHP error log with an XPAC cloudflare-turnstile prefix.

Test the complete production path

A CAPTCHA reduces automated abuse but cannot guarantee it. Test the real site and secret keys from every production hostname, with both a normal browser and the challenge path. Confirm that your privacy controls and content-security policy allow challenges.cloudflare.com.

Privacy and availability

A protected submission contacts Cloudflare from the visitor's browser and from your server. Account for that third-party processing in your privacy and consent setup. If Cloudflare's script or verification service is blocked or unavailable, the form fails closed and asks the visitor to retry.

See Cloudflare's server-side validation guide for the provider's token and idempotency contract.

Developer reference

Site-wide values are stored in the xpac option:

Option keyMeaning
cf_turnstile_site_keySite key
cf_turnstile_secret_keySecret key
cf_turnstile_themeTheme
cf_turnstile_sizeSize
cf_turnstile_modeAppearance mode

Per-form state is stored under cloudflareCaptcha in the form's form_settings meta:

{
	"cloudflareCaptcha": {
		"enable": true
	}
}

The optional positioning block is xp/form-field-cloudflare-turnstile. The raw token is posted as cloudflareCAPTCHA and removed from stored entry data.

xpac_cloudflare_turnstile_request_timeout filters the server timeout before the 1–10-second clamp. xpac_forms_enable_verification can skip a provider for a special submission flow; the filter provider slug is cloudflare-captcha. Returning false deliberately bypasses this protection and should be narrowly scoped. Forms Auto Submit uses that path because a signed server-driven submission has no browser from which to obtain a CAPTCHA token.

On this page