Anti-spam
OctaForms fights spam with three tools: a honeypot with a quarantine, an optional Akismet check, and rate limiting. The security token helps too, but it is deliberately not the main barrier. The guiding principle throughout is that a real person’s submission should never be silently thrown away.
The honeypot and its quarantine
Section titled “The honeypot and its quarantine”A honeypot is a hidden field that real visitors never fill in, but many bots fill automatically. You add one by marking any field with "honeypot": true in the config. See Field types.
Rendering it correctly
Section titled “Rendering it correctly”If you render your own markup, a honeypot only works when it is hidden the right way:
- Hide it with CSS, for example
position: absolute; left: -9999px. Never usetype="hidden", which bots recognise. - Add
tabindex="-1"andautocomplete="off"so keyboards and autofill skip it. - Give it a name that browsers will never autofill. Names like
company,website,phone,emailornameare risky, because a browser can fill them for a real person. The config validator warns about risky names.
The bundled helper and the shortcode render honeypots correctly for you.
The quarantine, not the trash
Section titled “The quarantine, not the trash”When a honeypot is filled, OctaForms does not drop the submission. It stores it in a spam quarantine and returns a response that looks exactly like success, so the bot learns nothing.
This matters because the honeypot’s most common false positive is browser autofill filling the field for a real person. Those real submissions land in the quarantine too, where you can review and recover them.
Find it under Submissions → Spam quarantine. Recovering a submission also sends its emails and webhooks, as if it had come through normally. A sudden spike in the quarantine usually means you are catching real people, so check the honeypot’s name.
The quarantine clears itself after 30 days.
The Akismet check
Section titled “The Akismet check”Turn it on under Settings → Privacy & security. Each submission’s wording, the sender’s e-mail and their IP address are sent to Akismet, which judges whether it looks like spam. You need an Akismet API key. When the Akismet plugin is already active on the site, its key is reused automatically if you leave the field empty.
Two things about this check are deliberate:
- It cannot lose a lead. If Akismet is down, slow, or the key is wrong, the submission goes through untouched. Only a confident spam verdict acts.
- Spam is quarantined, not rejected. A flagged submission is stored with everything a normal submission has, including attachments, the visit path and the consent record. The quarantine view says why each entry landed there (spam trap or content check). If Akismet was wrong, recover the entry and its emails and webhooks are sent as usual. The sender sees the normal thank-you either way, so a spammer learns nothing.
Because the check sends data to an outside service, mention it in your privacy policy when you enable it.
Rate limiting
Section titled “Rate limiting”OctaForms limits requests with two separate counters, because “too many requests” and “too many successful submissions” are different problems:
- Request floods earn a temporary ban. Someone hammering the endpoint gets a
429ban that lifts after a while. - Too many successful submissions from one network get a gentler response: a
429that says “try again shortly”, with aRetry-Afterheader, and never a ban. This is what protects a busy office behind one shared IP during a campaign. Real people filling in the form are asked to wait, not blocked.
Your integration should read Retry-After and wait, rather than retrying immediately. See the error table in Your own JavaScript.
The security token
Section titled “The security token”Every submission carries a token fetched from the API. It does three things: it deduplicates double submits, it acts as a time-trap against instant bot submissions, and it requires JavaScript to obtain.
It is worth being clear that the token is not the primary anti-bot defence. A determined bot can run JavaScript. The honeypot and rate limiting do the heavy lifting; the token raises the cost of the easy attacks.
A form can waive the token with settings.requireToken: false, which you would only do if a security plugin blocks the token endpoint. Honeypot and rate limiting stay active regardless. See the token contract.
One thing to know before you turn the token off: the token is what makes each double-submit check specific to one person. Without it, duplicates are detected from the form, the field values and the visitor’s network address within a short window. So if two different people behind the same network address (an office or a shared connection using NAT) send the exact same answers within about ten minutes, the second one can be folded into the first as a duplicate and you would see only one lead. This is rare, and it never happens with the token on, which is the default.
Adding a captcha or another challenge
Section titled “Adding a captcha or another challenge”OctaForms does not ship a captcha, but it has a ready mount point for one. The octa_forms_challenge filter runs early in the intake, right after the honeypot. Return block and the request is rejected with 400 and the error key _spam: ["challenge_failed"]; the bundled helper then shows the visitor a dedicated message. Return ok (the default) and nothing changes.
add_filter( 'octa_forms_challenge', function ( $verdict, $form, $raw_fields ) { // Verify your challenge token from $raw_fields here. return $verdict; // 'ok' or 'block'}, 10, 3 );On the front end, the helper submits every hidden input inside the form, and window.octaForms.onAttach() lets a script enhance each form as it binds, including forms the callback widget inserts later. That is all a challenge widget needs: render into the form, write its token into a hidden input, verify it in the filter.
One warning from experience. Challenge tokens are usually single-use, and this filter runs before field validation, so a validation error consumes the token. If you wire a challenge, reset its widget whenever the form emits the octaforms:error event. Remember also that pages served from a cache keep their old markup, so your widget will be missing there until the cache clears; decide what your filter does about a missing token on such pages before you turn it on.