Contact forms, login endpoints, and public APIs are frequent abuse targets: spam, brute force, scraping, and automated submissions. Effective protection combines several layers; none alone is enough.
This article describes a layered approach to protecting forms and endpoints in Astro, Laravel, and Node.js applications — the same pattern this portfolio uses in production.
Protection layers
- Rate limiting: limit requests per IP in a time window.
- Honeypot: hidden field bots fill and humans do not.
- Form timing: detect submissions faster than a human (< 3.5s).
- Origin check: validate Origin/Referer in production.
- Schema validation: Zod or equivalent on the server.
- Sanitization: clean inputs before processing or storing.
- Optional CAPTCHA: Cloudflare Turnstile when volume justifies it.
Rate limiting
Apply on sensitive public endpoints:
- Login and registration: 5 attempts per IP per minute.
- Password reset: 3 per IP per hour.
- Contact form: 5 per IP per minute.
- Unauthenticated APIs: depends on the use case.
In-memory implementation for a single process; Redis when there are multiple replicas. Respond with 429 Too Many Requests and Retry-After header.
Honeypot
Hidden field with CSS (display: none or position: absolute; left: -9999px). Bots fill it automatically; humans do not see it. If it arrives with a value, reject silently (respond 200 without processing, to avoid tipping off the bot).
Form timing
Record a timestamp when rendering the form (hidden field or session). On POST, reject if elapsed time is under 3–4 seconds. Bots submit instantly; humans need at least a few seconds to read and complete.
Server-side validation
Schemas with Zod (or equivalent) on every API endpoint:
- Strict types: email as email, phone with format, max length.
- Explicit required vs optional fields.
- Reject payloads with unexpected fields (strip or reject).
- Generic error messages to the client; detail only in server logs.
Sanitization
- Strip HTML from plain text fields.
- Normalize whitespace and control characters.
- Truncate strings that exceed max length.
- Never interpolate user input into SQL queries or templates without escaping.
Origin check
In production, validate that the Origin or Referer header matches the site domain. Reject requests from unknown domains. Not foolproof (can be forged), but filters basic bots and cross-origin scripts.
Cloudflare Turnstile
When spam volume justifies extra friction, Cloudflare Turnstile is lightweight and privacy-friendly. Configure with TURNSTILE_SITE_KEY (public) and TURNSTILE_SECRET_KEY (server). The server verifies the token before processing the form.
Full flow on an endpoint
- Rate limit check → 429 if exceeded.
- Honeypot check → silent 200 if filled.
- Form timing check → 400 if too fast.
- Origin check → 403 if invalid origin.
- Turnstile verification (if configured) → 400 if invalid.
- Zod validation → 400 with field errors.
- Sanitization → process.
Common mistakes
- Relying only on frontend validation.
- Rate limiting only on login, not contact or webhooks.
- Visible honeypot or accessible
tabindex(screen readers fill it). - Responding 403 to honeypot (the bot learns and adapts).
- Logging full payloads with emails and phones (PII in logs).
- In-memory rate limit with 3 replicas (each has its own counter).
Connect with security audit, cybersecurity, and REST APIs .