Insane Forms

Spam protection

The honeypot, rate limits, Allowed Origins and Turnstile.

Every Form runs a set of spam checks. Some are always on. You switch on the others Form by Form in its Spam tab.

A Submission the checks catch is stored as Spam, gets the same answer as a real one (201, or the 303 redirect) and sends no Notification or webhook. It waits 30 days and is then deleted. If something real was caught, mark it as not spam in the dashboard: it moves to the inbox and its Notification goes out. Files sent with a caught Submission aren't stored. Spam doesn't count towards your Plan's monthly Submissions.

Honeypot

Add a field named _gotcha to your form and hide it from people. Bots fill in every field they find, so a Submission with anything but whitespace in _gotcha is Spam.

The name is the same one Formspree and Basin use, so forms moved from either keep working. The field is removed before the Submission is stored.

Rate limits

Always on, for every Form:

  • 10 requests a minute from one visitor IP, across all Forms.
  • 300 requests a minute to one Form, from everyone.

Past either limit the answer is 429 rate_limited with a Retry-After header that says how many seconds to wait. Direct uploads have their own counters with the same numbers. From a server, pass the visitor's IP in X-Visitor-IP with a Submit Key so the limits count the visitor and not your server.

Allowed Origins

Off by default, so any website can post to the Form. Add the sites that may post and a browser post from any other site gets 403 origin_not_allowed. Server-side submits with a valid Submit Key skip this check.

Each entry is a scheme, host and optional port, with no path. Enter https://atelierondo.test, not https://atelierondo.test/contact. A bare atelierondo.test is read as https://atelierondo.test. Matching is exact and ignores case, so https://www.atelierondo.test is a separate entry, and wildcards aren't supported. A Form can hold up to 50.

The check reads the request's Origin header, then Referer. A request that sends neither is refused, which includes a page served with Referrer-Policy: no-referrer. With Allowed Origins set, the CORS answer names only the origin that asked, and only when it's on the list.

A Project can hold a default list of Allowed Origins that each new Form copies when it is created. Changing the default later doesn't touch existing Forms.

Cloudflare Turnstile

Switch Turnstile on and the endpoint asks Cloudflare to verify the token in the cf-turnstile-response field, the name Turnstile's widget uses. A missing or failing token gets 403 turnstile_failed, and nothing is stored. A Submit Key doesn't skip this check: a server sending to such a Form has to forward the visitor's token. Direct upload requests don't run the check, but the Submission that names the uploaded files does.

The dashboard doesn't show a Turnstile site key for your widget yet. Until it does, keep Turnstile off: with it on and no widget on the page, every Submission is rejected.

Timing

The React library will send _elapsed, the milliseconds between the form appearing and being sent. Under 1,500 counts as Spam. A request without it, or with a value that isn't a number, skips this check, so plain forms aren't affected.

On this page