Middleware & Security
Documentation / Middleware & Security / Anti-Bot Honeypot

Anti-Bot Honeypot

Runique provides a honeypot middleware that automatically protects forms against simple bots — without any modification to your handlers.

How it works

  1. On first visit, the middleware generates a random 16-character hex field name and stores it in the session.
  2. On every request, the name is injected as an Axum extension (HoneypotFieldName).
  3. Request::form() reads it and adds the field to the rendered HTML (invisible via hp.css).
  4. On any method other than GET and HEAD (POST, PUT, PATCH, DELETE…), if the field is non-empty → force_invalid = true → form.is_valid() returns false immediately.

The handler sees a normal form validation failure — no special case needed.

Activation

.middleware(|m| m.with_anti_bot())

That is the only change required in main.rs.

Template rendering

The honeypot field is injected automatically:

  • {{ form | form }} (full render): honeypot appended after the last field
  • Field by field ({{ form | form(field="name") }}): honeypot appended after the last field

The honeypot_html key is also available directly in the Tera context if you need to place it manually:

{{ form.honeypot_html | safe }}

Security properties

PropertyValue
Field nameRandom hex-16, session-bound
RotationNew name per session (persists across GET/POST)
VisibilityHidden via external CSS (hp.css) — CSP-safe, no inline style
Bot resistanceBlocks form-fillers that fill all fields unconditionally
Human impactNone — the field is invisible and ignored by browsers

The field name has no recognizable prefix — a bot cannot skip it by pattern matching.

Local testing constraint

The session cookie uses Secure=true in production (RUNIQUE_ENV=prod). On an http address that is not localhost (LAN IP, machine name), the browser does not send a Secure cookie: the session, and the trap field's name with it, is created again on every request, and the trap never triggers. Chrome and Firefox accept a Secure cookie on http://localhost, other browsers may not.

Test locally with RUNIQUE_ENV=dev. On the VPS over HTTPS, the middleware works correctly without any override.

Slot

65 — right after CSRF (60), before the handler. Requires the session middleware (slot 50) to be active.