What Happens When You Type a URL and Press Enter
DNS lookups, TCP and TLS handshakes, HTTP requests, servers, and the browser's rendering pipeline. The whole journey of a web page, explained so it finally clicks.
Read →Client-side validation makes forms pleasant; server-side validation keeps your app safe. How to validate properly with schemas, share rules between frontend and backend, and avoid common gaps.
Every app that accepts input, which is every app, needs validation. Where it happens matters a lot, and the most common mistake is treating client-side checks as if they protect the server. They don't. Here's how to do both properly.
Validation in the browser gives instant feedback: "That email doesn't look right," "Password must be at least 12 characters." It saves users from submitting a form only to be told what went wrong.
HTML has a lot built in:
<input type="email" required>
<input type="number" min="1" max="10">
<input type="text" minlength="3" maxlength="40" pattern="[a-z0-9-]+">
Plus JavaScript for richer rules and better messages.
But anyone can bypass the browser entirely: disable JavaScript, edit the HTML, or send requests with curl or a script. Client-side validation is a convenience, never a security measure.
Your server must validate every input as if the frontend didn't exist, because for an attacker, it doesn't. That includes:
Hand-written if checks get inconsistent quickly. A schema library declares the rules in one place and handles the details:
import { z } from 'zod'
const SignupSchema = z.object({
email: z.string().trim().toLowerCase().email(),
password: z.string().min(12).max(200),
displayName: z.string().trim().min(1).max(40).optional(),
plan: z.enum(['free', 'pro']),
})
app.post('/api/signup', async (req, res) => {
const result = SignupSchema.safeParse(req.body)
if (!result.success) {
return res.status(400).json({ error: 'Validation failed', fields: result.error.flatten().fieldErrors })
}
const data = result.data // trimmed, normalised, typed
// ...
})
Equivalent tools exist everywhere: Pydantic in Python, Laravel's validation rules, Joi, Yup and Valibot in JavaScript, and so on.
Define the schema once and use it on both sides. The frontend uses it to show friendly errors; the backend uses it to enforce them. With TypeScript, the same schema also produces your types. (Is TypeScript worth it?)
That eliminates the classic bug where the frontend allows 50 characters and the backend allows 40.
Describe what's allowed, not what's forbidden.
status only from a fixed set of values."role": "admin" to their profile update and an ORM saves it.These are different jobs:
Even perfectly valid input, like a comment containing <script>, can be dangerous if output unsafely. Use parameterized queries for SQL and your framework's auto-escaping for HTML. (Security checklist.)
Some validation needs data only the server has:
These checks must happen server-side, ideally backed by database constraints (unique indexes, foreign keys, check constraints) as the final safety net.
Respond with a 400 (or 422) and field-level messages the frontend can display next to the right inputs:
{
"error": "Validation failed",
"fields": {
"email": ["Enter a valid email address"],
"password": ["Must be at least 12 characters"]
}
}
(Consistent API error formats.) Keep messages human and specific, and never echo back raw internal errors.
No. Client-side validation can be bypassed easily. It improves user experience, but the server must validate every input independently.
Use a schema validation library to define allowed fields, types, formats and limits in one place, reject or strip unknown fields, and back critical rules with database constraints.
Validation checks whether input meets your rules and rejects it if not. Sanitization or escaping transforms data to make it safe in a particular output context, like HTML or SQL.
DNS lookups, TCP and TLS handshakes, HTTP requests, servers, and the browser's rendering pipeline. The whole journey of a web page, explained so it finally clicks.
Read →There are dozens of HTTP status codes, but about fifteen cover nearly everything. What each one means, when to return it, and the mix-ups (401 vs 403, 301 vs 302) that confuse everyone.
Read →A, AAAA, CNAME, MX, TXT and NS records in plain English, why DNS changes seem slow, and the troubleshooting commands that tell you what is really going on.
Read →