Skip to content
All articles

Protection

Contact-form spam protection: CAPTCHA, server verification, and realistic expectations

Combine Turnstile or reCAPTCHA with form code and reject submissions that cannot be verified.

Form spam has no single source: a bot can replay a request, and a person can send an unwanted message. A good configuration raises the cost of automated attempts and explicitly rejects verification failures. It cannot promise that no spam will ever get through. Start with controls you can verify after publishing.

Choose protection for the form

ZoraForms supports Cloudflare Turnstile and Google reCAPTCHA. Choose a provider and key type that fits the visitor experience you want: Turnstile can run in Managed, Non-interactive, or Invisible mode; reCAPTCHA offers v2 and v3 variants. A v3 configuration also includes a score threshold and expected action.

Add a credential in Settings → Security, then choose it for the specific form. The site key is the public integration part; you enter the secret key during setup and cannot read it back from the dashboard. Never put the secret in HTML, page JavaScript, or a repository. After saving protection, download the current code from Integration.

  • Create a key for the correct domain.
  • Store the secret only in the dashboard.
  • Match the form mode to the key mode.
  • Paste a newly generated snippet into the page.

Verification must finish on the server

A CAPTCHA widget in the browser collects a token, but a visible checkbox or invisible widget is not proof on its own. The token is checked by the service server-side. The result and hostname are checked, and reCAPTCHA v3 also checks score and action. The host should match the website domain or an allowed-origin host.

A provider error, timeout, or invalid token should reject the submission. This fail-closed approach prevents a verification outage from silently becoming permission for spam. Do not replace it with JavaScript alone: a script can be skipped and a POST request can be replayed.

Server-side rule
if (!captchaVerification.ok) {
  return rejectSubmission("CAPTCHA verification failed");
}

Layer defenses and do not mistake origin for identity

Combine CAPTCHA with a honeypot, a minimum completion time, and a short-term rate limit. A honeypot is a hidden field a human normally leaves alone; a minimum time limits immediate automated sends. Each method can be bypassed, but together they filter different simple patterns.

An allowed origin states the protocol-and-host combination from which a browser request may come. It helps limit accidental use of the form from another site, but Origin is not a user identity and it is not secret: an HTTP client can forge it. Do not base a security decision solely on that header.

  • Use CAPTCHA for forms exposed to public traffic.
  • Enable the current dashboard-provided honeypot and minimum time.
  • Set precise origins, including www if you use it.
  • Test success, CAPTCHA rejection, and a too-fast submission.

Test and observe instead of declaring victory

After deployment, send a valid test and check the protection result in submission details. Then test a scenario with stale or missing integration code. Changing CAPTCHA, honeypot, or timing settings requires pasting the complete code again, because the form must send the right fields and tokens.

If spam still arrives, look for patterns in content, frequency, and source rather than raising a v3 threshold without checking its effect on real visitors. CAPTCHA reduces automated submissions; it does not eliminate spam conclusively. The dashboard and its statuses help separate new submissions from handled ones; they do not automatically classify every message.

Useful links

Contact-form spam protection: CAPTCHA, server verification, and realistic expectations · ZoraForms Blog