Skip to content
Formlander

Spam and security

How Formlander keeps spam out of your forms - allowed sites, rate limits, a honeypot, and Cloudflare Turnstile - and what it stores about senders.

Formlander has four protections. Allowed sites and the rate limits are always on. The honeypot and Turnstile are yours to add.

Each form has a token in its address: /forms/contact/submit?token=abc123. The token is public, and you put it in your HTML.

A token only lets someone submit to that one form. It cannot read submissions or change settings. A token alone does not stop someone from copying your form to another site: allowed sites do that.

Formlander reads the Origin or Referer header of each submission, and accepts it only from the sites of the form. Set them in the form’s Allowed sites field. The field is required when you create a form.

Allowed sites Accepts
example.com example.com only
example.com, www.example.com both, and nothing else
*.example.com example.com and every subdomain of it
* every site. Use it only to test
empty nothing: every submission is rejected

A rejected submission gets 403 Forbidden. The message names the site to add:

{ "ok": false, "error": "origin not allowed: add shop.example.com to this form's Allowed Origins" }

A request with no Origin and no Referer header is rejected too. A test from localhost fails until localhost is in the list.

The limits are fixed, and no setting changes them:

What Limit
Form submissions 30 requests a minute from each IP address, for all forms together
Admin login 5 attempts a minute from each IP address

The count starts again each minute. Both limits are off in development and test. A request over the limit gets 429 Too Many Requests:

{ "error": "Too Many Requests", "message": "Rate limit exceeded. Please try again later.", "retry_after": 60 }

A honeypot is a hidden field that people leave empty and bots fill. Add it to your form:

<input type="text" name="__fl_hp" value="" tabindex="-1" autocomplete="off" style="position:absolute;left:-9999px">

When the field has a value, Formlander keeps the submission, marks it as spam, and sends no email or webhook for it. The bot gets a normal success answer, so it learns nothing.

Turnstile stops bots that get past the other protections.

  1. In the Cloudflare dashboard, add a Turnstile widget for your site. Copy its Site Key and Secret Key.
  2. In Formlander, go to Settings → Captchas and add a profile: a name, the Site Key, and the Secret Key. Theme, size, language, and action name are optional.
  3. Click Test on the profile. It proves that Cloudflare accepts the Secret Key.
  4. Edit your form, and choose the profile in Captcha.
  5. Add the widget to your HTML, inside the form:
<div class="cf-turnstile" data-sitekey="YOUR_SITE_KEY"></div>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>

The widget sends the field cf-turnstile-response with the form. When the check fails, the submission gets 400 Bad Request:

{ "ok": false, "error": "captcha verification failed" }

A submission keeps the fields that the person sent, the files, the User-Agent header, and the spam mark. Formlander does not store IP addresses: it uses them only for the rate limit, in memory.

A contact form with the honeypot and Turnstile. In the form’s settings, Allowed sites is example.com, www.example.com, and Captcha is your Turnstile profile.

<form action="https://forms.example.com/forms/contact/submit?token=YOUR_TOKEN" method="post">
<input type="text" name="__fl_hp" value="" tabindex="-1" autocomplete="off" style="position:absolute;left:-9999px">
<label>Name <input type="text" name="name" required></label>
<label>Email <input type="email" name="email" required></label>
<label>Message <textarea name="message" required></textarea></label>
<div class="cf-turnstile" data-sitekey="YOUR_SITE_KEY"></div>
<button type="submit">Send</button>
</form>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
Problem Fix
origin not allowed Add the site that the message names to Allowed sites. For subdomains, add *.example.com
the request has no Origin or Referer header The request does not come from a browser page, or a privacy setting strips both headers. Submit from your site
429 Too Many Requests Wait a minute. Check that no script sends the form in a loop
captcha verification failed Check that the Site Key and the Secret Key come from the same Turnstile widget, that the form uses the profile, and that the widget script loads
The widget does not show Check the Site Key in the profile, and the script tag on the page

To see blocked submissions, read the logs: chasen -a formlander logs, or docker logs of the container.