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.
Form tokens
Section titled “Form tokens”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.
Allowed sites
Section titled “Allowed sites”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.
Rate limits
Section titled “Rate limits”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 }Honeypot
Section titled “Honeypot”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.
Cloudflare Turnstile
Section titled “Cloudflare Turnstile”Turnstile stops bots that get past the other protections.
- In the Cloudflare dashboard, add a Turnstile widget for your site. Copy its Site Key and Secret Key.
- 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.
- Click Test on the profile. It proves that Cloudflare accepts the Secret Key.
- Edit your form, and choose the profile in Captcha.
- 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" }What Formlander stores about senders
Section titled “What Formlander stores about senders”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.
Example
Section titled “Example”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>Troubleshooting
Section titled “Troubleshooting”| 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.