CORS, Origin Allowlists and Rate Limits: What Actually Protects a Form?
Separate browser read restrictions, server-side origin policy and request throttling when defending public lead collection endpoints.
These three controls solve different problems
CORS is a browser-enforced cross-origin sharing mechanism. It is not a firewall that prevents curl, bots or server-side clients from sending a request. An origin allowlist is a server policy decision about which website origins are expected to use an endpoint, while a rate limit constrains how frequently requests can be processed.
A public lead form still requires input validation and an abuse strategy. None of these three controls alone proves that a request came from a human.
| Control | Actually constrains | Does not guarantee |
|---|---|---|
| CORS | Browser access to cross-origin responses | Non-browser requests are blocked |
| Origin allowlist | Requests with permitted declared origins | Origin header is unforgeable |
| Rate limiting | Volume by configured identity/key/window | Every allowed request is legitimate |
| Server validation | Accepted request shape and limits | The sender is a human |
| Bot verification | Challenge/behavior proof | Perfect bot or fraud detection |
A realistic request arrives without your web page
A script can POST directly to an exposed form endpoint without loading your site at all. That is why hiding a button, disabling JavaScript or setting restrictive CORS headers is not adequate protection. Treat every incoming field and header as untrusted until the server has applied the appropriate checks.
Origin and Referer headers are useful browser context but may be absent or spoofed by non-browser clients. Enforce policies appropriate to the supported submission modes rather than assuming their presence proves authenticity.
Design layered rate limits without harming real users
- Limit burst traffic per useful key and consider shared IPs, NAT and proxies.
- Distinguish anonymous browser submissions from authenticated server-to-server clients.
- Rate-limit heavy operations before expensive processing or remote API calls.
- Return clear recoverable 429 responses where appropriate.
- Monitor false positives, especially from offices, mobile networks and shared venues.
A simple policy sketch
The origin policy in this sketch is a business-specific choice: legitimate API clients may not send an Origin header. In production, durable persistence must complete before acknowledging an accepted submission.
// Simplified decision order, not a drop-in public endpoint.
async function admitSubmission(request) {
if (!hasSupportedOriginContext(request)) return 403;
if (await exceedsRateLimit(request)) return 429;
if (!hasValidBodyShape(request.body)) return 400;
if (!(await verifyBotProof(request))) return 403;
return 202; // Accept only after durable persistence.
}