Browser validation
Validate on both client and server
Use browser validation for immediate feedback and server validation as the final authority over every submitted value.
Client validation and server validation are not two copies of the same thing. They solve different problems, and you need both.
Client validation is about speed. It catches an empty required field before any request leaves the browser. It points at the field, keeps the person’s place in the form, and costs nothing on the server.
Server validation is about trust. A visitor can delete attributes in DevTools, turn off JavaScript, edit a hidden value, or send the request with curl and never load your page. Nothing that runs in the browser can protect the server from that.
An example with a hidden field
Imagine a form for reserving seats at a workshop:
<input name="seats" type="number" min="1" max="4" required>
<input name="workshopId" type="hidden" value="42">
The browser checks the visible number. It has no idea what workshop 42 is, or whether four seats are still free. Those are decisions only the server can make:
- Parse
seatsas an integer. - Reject values below 1 or above 4.
- Load workshop 42 from trusted storage.
- Check that it exists and still has enough space.
- Check that the current user may reserve it.
- Create the reservation without allowing a race to oversell it.
Changing the hidden ID to another workshop takes two seconds in DevTools. And seats=4 passing the range check proves nothing about availability. Format validation and business validation are separate layers. HTML gives you the first one. The second one is always yours.
What the server sends back
When a check fails, return errors in a shape the form can use. A response like this lets the page put each message next to the right control:
{
"errors": {
"seats": "Only 2 seats are left for this workshop"
}
}
Send back the safe values too, so the person can fix one field instead of retyping everything. And keep two kinds of failure apart. Bad input gets a field error. A database timeout gets a general “something went wrong, try again” message. Don’t blame the person’s input for your server’s problems.
Keep the rules in one place
If your stack allows it, derive both sides from one schema. A shared validation object can generate the min and max attributes and run the same check on the server. That avoids the classic drift where the page says 4 and the endpoint says 3.
But sharing the rules never means skipping the server side. The browser is a convenience for the visitor. The server is the authority.
Try this: open DevTools, remove max="4" from the input, submit seats=100, and confirm the server rejects it. That single test tells you whether your validation is real.
Lesson completed