Form Validation Explained: Client-Side, Server-Side, and Why You Need Both
Validation checks that what users type makes sense before you save it. The difference between browser-side and server-side validation, why only the server's counts for security, built-in HTML validation, sharing rules with a schema, and writing error messages people understand.
Every form in your app — sign-up, checkout, settings, contact — needs validation: checking that what people submit makes sense before you save or act on it. AI-built apps often have validation that looks thorough, and protects almost nothing. The difference is where it runs.
Two places validation happens
Client-side: in the browser
Checks that run in the user's browser, before the form is sent:
- "Email is required."
- "Password must be at least 12 characters."
- Red outlines and messages appearing as you type.
Purpose: a good user experience. Instant feedback, no waiting for the server.
Server-side: on your backend
Checks that run on your server when the data arrives, before it's saved.
Purpose: correctness and security.
Why only the server's validation counts
Client-side validation is trivially bypassed. Anyone can:
- Turn off JavaScript.
- Edit the page in DevTools and remove the
requiredattribute. - Skip your form entirely and send a request straight to your API with any data they like —
curl, a script, or a browser's Network tab "edit and resend."
So the browser's validation is a courtesy to honest users. The server must re-check everything, as if the front end didn't exist. (Frontend vs Backend.)
A classic AI-built-app bug: the sign-up form limits usernames to 20 letters, but the API accepts 50,000 characters of anything — including HTML that breaks every page it's displayed on. (What Is XSS?.)
What to validate on the server
For every field:
- Present? Required fields exist.
- Right type? A number is a number; a date is a date.
- Right format? An email looks like an email; a postcode fits the pattern.
- Right size? Minimum and maximum lengths; numbers within a sensible range. (No negative quantities, no 10 MB "names.")
- Allowed values? A
planfield is one offree,pro,team— nothing else. - Allowed for this user? The user can't set fields they shouldn't:
isAdmin,price,ownerId,credits. Only accept the fields the form is meant to change. (Authentication vs Authorization.)
And some checks only the server can do:
- Is the email already taken?
- Does this coupon exist and is it still valid?
- Is the item in stock?
Built-in browser validation
HTML has free validation built in — use it for the client-side part:
<form>
<label for="email">Email</label>
<input id="email" name="email" type="email" required>
<label for="age">Age</label>
<input id="age" name="age" type="number" min="13" max="120">
<label for="username">Username</label>
<input id="username" name="username" required minlength="3" maxlength="20"
pattern="[a-z0-9_]+">
<button>Sign up</button>
</form>
type="email" also brings up the right keyboard on phones. (Regular Expressions for Beginners explains pattern.)
One set of rules, used in both places
Writing the same rules twice — once for the browser, once for the server — means they drift apart. The modern approach is a schema: define the rules once, and use them in both places. In TypeScript projects, Zod is the common choice:
import { z } from "zod"
export const signUpSchema = z.object({
email: z.string().email(),
username: z.string().min(3).max(20).regex(/^[a-z0-9_]+$/),
age: z.number().int().min(13).max(120),
})
The browser uses it for instant feedback; the server uses the same schema to check the request before touching the database. (Validating Input With Zod goes further.)
Error messages people understand
Validation is also a conversation with your users. Good messages:
- Say what's wrong and how to fix it. "Password must be at least 12 characters" beats "Invalid password."
- Appear next to the field, not only in a banner at the top.
- Keep what the user typed. Don't clear the whole form because one field was wrong.
- Don't blame. "Please enter a date in the future" beats "Invalid input!"
- Are accessible — connected to the field for screen readers, not just shown as a red border. (Web Accessibility Basics.)
- Don't reveal too much. On login and password reset, don't confirm whether an email has an account.
Don't over-validate
Some validation annoys real users for no benefit:
- Names — people's names contain apostrophes, hyphens, spaces, accents, and non-Latin characters. Don't restrict them to
A–Z. - Email — perfect email validation is impossible. Do a light check and send a confirmation email.
- Phone numbers and addresses — formats vary enormously between countries.
- Password rules — long passphrases beat forced symbol-and-number combinations. Allow paste, so password managers work.
The checklist
- Every form's data is validated again on the server
- Types, formats, lengths, and ranges are checked
- Users can't set protected fields
- Rules are defined once (a schema) and shared
- Errors are specific, next to the field, and keep the user's input
- Names, emails, and phone numbers aren't over-restricted
EasySpawn runs your app's backend as a real server, so server-side validation has somewhere to live — and Claude Code can test it by sending the bad requests an attacker would. See how it works for AI-built apps or join the waitlist.
Related: SQL Injection Explained · What Is CRUD? · A Security Checklist for Vibe-Coded Apps
Keep reading
What Is a JWT? JSON Web Tokens Explained Simply
Supabase, Firebase, Auth0, and Clerk all hand your app JWTs. What a JSON Web Token is, its three parts, why anyone can read it but nobody can forge it, how apps use it for login, and the mistakes AI-generated code makes with tokens.
GDPR Basics for App Builders: What a Small App Actually Needs
If anyone in the EU or UK uses your app, GDPR probably applies. What personal data is, the principles in plain English, lawful bases, the rights users have (access, deletion), what to do about third-party services and data breaches, and a practical checklist for a small app. Not legal advice.