Skip to content

Integrate FCaptcha and verify tokens

An FCaptcha integration has two trust boundaries: the browser obtains a token, and your backend verifies that token before performing the protected action. Never put the verification secret in browser code.

Serve the client from your FCaptcha instance and point the widget at the same server:

<form method="post" action="/signup">
<input type="email" name="email" required>
<div
data-fcaptcha="signup"
data-endpoint="https://captcha.example.com"></div>
<button type="submit">Create account</button>
</form>
<script src="https://captcha.example.com/fcaptcha.js"></script>

After verification, the widget adds a hidden fcaptcha_token field to the form. The public site key identifies the integration; it is not a secret. In production, allowlist expected keys with FCAPTCHA_SITE_KEYS and expected browser origins with FCAPTCHA_ALLOWED_HOSTNAMES.

Send the token and FCAPTCHA_VERIFY_SECRET to the native verification endpoint:

Terminal window
curl --fail-with-body \
-H 'Content-Type: application/json' \
-d '{"token":"TOKEN_FROM_FORM","secret":"YOUR_VERIFY_SECRET"}' \
https://captcha.example.com/api/token/verify

A valid response includes the signed site key, score, hostname, action, and optional customer data:

{
"valid": true,
"site_key": "signup",
"score": 0.15,
"hostname": "www.example.com",
"action": "",
"cdata": ""
}

Require valid: true and compare the signed site_key, hostname, and action to the request you intended to authorize. Tokens expire after five minutes and are single-use.

If you supply remoteip, it must be the original visitor address and match the token binding. Do not pass your backend server’s address.

Turnstile, reCAPTCHA, or hCaptcha compatibility

Section titled “Turnstile, reCAPTCHA, or hCaptcha compatibility”

Existing integrations can keep their siteverify request shape and change the base URL:

Terminal window
curl --fail-with-body \
-d 'secret=YOUR_VERIFY_SECRET' \
-d 'response=TOKEN_FROM_FORM' \
https://captcha.example.com/turnstile/v0/siteverify

FCaptcha also serves /recaptcha/api/siteverify and /siteverify. Form-encoded and JSON requests are accepted. Compatibility script paths are documented in the FCaptcha README.

  • Treat network errors and invalid tokens as an unavailable verification, not a successful challenge.
  • Do not authorize from the browser callback or score alone.
  • Do not retry the same token without an idempotency key; replay protection intentionally rejects reuse.
  • Log only what your privacy policy permits. Verdict logging is off by default.

For complete environment variables, Redis, proxy trust, key rotation, CORS, and upgrade notes, read the upstream installation guide and hardening guide.