HTML Validation
Use built-in HTML validation attributes to catch obviously invalid input before submission, while understanding clearly that client-side validation is a convenience layer, never a substitute for server-side validation.
HTML5 gives browsers a surprising amount of built-in validation power, entirely without JavaScript: required fields, length limits, numeric ranges, step increments, and full regex pattern matching. Used well, these attributes catch obvious mistakes immediately and give users fast, friendly feedback. But there is one rule that every developer must internalize early: none of this replaces validating the same data again on the server. Client-side validation is a UX nicety that a user can bypass entirely — the server is the only checkpoint that actually protects your data and your system.
required
The required boolean attribute marks a field as mandatory. If the user tries to submit the form while a required field is empty, the browser blocks submission and shows a native validation message pointing at the offending field — no custom code needed.
minlength and maxlength
minlength and maxlength constrain the number of characters allowed in text-based fields like text, password, and textarea. maxlength actively prevents the user from typing past the limit as they type; minlength is checked at submission time, blocking submission if the entered value is too short — for instance, requiring a password to be at least 8 characters.
min, max, and step
For numeric and date-like inputs (number, range, date, time, and similar), min and max set the allowed lower and upper bounds. step controls the increment values must land on — for example, step="5" on a number input only accepts multiples of 5 relative to the min. Together these three attributes let a quantity field require, say, a whole number between 1 and 100 in steps of 1, or a price field allow increments of 0.01.
pattern — regex validation
The pattern attribute accepts a regular expression that the input's value must fully match to be considered valid. It's commonly used for formats that built-in types don't cover directly — enforcing a specific product-code format like 'AB-1234', or a postal code shape, or a username restricted to letters, numbers, and underscores. Because pattern is checked against the entire value, the regex doesn't need explicit ^ and $ anchors — HTML applies it as a full match automatically.
Type-based validation
Recall from the input controls lesson that certain type values carry their own built-in validation for free: email checks for a plausible email shape, url checks for a well-formed URL, number rejects non-numeric characters entirely. Choosing the accurate type is itself a form of validation, before you add a single extra attribute.
Native validation UI
When a submission is blocked by any of these attributes, the browser automatically shows its own validation bubble — a small popup pointing at the invalid field with a message like 'Please fill out this field' or 'Please match the requested format.' This UI is free, requires no CSS or JavaScript, and is reasonably accessible by default, though its exact appearance and wording varies from browser to browser and can't be styled with much precision. Many production apps eventually layer custom JavaScript-driven validation messages on top for more control, but the native mechanism is a genuinely solid baseline.
The critical limitation: client-side validation is never enough
Every one of these attributes runs inside the user's own browser — and anything running in the user's own browser is fundamentally something the user controls. A user can disable JavaScript, edit the HTML with browser developer tools to remove required or pattern before submitting, or skip the browser entirely and send a raw HTTP POST request straight to your server's endpoint using a tool like curl or a script. None of the HTML5 validation attributes exist on that raw request at all — they're not enforced anywhere except inside the specific browser rendering that specific page.
This means client-side validation must be understood as a UX convenience only: it gives honest users fast, friendly feedback without a round trip to the server. It does nothing to protect your application from malicious or malformed input, because it is trivially bypassable by anyone with basic tools. The server must independently re-validate every single piece of incoming data — length, format, range, required-ness, all of it — before trusting or storing it. Client-side and server-side validation are not redundant; they solve different problems for different audiences, and skipping the server-side half is a real, common security mistake.
Validation attributes
required, minlength/maxlength, min/max/step, and pattern combine on a single field freely. pattern is matched against the entire value automatically, no anchors needed. All of this runs only in the browser — the server must validate independently.
<input type="text" name="username" required minlength="3" maxlength="20"
pattern="[A-Za-z0-9_]+">
<input type="number" name="quantity" min="1" max="100" step="1" required>Code Examples
The browser blocks submission if this field is left empty or the entered password is under 8 characters, showing a native validation message pointing at the field — without a line of JavaScript.
Only whole numbers from 1 through 50 are accepted; entering 0, 51, or a fractional value like 2.5 is rejected before submission, thanks to the combination of min, max, and step.
The pattern requires exactly two uppercase letters, a hyphen, then four digits. The title attribute supplies the text shown in the browser's native validation bubble when the pattern doesn't match, since pattern alone gives no hint about the expected format.
This markup stops a normal user from submitting an empty email or a 4-character password through the browser's UI. But anyone can send a POST request straight to /create-account with curl or Postman, bypassing the browser (and this HTML) entirely — so if the server doesn't independently re-check that the email is present and the password meets length requirements, invalid or malicious data will be accepted and stored regardless of what this form declares.
Common mistake: Trusting required/minlength/pattern as if they were a security boundary, instead of re-validating the same rules on the server.
<label for="pwd">Password</label>
<input type="password" id="pwd" name="pwd" required minlength="8"><label for="qty">Quantity</label>
<input type="number" id="qty" name="qty" min="1" max="50" step="1" required><label for="code">Promo code</label>
<input type="text" id="code" name="code"
pattern="[A-Z]{2}-[0-9]{4}"
title="Format: two uppercase letters, a hyphen, then four digits (e.g. AB-1234)"><form action="/create-account" method="POST">
<input type="email" name="email" required>
<input type="password" name="password" required minlength="8">
<button type="submit">Sign Up</button>
</form>Where validation actually happens
Client-side validation is a convenience layer for honest users; server-side validation is the only layer that actually protects the application.
Browser (HTML attributes) --catches typos, honest mistakes--> fast UX feedback
Server (server-side code) --catches EVERYTHING, including bypassed requests--> real protectionImportant Rules
required, minlength/maxlength, min/max/step, and pattern are all checked automatically before a form submits, with no JavaScript needed.
pattern is applied as a full match against the value — no ^ or $ needed in the regex.
pair pattern with a title attribute so users see a helpful hint, not just a generic 'invalid format' message.
Client-side validation can always be bypassed — by editing the DOM, disabling JavaScript, or sending requests directly to the server.
Every field validated in HTML must be validated again, independently, on the server before the data is trusted or stored.
Why this matters in practice
A very common real-world vulnerability pattern is a signup or checkout API endpoint that trusted its own HTML form's required and pattern attributes and skipped equivalent checks in the backend handler — leaving the endpoint open to malformed, oversized, or malicious payloads sent directly, bypassing the browser entirely. Defense-in-depth here is not optional extra credit: it's the difference between a form that merely looks validated and one that is actually validated.
Relying only on <input required pattern="..."> and skipping server-side checks entirely
Keep the HTML validation for UX, and re-check required-ness, length, range, and format again in the server-side handler
A user can strip required and pattern from the DOM with dev tools, or send a raw POST request that never touches this HTML at all — a server that trusts the client has no real protection against invalid or malicious data.
<input type="text" name="code" pattern="^[A-Z]{2}-[0-9]{4}$">
<input type="text" name="code" pattern="[A-Z]{2}-[0-9]{4}"> HTML's pattern attribute already matches against the full value, so ^ and $ anchors are redundant (and while most browsers tolerate them harmlessly, omitting them is the conventional, cleaner form).
[True / False] True or False: Adding required, pattern, and minlength attributes to a login form's inputs is sufficient on its own to protect the server from receiving invalid or malicious data. A. True — HTML5 validation attributes are enforced by the HTTP protocol itself, so invalid data can never reach the server B. False — these attributes only run inside the browser rendering the page and can be bypassed entirely by editing the DOM, disabling JavaScript, or sending requests directly to the server C. True, but only when the form's method is POST rather than GET D. False, but only because pattern specifically is not supported in most modern browsers
B. False — these attributes only run inside the browser rendering the page and can be bypassed entirely by editing the DOM, disabling JavaScript, or sending requests directly to the server — HTML validation attributes are a client-side, browser-enforced convenience only. Nothing stops a user from disabling JavaScript, altering the HTML with dev tools, or sending a request straight to the server's endpoint with a tool like curl — none of which passes through this HTML at all. The server must always independently re-validate incoming data regardless of what the client-side form declares.
Build a signup form field for a username that is required, between 3 and 15 characters, and only contains letters, numbers, and underscores. Then explain in one sentence why this HTML alone is not sufficient security.
<input type="text" name="username" required minlength="3" maxlength="15" pattern="[A-Za-z0-9_]+"> This HTML only restrains what a normal browser UI submits — a user can bypass it (disabling JS, editing the DOM, or calling the endpoint directly), so the server must independently re-validate the same rules before trusting the data.
Combine required, minlength, maxlength, and a pattern using a character class like [A-Za-z0-9_].