HTML Guides for url
Learn how to identify and fix common HTML validation errors flagged by the W3C Validator — so your pages are standards-compliant and render correctly across every browser. Also check our Accessibility Guides.
The maxlength attribute provides built-in client-side validation that caps the number of characters a user can type into a field. Browsers enforce this by preventing further input once the limit is reached. However, this behavior only makes sense for input types that accept arbitrary text strings. Input types like number, date, range, and checkbox have their own value formats and constraints — a number input's value is controlled by min, max, and step, not by character count.
When you place maxlength on an unsupported input type, browsers will ignore the attribute. This means it provides no actual validation while giving a false sense of security. It also produces invalid HTML, which can cause issues with assistive technologies that may try to interpret the attribute and relay incorrect information to users. Keeping your markup valid ensures predictable behavior across browsers and a better experience for all users.
How to fix it
- Remove
maxlengthfrom any<input>whosetypeis notemail,password,search,tel,text, orurl. - Use the correct constraint attributes for the input type in question. For
numberinputs, useminandmax. Fordateinputs, useminandmaxwith date strings. - If you genuinely need character-length validation, consider whether a text-based input type is more appropriate for your use case, or implement the constraint in JavaScript.
Examples
❌ Invalid: maxlength on a number input
<label for="quantity">Quantity</label>
<input type="number" id="quantity" name="quantity" maxlength="3">
The number type does not support maxlength. Browsers will ignore it, and the HTML is invalid.
✅ Fixed: using min and max for a number input
<label for="quantity">Quantity</label>
<input type="number" id="quantity" name="quantity" min="0" max="999">
If the goal was to limit the value to three digits, min and max are the correct constraints.
❌ Invalid: maxlength on a date input
<label for="start-date">Start date</label>
<input type="date" id="start-date" name="start-date" maxlength="10">
Date inputs have a browser-provided date picker, and their values are always in YYYY-MM-DD format. The maxlength attribute has no effect here.
✅ Fixed: using min and max for a date input
<label for="start-date">Start date</label>
<input type="date" id="start-date" name="start-date" min="2020-01-01" max="2030-12-31">
❌ Invalid: maxlength on a checkbox
<label>
<input type="checkbox" name="agree" maxlength="1"> I agree
</label>
A checkbox is a boolean toggle — character length is meaningless here.
✅ Fixed: removing the invalid attribute
<label>
<input type="checkbox" name="agree"> I agree
</label>
✅ Valid: maxlength on supported text-based types
<label for="username">Username</label>
<input type="text" id="username" name="username" maxlength="30">
<label for="user-email">Email</label>
<input type="email" id="user-email" name="email" maxlength="254">
<label for="user-phone">Phone</label>
<input type="tel" id="user-phone" name="phone" maxlength="15">
<label for="site-url">Website</label>
<input type="url" id="site-url" name="website" maxlength="2048">
<label for="user-pass">Password</label>
<input type="password" id="user-pass" name="password" maxlength="128">
<label for="query">Search</label>
<input type="search" id="query" name="q" maxlength="100">
All six of these input types support maxlength because they accept free-form text where limiting character count is meaningful.
The minlength attribute defines the minimum number of characters (as UTF-16 code units) that a user can enter into a text-based input field. It provides built-in client-side validation without requiring JavaScript. However, it only makes sense on input types where the user is typing free-form text. For input types like number, date, color, range, or checkbox, the concept of a minimum character length doesn't apply — these inputs have their own constrained value formats or use other attributes like min and max for validation.
When the W3C validator encounters minlength on an unsupported input type, it flags the attribute as invalid. Browsers will typically ignore the attribute silently, meaning your intended validation won't actually work. This can lead to a false sense of security where you believe the input is being validated when it isn't.
The minlength value must be a non-negative integer (0 or higher) and must be less than or equal to maxlength if both are specified. The minlength attribute also works on <textarea> elements.
Examples
❌ Incorrect: minlength on a number input
The number input type doesn't support minlength. If you want to enforce a minimum value, use the min attribute instead.
<label for="age">Enter your age</label>
<input type="number" minlength="1" id="age">
✅ Fixed: Using min for number inputs
<label for="age">Enter your age</label>
<input type="number" min="1" id="age">
❌ Incorrect: minlength on a date input
<label for="start-date">Start date</label>
<input type="date" minlength="10" id="start-date">
✅ Fixed: Remove minlength from date inputs
Date inputs have a browser-controlled format, so character length constraints don't apply. Use min and max to constrain the date range.
<label for="start-date">Start date</label>
<input type="date" min="2024-01-01" id="start-date">
✅ Correct: minlength on supported input types
Here are valid uses of minlength across all supported input types:
<label for="username">Username (at least 3 characters)</label>
<input type="text" minlength="3" id="username">
<label for="email">Email</label>
<input type="email" minlength="5" id="email">
<label for="pass">Password (at least 8 characters)</label>
<input type="password" minlength="8" id="pass">
<label for="phone">Phone number</label>
<input type="tel" minlength="7" id="phone">
<label for="query">Search</label>
<input type="search" minlength="2" id="query">
<label for="website">Website URL</label>
<input type="url" minlength="10" id="website">
✅ Correct: minlength with maxlength on a textarea
<label for="bio">Bio (between 10 and 200 characters)</label>
<textarea minlength="10" maxlength="200" id="bio"></textarea>
Quick Reference
| Input Type | Supports minlength? | Alternative |
|---|---|---|
text, email, password, search, tel, url | ✅ Yes | — |
textarea | ✅ Yes | — |
number, range | ❌ No | Use min / max |
date, datetime-local, time, month, week | ❌ No | Use min / max |
checkbox, radio | ❌ No | Use required |
file | ❌ No | Validate with JavaScript |
color, hidden | ❌ No | Not applicable |
The pattern attribute provides a powerful way to add client-side form validation directly in HTML without relying on JavaScript. It accepts a regular expression that the browser uses to validate user input before the form is submitted. However, the HTML specification restricts pattern to input types where the user enters free-form text. Input types like number, date, range, color, and checkbox have their own built-in validation mechanisms (such as min, max, and step), so applying a regex pattern to them is meaningless and invalid.
When you add pattern to an unsupported input type, browsers will simply ignore it. This means you might think you have validation in place when you actually don't, which can lead to unexpected invalid data being submitted. Removing the invalid attribute also keeps your markup clean and standards-compliant, which benefits accessibility tools and future browser behavior.
Why certain types don't support pattern
number— Values are constrained bymin,max, andstep. The browser enforces numeric input natively.date,time,datetime-local,month,week— These use date/time pickers with their own format and range constraints.range— A slider control already constrained bymin,max, andstep.checkbox,radio— These are toggled on/off or selected from a group; a regex pattern doesn't apply.file— File selection is handled by the OS file picker; use theacceptattribute instead.color— Uses a color picker with a fixed hex format.hidden— Not user-editable, so client-side validation is irrelevant.
How to fix it
- Remove the
patternattribute if the input type already provides sufficient validation through its native controls. - Change the input
typeto one of the six supported types (email,password,search,tel,text, orurl) if you genuinely need regex-based validation. - Use alternative attributes like
min,max,step, oracceptthat are designed for the specific input type. - Use JavaScript validation if you need custom validation logic that goes beyond what native attributes offer.
Examples
❌ Incorrect: pattern on a number input
<label for="qty">Quantity (multiples of 5):</label>
<input type="number" id="qty" name="qty" pattern="[0-9]+" min="0" max="100">
The pattern attribute is not allowed on type="number". Since min, max, and step already handle numeric constraints, pattern is unnecessary here.
✅ Correct: using step instead of pattern for number validation
<label for="qty">Quantity (multiples of 5):</label>
<input type="number" id="qty" name="qty" min="0" max="100" step="5">
❌ Incorrect: pattern on a date input
<label for="dob">Date of birth:</label>
<input type="date" id="dob" name="dob" pattern="\d{4}-\d{2}-\d{2}">
The date input type already enforces a date format through its native picker, so pattern is invalid here.
✅ Correct: removing pattern from date input
<label for="dob">Date of birth:</label>
<input type="date" id="dob" name="dob" min="1900-01-01" max="2025-12-31">
❌ Incorrect: pattern on a checkbox input
<label>
<input type="checkbox" name="agree" pattern=".+"> I agree to the terms
</label>
✅ Correct: using required instead of pattern for checkbox
<label>
<input type="checkbox" name="agree" required> I agree to the terms
</label>
✅ Correct: pattern on a supported text input
<label for="zip">ZIP code:</label>
<input type="text" id="zip" name="zip" pattern="[0-9]{5}" title="Five digit ZIP code" required>
When using pattern on a supported input type, always include a title attribute that describes the expected format. Browsers display the title text as part of the validation error message, helping users understand what input is expected.
The required attribute is a boolean attribute that tells the browser a field must be filled in before the form can be submitted. However, not every input type supports this concept. Some input types always have a value (like range, which defaults to a midpoint, or color, which defaults to #000000), while others represent actions rather than user data (like submit, reset, image, and button). For hidden inputs, the user has no way to interact with the field at all, so requiring them to provide a value makes no sense.
The HTML specification explicitly limits required to the following input types: checkbox, date, datetime-local, email, file, month, number, password, radio, search, tel, text, time, url, and week.
Using required on an unsupported type is invalid HTML. Browsers will typically ignore the attribute in this situation, which means you might believe a field is required when it actually isn't being validated at all. This can lead to forms being submitted with missing or unexpected data. It also creates confusion for assistive technologies — screen readers may announce a field as required even though the browser won't enforce it, misleading users.
How to fix it
- Check the input type. If you're using
requiredon an input with a type likehidden,range,color,submit,reset,image, orbutton, the attribute is not allowed. - Remove the
requiredattribute if the input type inherently provides a value or doesn't accept user-provided data. - Change the input type if you actually need the field to be required and the current type doesn't match your intent.
- Use server-side validation for inputs like
hiddenthat can't userequiredbut still need a value.
Examples
❌ Invalid: required on a hidden input
<form>
<input type="hidden" name="token" required>
<button type="submit">Submit</button>
</form>
The user cannot interact with a hidden input, so required is not allowed here. The browser won't enforce it.
❌ Invalid: required on a range input
<form>
<label for="volume">Volume:</label>
<input type="range" id="volume" name="volume" min="0" max="100" required>
<button type="submit">Submit</button>
</form>
A range input always has a value (it defaults to the midpoint), so required is meaningless and not permitted.
❌ Invalid: required on a color input
<form>
<label for="color">Pick a color:</label>
<input type="color" id="color" name="color" required>
<button type="submit">Submit</button>
</form>
A color input always has a value (defaulting to #000000), so required is not valid here.
✅ Valid: required removed from unsupported types
<form>
<input type="hidden" name="token" value="abc123">
<label for="volume">Volume:</label>
<input type="range" id="volume" name="volume" min="0" max="100">
<button type="submit">Submit</button>
</form>
✅ Valid: required on supported input types
<form>
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
<label for="dob">Date of birth:</label>
<input type="date" id="dob" name="dob" required>
<label>
<input type="checkbox" name="terms" required>
I agree to the terms
</label>
<button type="submit">Submit</button>
</form>
These input types — email, date, and checkbox — all accept direct user input and are on the allowed list for the required attribute.
The href value on the <a> element contains a literal > character, which is not allowed in a URL path.
The URL standard does not allow a raw > anywhere in a URL. Writing it as > doesn't change that, because the HTML parser decodes > back to > before the URL is read. Browsers still follow the link, but they percent-encode the character themselves and request a path that usually does not exist.
When the value starts with >, it is often not an address at all. Some sites put a > in front of link text as a small arrow, as in > Read the full report. If an editor pastes that text into the URL field of a CMS instead of the label field, the whole sentence becomes the href, and the browser treats it as a relative path to a missing page. A stray > at the end of a value, like href="/reports/annual>", is usually a typo in the markup.
Put the real address in href and keep the text inside the element. The validator reports only the first illegal character in a value, so if you remove the > but leave the sentence in href, it will report the spaces next. If a path really needs a >, write it as %3E.
Invalid example
<a href="> Read the full report">Read the full report</a>
Valid example
<a href="/reports/annual">Read the full report</a>
The href value on the <a> element contains a literal < character, which is not allowed in a URL path.
The URL standard does not allow a raw < anywhere in a URL. Escaping it as < does not help, because the HTML parser turns < back into < before the URL is read. If a path really needs the character, write it as %3C.
A value that starts with part of a tag, such as <div class=, means a template or content filter wrote HTML where a URL belongs. Lazy-loading and video embed plugins can do this when they swap a video URL for placeholder markup without checking whether the URL sits inside an attribute. The first " in that markup closes the href, so the value stops at class= and the browser treats it as a relative path to a page that doesn't exist.
The rest of the injected tag spills into the <a> start tag as broken attributes. That's why the validator usually also reports a stray end tag and a quote in an attribute name for the same link. Those errors go away once the href holds a plain URL.
Other values with a < come from angle brackets copied along with an address, as in <https://www.example.com/> from a plain-text email, or from placeholders like /files/<name>.pdf left in a template. Remove the brackets or fill in the real value.
Invalid example
<a href="<div class="video-embed" data-src="https://www.example.com/embed/abc123"></div>">Watch the video</a>
Valid example
<a href="https://www.example.com/embed/abc123">Watch the video</a>
The href value on the <a> element contains a literal " character, which is not allowed in a URL path.
The quotes around an attribute value are HTML syntax, not part of the URL. A " inside the value is different: written as ", or typed inside a single-quoted attribute such as href='"/brochure.pdf"', it becomes a character of the URL itself. The URL standard does not allow a raw " in a path, so the validator rejects the whole value. Browsers still follow the link, but they send the quote as %22 and request a path that usually does not exist.
The stray quote rarely comes from someone typing it. More often the code that writes the link adds it. A template that JSON-encodes the URL before printing it, for example with JSON.stringify(url), wraps the address in an extra pair of quotes. A CMS that stores link settings as JSON, like {"url":"/brochure.pdf","target":"_blank"}, can print the wrong piece of that string, so href ends up holding a fragment such as "url":"/brochure.pdf" or the tail of the object instead of the address. Fix the template so it outputs the plain URL. If a file name really contains a double quote, write it as %22 in the path.
Invalid example
<a href=""/brochure.pdf"">Download the brochure</a>
Valid example
<a href="/brochure.pdf">Download the brochure</a>
The rel attribute defines the relationship between the current document and a linked resource. The HTML specification maintains a set of recognized keyword values for this attribute, and the allowed keywords vary depending on which element the attribute appears on. For example, stylesheet is valid on <link> but not on <a>, while nofollow is valid on <a> and <form> but not on <link>.
When the validator encounters a rel value that isn't a recognized keyword, it checks whether the value is a valid absolute URL. This is because the HTML specification allows custom link types to be defined using absolute URLs as identifiers (similar to how XML namespaces work). If the value is neither a recognized keyword nor a valid absolute URL, the validator raises this error.
Common causes of this error include:
- Typos in standard keywords — for example,
rel="styelsheet"orrel="no-follow"instead of the correctrel="stylesheet"orrel="nofollow". - Using non-standard or invented values — such as
rel="custom"orrel="external", which aren't part of the HTML specification's recognized set. - Using relative URLs as custom link types — for example,
rel="my-custom-type"instead of a full URL likerel="https://example.com/my-custom-type".
This matters because browsers and other user agents rely on recognized rel values to determine how to handle linked resources. An unrecognized value will simply be ignored, which could mean your stylesheet doesn't load, your prefetch hint doesn't work, or search engines don't respect your intended link relationship. Using correct values ensures predictable behavior across all browsers and tools.
Examples
Incorrect: Misspelled keyword
<link rel="styleshet" href="main.css">
The validator reports that styleshet is not a recognized keyword and is not an absolute URL.
Correct: Fixed spelling
<link rel="stylesheet" href="main.css">
Incorrect: Non-standard keyword on an anchor
<a href="https://example.com" rel="external">Visit Example</a>
The value external is not a standard rel keyword in the HTML specification, so the validator flags it.
Correct: Using a recognized keyword
<a href="https://example.com" rel="noopener">Visit Example</a>
Incorrect: Relative URL as a custom link type
<link rel="my-custom-rel" href="data.json">
Correct: Absolute URL as a custom link type
If you genuinely need a custom relationship type, provide a full absolute URL:
<link rel="https://example.com/rels/my-custom-rel" href="data.json">
Correct: Common valid rel values
Here are some frequently used standard rel keywords with their appropriate elements:
<!-- Linking a stylesheet -->
<link rel="stylesheet" href="styles.css">
<!-- Linking a favicon -->
<link rel="icon" href="favicon.ico">
<!-- Preloading a resource -->
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
<!-- Telling search engines not to follow a link -->
<a href="https://example.com" rel="nofollow">Sponsored link</a>
<!-- Opening a link safely in a new tab -->
<a href="https://example.com" target="_blank" rel="noopener noreferrer">External site</a>
Multiple rel values
You can specify multiple space-separated rel values. Each one must individually be either a recognized keyword or a valid absolute URL:
<!-- Correct: both values are recognized keywords -->
<a href="https://example.com" target="_blank" rel="noopener noreferrer">External</a>
<!-- Incorrect: "popup" is not a recognized keyword or absolute URL -->
<a href="https://example.com" target="_blank" rel="noopener popup">External</a>
To resolve this error, consult the MDN rel attribute reference for the full list of recognized keywords and which elements support them. If your value isn't on the list, either replace it with the correct standard keyword or use a complete absolute URL to define your custom link type.
A space in the src URL of a <video> element is an illegal character, so write each space as %20 or rename the file without spaces.
The src attribute takes a valid URL, as defined by the URL Living Standard. Each folder name in the path is a path segment, and so is the file name. None of them can contain a literal space, so the validator flags my videos/demo.mp4 just as it flags videos/my demo.mp4.
Files uploaded through a CMS or copied from a desktop folder often keep the spaces in their names. Most browsers encode the space themselves and play the video anyway, but the markup is still invalid, and a strict URL parser may cut the address off at the first space.
The cleanest fix is to rename the file with hyphens or underscores and update src to match. If you can't rename it, replace each space with %20. The server decodes %20 back to a space, so it still finds the original file. The same rule applies to the src of a <source> element and to the poster attribute of <video>.
Invalid example
<video src="videos/product demo.mp4" controls></video>
Valid examples
<video src="videos/product%20demo.mp4" controls></video>
<video src="videos/product-demo.mp4" controls></video>
The first image URL in the srcset attribute starts with a literal " character, which is not allowed in a URL path.
srcset holds a comma-separated list of image candidates. Each one is a URL, optionally followed by a width descriptor such as 600w or a pixel density descriptor such as 2x. The quotes around an attribute value are HTML syntax, not part of the value. When an extra " ends up inside the value, it becomes the first character of the first URL. A raw " is not allowed in a URL path segment, so the validator rejects that candidate. Browsers read "https://example.com/logo.png as a relative path, because a URL scheme has to start with a letter, and request a file on your own site that does not exist.
The stray quote usually comes from one of two mistakes. In the first, the quotes around the value were HTML-encoded as ", often by a plugin or template that escaped markup a second time. " can't delimit an attribute, so the parser reads srcset="logo-600.png as an unquoted value that stops at the first space. The rest of the list turns into stray attributes such as 600w,, which the validator reports separately, and the bad value in this message is only the first URL. In the second, the value is wrapped in two sets of quotes, as in srcset='"logo-600.png 600w"', and the whole list is the bad value.
The cause rarely affects srcset alone. If width, height, sizes or decoding on the same <img> also report a stray ", fix the code that writes the tag instead of editing each attribute.
Invalid example
<img
src="logo-600.png"
alt="Company logo"
srcset="logo-600.png 600w, logo-1200.png 1200w"
sizes="(max-width: 600px) 100vw, 600px">
Valid example
<img
src="logo-600.png"
alt="Company logo"
srcset="logo-600.png 600w, logo-1200.png 1200w"
sizes="(max-width: 600px) 100vw, 600px">
The url() in a background value starts with a scheme the CSS validator doesn't recognize, so it can't read the value as a URL.
The validator accepts relative paths such as images/arrow.png or /images/arrow.png, and absolute URLs with common schemes such as https: and data:. It treats everything before the first colon as the scheme, and if it doesn't know that scheme, it reports the whole url() as incorrect. Quotes don't change this: url("myapp://icons/arrow.png") gets the same error.
The usual cause is a placeholder that a build tool or CMS was supposed to replace with a real path, such as url(myapp://icons/arrow.png). A browser can't resolve that scheme either, so the image never loads. Local file paths cause it too: in url(C:/images/arrow.png), the validator takes the drive letter C as the scheme, and visitors' browsers couldn't open a file on your disk anyway.
Look at the CSS the browser receives, not the template. Point url() at the file's real location with a relative path or a full https:// URL, and fix the step that should have replaced the placeholder.
Invalid example
<div style="background: #f5f5f5 url(myapp://icons/arrow.png) no-repeat right center;">
<p>Next page</p>
</div>
Valid example
<div style="background: #f5f5f5 url(/images/arrow.png) no-repeat right center;">
<p>Next page</p>
</div>
Validate at scale.
Ship accessible websites, faster.
Automated HTML & accessibility validation for large sites. Check thousands of pages against WCAG guidelines and W3C standards in minutes, not days.
1K Weekly
1,000 monthly checks. Cancel anytime.
Get 1K Weekly →Join teams across 40+ countries