Panther Hook Blog
Security & Anti-Spam

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.

LAYERED CHECKSCORSOriginRate limitForm API

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.

Security boundaries that are easy to confuse
ControlActually constrainsDoes not guarantee
CORSBrowser access to cross-origin responsesNon-browser requests are blocked
Origin allowlistRequests with permitted declared originsOrigin header is unforgeable
Rate limitingVolume by configured identity/key/windowEvery allowed request is legitimate
Server validationAccepted request shape and limitsThe sender is a human
Bot verificationChallenge/behavior proofPerfect 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.

javascript
// 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.
}
Read the public submission API guide