A public form is a door anyone can knock on, so the first question is what a visitor is allowed to ask for. This guide defines that operation, separates trusted configuration from anything the client supplies, explains why several different controls are needed and what each one cannot tell you, walks through an invented review of a small contact route, handles ambiguous results and keeps logging proportionate. It offers a review process, not a claim that any form is fully safe.
Define the operation a visitor is allowed to request
A public form should expose a specific operation. A newsletter request should identify the intended publication through trusted configuration; a contact form should have an approved recipient. Hidden inputs are still values supplied by the client and cannot establish the authority to choose a different recipient, sender or account.
Write a short contract for the request: the fields accepted, their types and limits, and the result the operation can produce. Reject unexpected fields rather than passing an arbitrary object into a service integration. Keep administrative actions on a separate, appropriately protected interface.
A useful comparison is between what the publication decides and what the request merely carries.
- Trusted configuration: the approved recipient, the publication identity, the sender, the list of accepted fields and their limits, and which single operation the route performs.
- Client-supplied values: every submitted field including hidden inputs, anything presented as a forwarding header and any claim about who is sending. These are input to bound and validate, however the page was designed to present them.
Use several controls for different problems
Input validation can reject malformed or oversized requests, but automated systems can submit perfectly ordinary-looking values. A honeypot may catch some unsophisticated traffic; it is not a complete defence. OWASP’s input-validation guidance, listed in this article’s sources, is relevant to the input-validation part of the problem: deciding what a well-formed request looks like and rejecting the rest.
Rate controls, provider protections and monitoring address other problems. Give each control its own owner and review its limits; do not assume that one covers the gaps left by another.
Edge identity, forwarding headers and shared budgets
If a reverse proxy stands in front of the application, decide where the real visitor address is trustworthy. Do not trust a freely supplied forwarding header. A header the sender can write is only a claim; the address seen by a trusted edge is a different kind of evidence, and the application should know which of the two it is reading.
Distinguish a per-visitor control at a trusted edge from a global resource limit for the publication; otherwise all legitimate visitors may accidentally share a tiny quota.
- Per-visitor control: applied at a trusted edge against the visitor identity that edge can see. Its job is to stop one source from consuming the route.
- Global budget: kept by the application for the publication’s overall resources. It protects the shared resource, but if it is the only control, one noisy source can use up capacity that legitimate visitors needed.
- Mixing them up: if visitor identity is read from an untrusted header, either control may misidentify visitors, and if a global limit is used as though it were per-visitor, legitimate visitors share one tiny quota.
Keep security controls distinct from identity claims
An Origin check can help reject requests presented by a browser as coming from an unapproved page, but it does not prove that the requester is a particular person or that traffic is human. MDN’s documentation of the Origin header describes what the header is for. Use the security mechanisms appropriate to the action and to any authenticated session involved.
It helps to ask of each control what it can tell you, and to keep the answer modest.
- Validation: whether a request meets the stated contract, not whether the sender means well.
- Honeypot: may catch some unsophisticated automated submissions; failing to catch one proves nothing.
- Origin check: whether a browser presented the request as coming from an approved page; it does not identify a person or show that traffic is human.
- Edge limit and global budget: volume per visitor identity at a trusted edge, and overall consumption of the publication’s resources.
- Monitoring and logs: visibility for operators, within the limits discussed under logging below.
When the result is ambiguous
For a simple public enquiry, also review what happens when a network result is ambiguous. Automatically sending the message again can create duplicate mail. The confirmation guide and launch checklist show why an uncertain result deserves its own state. Repeated processing of the same request needs a defined outcome too, so that a retry by a person or by software does not silently become a second message.
Log enough to operate, without copying the submission everywhere
A team may need counts of failures, processing status and an identifier for a request. That does not mean every application and proxy log should contain a person’s address or message. Decide which component stores which data and who can access it. Review the sending service and hosting logs as well as the application itself.
Use synthetic content for routine tests, protect necessary secrets outside the public site and keep them out of examples. The practical goal is an inspectable operation with bounded authority, not a slogan that the form is fully safe or stores no data anywhere.
Review a small contact route
Consider a fictional publisher, Example Quarterly, reviewing a contact route that accepts a name, reply address, topic and message. This worked example describes no real system or test.
Step one is the contract. The reviewers list the four accepted fields, give each a type and a bound, and define the permitted operation: sending a plain-text enquiry to the approved recipient. They find a hidden destination field. Because it is client-supplied, they remove it from the template and reject requests that still carry it.
Step two is identity. The application currently reads a forwarding header to decide who is calling. The reviewers ask where the real visitor address is trustworthy, conclude that the header is freely supplied, and decide that the per-visitor control belongs at the trusted edge while the application keeps a separate overall budget. They choose no figures in the review itself; the numbers are a later decision for the owner who can see real traffic.
Step three is construction and results. The route builds its message through a structured library, validates the reply address as one value and returns a useful error when a new request is limited. The reviewers write distinct wording for limited, rejected and uncertain outcomes and decide what happens if the same request is processed twice.
Step four is data. They ask which component stores what: the application, the proxy and the sending service may each hold request information. They decide that routine tests use synthetic content, that a request identifier and a status are enough for most operations, and that someone is named to review hosting and sending-service logs. The result is a list of findings, not a certificate; a form reviewed this way can still be attacked.
Messages for limited, rejected and uncertain results
These illustrative messages cover different failure states. Use each only when it matches the actual result; none reports that a message was sent.
- Limited: “We cannot accept another request just now. Please try again later. Your message is still in the box below.”
- Rejected input: “One of the fields is outside what this form accepts. Please check the length of your message and try again.”
- Uncertain: “We could not confirm that your message was received. Please wait before trying again, because sending it a second time might produce a duplicate.”
- Never claimed: wording such as “your message is safe” or “automated traffic cannot reach this form”, because the review establishes neither.
A public-form review checklist
Use this checklist to review the operation and identify questions for its owner. It is not a security certificate.
- Are the destination, publication identity and sender fixed in trusted configuration, with no hidden input able to change them?
- Is there a written contract for fields, types and limits, and are unexpected fields rejected?
- Is any visitor address used for a control taken from a place the edge trusts, rather than from a freely supplied forwarding header?
- Are per-visitor and global controls separate, and does each have a named owner?
- Is the honeypot treated as one partial signal, and is validation described as a check on shape rather than on intent?
- Is an Origin check described only as a check on the page a browser presents, not as proof of a person?
- Does the route define outcomes for repeated processing, limited requests and uncertain results, with no automatic resend?
- Does each log store only what operations need, and have application, proxy, hosting and sending-service logs all been reviewed?
- Do routine tests use synthetic content, with secrets kept outside the public site and out of examples?
- Do public statements avoid claiming that the form is fully safe or stores no data anywhere?
- 1 Public boundary
- 2 Server checks
- 3 Rate controls
- 4 Honest result
Questions readers ask
Can hidden fields authorise a form to use another recipient or account?
No. Hidden inputs are still client-supplied values. Fix the publication and approved destination through trusted configuration, and reject fields outside the request's defined contract.
Why is a per-visitor rate control different from a global quota?
A trusted edge can apply a visitor control, while the application keeps a separate overall resource limit. Confusing the two can make legitimate visitors share one tiny quota. Freely supplied forwarding headers are not a trustworthy visitor identity.
Does an Origin check prove that traffic is human?
No. It can help reject browser requests presented as coming from an unapproved page, but it does not identify a particular person or establish that the traffic is human.
