HTML Guides for bad value
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.
Square brackets ([ and ]) are not valid characters in a URL path and must be percent-encoded.
The HTML specification requires that the href attribute on an <a> element contains a valid URL. According to RFC 3986, square brackets are reserved characters that are only permitted in the host component of a URI (specifically for IPv6 addresses like [::1]). When they appear in the path, query, or fragment components, they must be percent-encoded as %5B for [ and %5D for ].
This commonly happens with URLs that contain array-like query parameters (e.g., filter[status]=active) or paths that include brackets for some framework-specific routing convention.
To fix the issue, replace every [ with %5B and every ] with %5D in the URL path or query string.
HTML examples
Invalid: square brackets in the URL
<ahref="/products?filter[color]=red&filter[size]=large">
Red, large products
</a>
Valid: percent-encoded square brackets
<ahref="/products?filter%5Bcolor%5D=red&filter%5Bsize%5D=large">
Red, large products
</a>
The encoded version is functionally equivalent. Most web servers and frameworks will decode %5B and %5D back to [ and ] automatically when processing the request.
Square brackets ([ and ]) are not valid characters in URLs according to RFC 3986 and must be percent-encoded in the href attribute of <a> elements.
URLs have a strict set of allowed characters. Square brackets are reserved exclusively for IPv6 addresses in the host portion of a URL (e.g., http://[::1]/). Anywhere else in a URL, including query strings, they must be percent-encoded: [ becomes %5B and ] becomes %5D.
This issue commonly appears when linking to URLs that use square brackets in query parameters, a pattern found in some PHP frameworks and APIs (e.g., ?filter[name]=value). While most browsers handle unencoded brackets without problems, the HTML specification requires valid URLs, and the W3C validator flags the violation.
Invalid example
<ahref="https://example.com/results?filter[status]=active&filter[role]=admin">
View results
</a>
Valid example
Replace [ with %5B and ] with %5D:
<ahref="https://example.com/results?filter%5Bstatus%5D=active&filter%5Brole%5D=admin">
View results
</a>
The browser will decode the percent-encoded characters when sending the request, so the server receives the same query string either way.
The | (pipe) character is not allowed in URLs and must be percent-encoded as %7C in the href attribute of an <a> element.
URLs follow the syntax rules defined in RFC 3986. The pipe character is not among the allowed characters in the query component of a URI. When a URL contains a literal |, browsers will often handle it gracefully, but the HTML is technically invalid. The fix is to replace every | with its percent-encoded equivalent %7C.
This commonly appears when linking to external APIs, legacy systems, or content management platforms that use | as a delimiter in query strings.
HTML examples
Invalid example
<ahref="https://example.com/search?filters=color|size|price">Search</a>
Valid example
<ahref="https://example.com/search?filters=color%7Csize%7Cprice">Search</a>
If the URLs are generated dynamically in JavaScript, encodeURI() or encodeURIComponent() will handle the encoding automatically:
constfilters="color|size|price";
consturl=`https://example.com/search?filters=${encodeURIComponent(filters)}`;
// Result: https://example.com/search?filters=color%7Csize%7Cprice
A URL in an href attribute contains a character that must be percent-encoded before it can appear in a query string.
URLs follow the syntax rules defined in RFC 3986. The query component of a URL (everything after the ?) allows a specific set of characters: unreserved characters (A-Z, a-z, 0-9, -, ., _, ~), sub-delimiters (!, $, &, ', (, ), *, +, ,, ;, =), plus :, @, /, and ?. Characters outside this set, such as {, }, |, ^, [, ], spaces, and non-ASCII characters like é or ñ, are illegal in their raw form and must be percent-encoded.
Percent-encoding replaces each disallowed byte with a % followed by its two-digit hexadecimal value. For example, a space becomes %20, a pipe | becomes %7C, and { becomes %7B.
Common causes of this error include:
- Pasting a URL directly from a browser address bar or CMS that displays decoded characters.
- Using curly braces for template placeholders in URLs without encoding them.
- Including spaces or non-ASCII characters in query parameter values.
HTML examples
Invalid: raw illegal characters in the query string
<ahref="https://example.com/search?q=hello world&cat=sci|tech">Search</a>
The space between hello and world and the pipe | between sci and tech are not allowed in a URL query.
Valid: percent-encoded characters
<ahref="https://example.com/search?q=hello%20world&cat=sci%7Ctech">Search</a>
Each illegal character is replaced with its percent-encoded equivalent: space is %20 and | is %7C.
The URL standard (defined by WHATWG) specifies a strict set of characters allowed in each part of a URL. A space character is not among them. When the validator encounters a literal space in an href value, it reports the error "Illegal character in scheme data: space is not allowed." This applies to spaces anywhere in the URL — the path, query string, fragment, or even after the scheme (e.g., https:).
While most modern browsers are forgiving and will attempt to fix malformed URLs by encoding spaces automatically, relying on this behavior is problematic for several reasons:
- Standards compliance: Invalid URLs violate the HTML specification, and markup that depends on browser error-correction is fragile and unpredictable.
- Accessibility: Assistive technologies, such as screen readers, may not handle malformed URLs the same way browsers do. This can result in broken links for users relying on these tools.
- Interoperability: Non-browser consumers of your HTML — search engine crawlers, link checkers, email clients, RSS readers, and APIs — may not perform the same auto-correction, leading to broken links or missed content.
- Copy-paste and sharing: When users copy a malformed URL from the source, the space can cause the link to break when pasted into other applications.
How to fix it
The fix depends on where the space appears:
- In the path or fragment: Replace each space with
%20. For example,/my file.htmlbecomes/my%20file.html. - In the query string: You can use
%20or, if the value is part ofapplication/x-www-form-urlencodeddata,+is also acceptable for spaces within query parameter values. However,%20is universally safe. - Programmatically: Use
encodeURI()in JavaScript to encode a full URL (it preserves structural characters like/,?, and#). UseencodeURIComponent()to encode individual query parameter values. On the server side, use your language's equivalent URL-encoding function.
If you're writing URLs by hand in HTML, simply find every space and replace it with %20. If URLs are generated dynamically (from a database, CMS, or user input), ensure your templating or server-side code encodes them before inserting into the markup.
Examples
Invalid — space in the path
<ahref="https://example.com/docs/My Report.pdf">Download Report</a>
The literal space between "My" and "Report" triggers the validator error.
Fixed — space encoded as %20
<ahref="https://example.com/docs/My%20Report.pdf">Download Report</a>
Invalid — space in a query parameter
<ahref="https://example.com/search?q=hello world">Search</a>
Fixed — space encoded in the query string
<ahref="https://example.com/search?q=hello%20world">Search</a>
Invalid — multiple spaces in different URL parts
<ahref="https://example.com/my folder/page two.html?ref=some value#my section">Link</a>
Fixed — all spaces encoded
<ahref="https://example.com/my%20folder/page%20two.html?ref=some%20value#my%20section">Link</a>
Encoding URLs with JavaScript
If you're building URLs dynamically, use the built-in encoding functions rather than doing manual string replacement:
<script>
// encodeURI encodes a full URL but preserves :, /, ?, #, etc.
consturl=encodeURI("https://example.com/docs/My Report.pdf");
// Result: "https://example.com/docs/My%20Report.pdf"
// encodeURIComponent encodes a single value (for query params)
constquery=encodeURIComponent("hello world");
// Result: "hello%20world"
</script>
Note that encodeURI() is appropriate for encoding a complete URL, while encodeURIComponent() should be used for individual components like query parameter values — it encodes characters such as / and ? that have structural meaning in a URL.
The URL in this href has a slash or backslash where the parser did not expect one, usually too many slashes after the scheme or a backslash standing in for the // separator.
After a scheme like https: the URL syntax expects exactly two forward slashes before the host: https://example.com. Adding a third slash (https:///example.com) leaves an empty host, and writing the separator with backslashes (https:\\example.com) uses a character the URL grammar does not allow there. In both cases the validator reports an unexpected slash or backslash.
This often comes from string concatenation that joins a base URL already ending in / with a path that also starts with /, or from copying a path that used backslashes. Browsers may paper over some of these, but the link can still resolve to the wrong place or fail in non-browser clients.
Invalid example
<ahref="https:///example.com/page">Visit the page</a>
Valid example
<ahref="https://example.com/page">Visit the page</a>
Keep exactly two forward slashes after the scheme, and use forward slashes throughout the URL.
In the structure of a URL, the @ symbol has a special meaning: it separates the userinfo component (username and password) from the host. A URL with credentials follows this pattern:
scheme://username:password@hostname/path
When the username or password itself contains an @ character — for example, an email address used as a username — the browser or URL parser may not be able to determine where the credentials end and the hostname begins. For instance, in http://user@name:pass@example.com, it's unclear whether the host is name or example.com.
The URL Standard (maintained by WHATWG) requires that any @ appearing within the userinfo component be percent-encoded as %40. Percent-encoding replaces the literal character with a % followed by its hexadecimal ASCII code (40 for @). This removes the ambiguity and ensures all parsers interpret the URL identically.
While modern browsers may attempt to handle ambiguous URLs, the behavior is not guaranteed to be consistent across all user agents, link checkers, or HTTP clients. Properly encoding these characters ensures reliable behavior everywhere and keeps your HTML valid.
Note: Including credentials directly in URLs is generally discouraged for security reasons, as they may be exposed in browser history, server logs, and referrer headers. Consider alternative authentication methods when possible.
Examples
❌ Incorrect: unencoded @ in the username
<ahref="http://user@name:password@example.com/path">Login</a>
Here, the parser cannot reliably distinguish user@name as the username from the @ that separates credentials from the host.
✅ Correct: percent-encoded @ in the username
<ahref="http://user%40name:password@example.com/path">Login</a>
The @ within the username is encoded as %40, leaving only one literal @ to serve as the delimiter before the hostname.
❌ Incorrect: unencoded @ in the password
<ahref="http://admin:p@ss@example.com/dashboard">Dashboard</a>
✅ Correct: percent-encoded @ in the password
<ahref="http://admin:p%40ss@example.com/dashboard">Dashboard</a>
❌ Incorrect: email address used as username without encoding
<ahref="ftp://joe@example.org:secret@ftp.example.com/files">Files</a>
✅ Correct: email address with @ percent-encoded
<ahref="ftp://joe%40example.org:secret@ftp.example.com/files">Files</a>
To fix this issue, identify every @ character that appears before the final @ in the authority section of the URL and replace it with %40. The last @ in the authority is the actual delimiter and must remain as a literal character.
The <area> element defines a clickable region within an <map> element, which is used with images to create image maps. When an <area> element includes an href attribute, the browser treats it as a hyperlink. The value of href must be a valid URL, and http:// alone fails validation because the URL specification requires a host after the :// separator. An empty host is not permitted.
This matters for several reasons. Browsers may handle malformed URLs unpredictably — some might ignore the link, others might attempt navigation to a nonsensical destination, and others might throw a network error. Screen readers and other assistive technologies rely on valid href values to announce links correctly and provide meaningful navigation to users. From a standards compliance perspective, the WHATWG URL Standard explicitly rejects URLs with empty hosts, and the W3C validator flags this as an error.
This issue commonly appears when placeholder URLs are left in during development, when CMS tools generate incomplete markup, or when a URL value is dynamically constructed but the host portion ends up empty.
How to fix it
- If the area should link somewhere, replace
http://with the full, intended URL including the host (e.g.,https://example.com/page). - If the area is a temporary placeholder, use
href="#"to create a valid no-op link, though be aware this will scroll the page to the top when clicked. - If the area shouldn't be interactive, remove the
hrefattribute entirely. Withouthref, the<area>element is not a hyperlink and won't be clickable.
Examples
Invalid: empty host in URL
The value http:// has no host, which triggers the validation error.
<imgsrc="diagram.png"alt="Site map"usemap="#siteMap">
<mapname="siteMap">
<areashape="rect"coords="30,23,183,191"href="http://"alt="Home page">
</map>
Fixed: complete URL with a valid host
Providing a full URL with a host resolves the error.
<imgsrc="diagram.png"alt="Site map"usemap="#siteMap">
<mapname="siteMap">
<areashape="rect"coords="30,23,183,191"href="https://example.com/"alt="Home page">
</map>
Fixed: placeholder link with #
If you need the area to remain clickable but don't have a destination yet, href="#" is a valid placeholder.
<imgsrc="diagram.png"alt="Site map"usemap="#siteMap">
<mapname="siteMap">
<areashape="rect"coords="30,23,183,191"href="#"alt="Home page">
</map>
Fixed: non-interactive area without href
If the area doesn't need to be a link, simply omit the href attribute.
<imgsrc="diagram.png"alt="Site map"usemap="#siteMap">
<mapname="siteMap">
<areashape="rect"coords="30,23,183,191"alt="Home page">
</map>
The <pattern> element lives inside <svg>, and SVG is an XML-based language. Unlike regular HTML — where id values follow relatively relaxed rules — SVG content must comply with XML 1.0 naming conventions. This means id values have stricter character and formatting requirements than you might be used to in plain HTML.
XML 1.0 Name Rules
An XML 1.0 name (used for id attributes in SVG) must follow these rules:
- First character must be a letter (
A–Z,a–z) or an underscore (_). - Subsequent characters can be letters, digits (
0–9), hyphens (-), underscores (_), or periods (.). - Spaces and special characters like
!,@,#,$,%,(,), etc. are not allowed anywhere in the name.
Common mistakes that trigger this error include starting an id with a digit (e.g., 1pattern), a hyphen (e.g., -myPattern), or a period (e.g., .dotPattern), or including characters like spaces or colons.
Why This Matters
- Standards compliance: SVG is parsed as XML in many contexts. An invalid XML name can cause parsing errors or unexpected behavior, especially when SVG is served with an XML MIME type or embedded in XHTML.
- Functionality: The
<pattern>element'sidis typically referenced viaurl(#id)infillorstrokeattributes. An invalididmay cause the pattern reference to silently fail, leaving elements unfilled or invisible. - Cross-browser consistency: While some browsers are lenient with invalid XML names, others are not. Using valid names ensures consistent rendering across all browsers and environments.
How to Fix
Rename the id value so it starts with a letter or underscore and contains only valid characters. If you reference this id elsewhere (e.g., in fill="url(#...)" or in CSS), update those references to match.
Examples
❌ Invalid: id starts with a digit
<svgwidth="200"height="200"xmlns="http://www.w3.org/2000/svg">
<defs>
<patternid="1stPattern"width="10"height="10"patternUnits="userSpaceOnUse">
<circlecx="5"cy="5"r="3"fill="blue"/>
</pattern>
</defs>
<rectwidth="200"height="200"fill="url(#1stPattern)"/>
</svg>
❌ Invalid: id starts with a hyphen
<svgwidth="200"height="200"xmlns="http://www.w3.org/2000/svg">
<defs>
<patternid="-stripe-bg"width="10"height="10"patternUnits="userSpaceOnUse">
<rectwidth="5"height="10"fill="red"/>
</pattern>
</defs>
<rectwidth="200"height="200"fill="url(#-stripe-bg)"/>
</svg>
❌ Invalid: id contains special characters
<svgwidth="200"height="200"xmlns="http://www.w3.org/2000/svg">
<defs>
<patternid="my pattern!"width="10"height="10"patternUnits="userSpaceOnUse">
<circlecx="5"cy="5"r="3"fill="green"/>
</pattern>
</defs>
<rectwidth="200"height="200"fill="url(#my pattern!)"/>
</svg>
✅ Valid: id starts with a letter
<svgwidth="200"height="200"xmlns="http://www.w3.org/2000/svg">
<defs>
<patternid="firstPattern"width="10"height="10"patternUnits="userSpaceOnUse">
<circlecx="5"cy="5"r="3"fill="blue"/>
</pattern>
</defs>
<rectwidth="200"height="200"fill="url(#firstPattern)"/>
</svg>
✅ Valid: id starts with an underscore
<svgwidth="200"height="200"xmlns="http://www.w3.org/2000/svg">
<defs>
<patternid="_stripe-bg"width="10"height="10"patternUnits="userSpaceOnUse">
<rectwidth="5"height="10"fill="red"/>
</pattern>
</defs>
<rectwidth="200"height="200"fill="url(#_stripe-bg)"/>
</svg>
✅ Valid: Using letters, digits, hyphens, and underscores
<svgwidth="200"height="200"xmlns="http://www.w3.org/2000/svg">
<defs>
<patternid="dot-grid_v2"width="10"height="10"patternUnits="userSpaceOnUse">
<circlecx="5"cy="5"r="3"fill="green"/>
</pattern>
</defs>
<rectwidth="200"height="200"fill="url(#dot-grid_v2)"/>
</svg>
Note that this same XML 1.0 naming rule applies to id attributes on all SVG elements — not just <pattern>. If you see similar errors on elements like <linearGradient>, <clipPath>, or <filter>, the same fix applies: ensure the id starts with a letter or underscore and uses only valid characters.
The media attribute on a <link> element specifies the conditions under which the linked resource should apply. It accepts either a simple media type or a full media query. When the validator reports "unrecognized media," it means the value you provided doesn't match any known media type or valid media query syntax.
Several older media types that were defined in earlier CSS specifications have been deprecated. Types like handheld, projection, tv, tty, aural, braille, and embossed are no longer recognized as valid. Modern CSS and HTML only support three media types: all, screen, and print. If you're using a deprecated type, you should replace it with an appropriate modern media query that targets the device characteristics you need.
Beyond deprecated types, this error also occurs when a media query expression is malformed — for example, missing parentheses around a feature expression, using an unknown feature name, or having a typo in the value.
Why this matters
- Standards compliance: Using unrecognized media types means your HTML doesn't conform to the current HTML and CSS specifications.
- Browser behavior: Browsers may ignore the entire
<link>element or apply the resource unconditionally when they encounter an unrecognized media type, leading to unexpected results. - Performance: The
mediaattribute helps browsers prioritize resource loading. A valid media query allows the browser to defer loading stylesheets that don't match the current context (e.g., print stylesheets), improving page load performance.
How to fix it
- Replace deprecated media types with
screen,print, orall, or use modern media queries that target specific device features. - Check for typos in your media type or query expression.
- Validate your media query syntax — feature expressions must be wrapped in parentheses and use recognized feature names like
max-width,orientation, orprefers-color-scheme.
Examples
Incorrect: using a deprecated media type
<linkrel="stylesheet"href="mobile.css"media="handheld">
The handheld media type is deprecated and will trigger the validation error.
Incorrect: misspelled media type
<linkrel="stylesheet"href="styles.css"media="screen">
Incorrect: malformed media query
<linkrel="stylesheet"href="responsive.css"media="max-width: 768px">
The feature expression is missing its surrounding parentheses.
Correct: using valid media types
<linkrel="stylesheet"href="general.css">
<linkrel="stylesheet"href="print.css"media="print">
<linkrel="stylesheet"href="screen.css"media="screen">
When no media attribute is specified, it defaults to all.
Correct: replacing deprecated types with modern media queries
Instead of media="handheld", use a media query that targets small screens or specific device capabilities:
<linkrel="stylesheet"href="mobile.css"media="screen and (max-width: 768px)">
Correct: using complex media queries
<linkrel="stylesheet"href="dark.css"media="(prefers-color-scheme: dark)">
<linkrel="stylesheet"href="portrait.css"media="screen and (orientation: portrait)">
<linkrel="stylesheet"href="large.css"media="screen and (min-width: 1200px)">
Valid media types reference
| Media type | Description |
|---|---|
all | Matches all devices (default when omitted) |
print | Matches printers and print preview mode |
screen | Matches screens (computers, tablets, phones) |
For anything more specific than these three types, use media feature expressions like (max-width: 600px), (hover: hover), or (prefers-reduced-motion: reduce) to target the exact device characteristics you need.
The multiple attribute tells the browser that the user can supply more than one value for a given input. For <input type="file">, it allows selecting multiple files at once. For <input type="email">, it allows entering a comma-separated list of email addresses. These are the only two input types that support the multiple attribute according to the HTML specification.
This validation error can appear for two distinct reasons, and sometimes both at once:
An invalid value is assigned to the boolean attribute. Boolean attributes in HTML follow strict rules. Valid syntaxes are: the attribute name alone (
multiple), an empty string value (multiple=""), or the attribute's own name as the value (multiple="multiple"). Any other value — includingmultiple="true",multiple="1", ormultiple="yes"— is invalid.The attribute is used on an unsupported input type. Placing
multipleon input types liketext,number,password, orurlis not valid because those types don't define behavior for this attribute. Browsers will simply ignore it, but it still constitutes invalid markup.
Why this matters
- Standards compliance: Invalid boolean attribute values violate the WHATWG HTML specification. While most browsers are forgiving and may still interpret
multiple="true"as the attribute being present, relying on this behavior is fragile and non-standard. - Accessibility: Assistive technologies rely on valid, well-structured markup. An invalid attribute value could lead to unpredictable behavior in screen readers or other tools.
- Maintainability: Using
multipleon an unsupported input type suggests a misunderstanding of the element's capabilities, which can confuse other developers and lead to bugs.
How to fix it
- Remove the value entirely: Change
multiple="1"ormultiple="true"to justmultiple. - Use a valid boolean syntax if a value is required: Some templating systems or XML-based contexts (like XHTML) require explicit attribute values. In those cases, use
multiple=""ormultiple="multiple". - Ensure the input type supports
multiple: Onlytype="email"andtype="file"accept this attribute. If you need multi-value input for other types, consider alternative approaches like multiple separate inputs, a<select multiple>element, or a JavaScript-based solution.
Examples
Invalid: wrong value on a boolean attribute
<!-- Bad: "1" is not a valid boolean attribute value -->
<inputtype="file"name="attachments"multiple="1">
<!-- Bad: "true" is not a valid boolean attribute value -->
<inputtype="email"name="recipients"multiple="true">
Invalid: multiple on an unsupported input type
<!-- Bad: type="text" does not support the multiple attribute -->
<inputtype="text"name="tags"multiple>
<!-- Bad: type="number" does not support the multiple attribute -->
<inputtype="number"name="quantities"multiple>
Valid: correct usage of multiple
<!-- Correct: boolean attribute with no value -->
<inputtype="file"name="attachments"multiple>
<!-- Correct: empty string value (valid boolean syntax) -->
<inputtype="email"name="recipients"multiple="">
<!-- Correct: attribute name as value (valid for XHTML compatibility) -->
<inputtype="file"name="documents"multiple="multiple">
Full corrected document
<!DOCTYPE html>
<htmllang="en">
<head>
<title>Upload Form</title>
</head>
<body>
<formaction="/submit"method="post"enctype="multipart/form-data">
<labelfor="recipients">Recipients:</label>
<inputtype="email"id="recipients"name="recipients"multipleplaceholder="a@example.com, b@example.com">
<labelfor="files">Attachments:</label>
<inputtype="file"id="files"name="files"multiple>
<buttontype="submit">Send</button>
</form>
</body>
</html>
In HTML, the name attribute on an <iframe> defines a browsing context name. This name can be referenced by other elements — for example, a link with target="my-frame" will open its URL inside the <iframe> whose name is "my-frame". The HTML specification reserves all browsing context names that start with an underscore for special keywords:
_self— the current browsing context_blank— a new browsing context_parent— the parent browsing context_top— the topmost browsing context
Because the underscore prefix is reserved for these (and potentially future) keywords, the spec requires that any custom browsing context name must not begin with _. Setting name="_example" or name="_myFrame" on an <iframe> is invalid HTML, even though those exact strings aren't currently defined keywords. Browsers may handle these inconsistently — some might ignore the name entirely, while others could treat it as one of the reserved keywords, leading to unexpected navigation behavior.
This matters for several reasons:
- Standards compliance: The WHATWG HTML living standard explicitly states that a valid browsing context name must not start with an underscore character.
- Predictable behavior: Using a reserved prefix can cause links or forms targeting that
<iframe>to navigate in unintended ways (e.g., opening in a new tab instead of within the frame). - Future-proofing: New underscore-prefixed keywords could be added to the spec, which might break pages that use custom names starting with
_.
To fix the issue, simply rename the name attribute value so it doesn't start with an underscore. You can use underscores elsewhere in the name — just not as the first character.
Examples
❌ Invalid: name starts with an underscore
<iframesrc="https://example.com"name="_example"></iframe>
<ahref="https://example.com/page"target="_example">Open in frame</a>
The name _example starts with an underscore, which makes it invalid. A browser might interpret _example unpredictably or ignore the name entirely when used as a target.
✅ Fixed: underscore removed from the start
<iframesrc="https://example.com"name="example"></iframe>
<ahref="https://example.com/page"target="example">Open in frame</a>
✅ Fixed: underscore used elsewhere in the name
<iframesrc="https://example.com"name="my_example"></iframe>
<ahref="https://example.com/page"target="my_example">Open in frame</a>
Underscores are perfectly fine as long as they aren't the first character.
❌ Invalid: using a reserved keyword as a frame name
<iframesrc="https://example.com"name="_blank"></iframe>
Using _blank as an <iframe> name is also invalid because it's a reserved browsing context keyword. A link targeting _blank would open in a new window/tab rather than inside this <iframe>, which is almost certainly not what you intended.
✅ Fixed: descriptive name without underscore prefix
<iframesrc="https://example.com"name="content-frame"></iframe>
<ahref="https://example.com/page"target="content-frame">Open in frame</a>
How to fix
- Find every
<iframe>element whosenameattribute starts with_. - Rename each one by removing the leading underscore or replacing it with a letter or other valid character.
- Update any
targetattributes on<a>,<form>, or<base>elements that reference the old name so they match the new name. - Re-validate your HTML to confirm the issue is resolved.
Understanding Boolean Attributes in HTML
In HTML, boolean attributes work differently from regular attributes. A boolean attribute's presence on an element represents true, and its absence represents false. According to the HTML specification, a boolean attribute may only have three valid forms:
- The attribute name alone:
novalidate - The attribute with an empty value:
novalidate="" - The attribute with a value matching its own name (case-insensitive):
novalidate="novalidate"
Any other value — such as "true", "false", "1", "0", or "yes" — is invalid and triggers this validation error. This is a common source of confusion, especially for developers coming from frameworks like React (which uses noValidate={true}) or from languages where boolean attributes accept explicit true/false strings.
Why This Matters
- Standards compliance: Using invalid values violates the HTML specification and will cause W3C validation failures.
- Unexpected behavior: While most browsers are lenient and treat any value of
novalidateas "present" (meaning evennovalidate="false"would disable validation, not enable it), relying on this behavior is unreliable and misleading to other developers reading your code. - Maintainability: Writing
novalidate="false"suggests the form should be validated, but the opposite is true — the attribute is present, so validation is skipped. This creates confusing, error-prone code.
About the novalidate Attribute
The novalidate attribute tells the browser to skip its built-in constraint validation when the form is submitted. Without it, the browser checks required fields, input patterns, email formats, and other constraints before allowing submission.
If novalidate is not set on the form, individual submit buttons can still bypass validation on a per-button basis using the formnovalidate attribute on a <button>, <input type="submit">, or <input type="image"> element.
Examples
❌ Invalid: Arbitrary value on novalidate
<formmethod="post"novalidate="true">
<label>Email:
<inputtype="email"name="email"required>
</label>
<button>Submit</button>
</form>
This triggers the error because "true" is not a valid value for a boolean attribute. Other invalid variations include:
<formmethod="post"novalidate="1">
<formmethod="post"novalidate="yes">
<formmethod="post"novalidate="false">
Note that novalidate="false" is especially dangerous — it does not enable validation. Because the attribute is present, the browser disables validation regardless of the value.
✅ Valid: Attribute name alone (recommended)
<formmethod="post"novalidate>
<label>Email:
<inputtype="email"name="email"required>
</label>
<button>Submit</button>
</form>
✅ Valid: Empty string value
<formmethod="post"novalidate="">
<label>Email:
<inputtype="email"name="email"required>
</label>
<button>Submit</button>
</form>
✅ Valid: Value matching the attribute name
<formmethod="post"novalidate="novalidate">
<label>Email:
<inputtype="email"name="email"required>
</label>
<button>Submit</button>
</form>
✅ Valid: Removing the attribute to enable validation
If you want the form to be validated, simply remove the novalidate attribute entirely:
<formmethod="post">
<label>Email:
<inputtype="email"name="email"required>
</label>
<button>Submit</button>
</form>
✅ Using formnovalidate on a specific button
If you want validation on the primary submit but want to skip it for a "Save Draft" button, use formnovalidate on the button instead:
<formmethod="post">
<label>Email:
<inputtype="email"name="email"required>
</label>
<button>Submit</button>
<buttonformnovalidate>Save Draft</button>
</form>
This pattern keeps validation active for the main submission while allowing drafts to bypass it — without needing novalidate on the form at all.
The ping attribute specifies a space-separated list of URLs that the browser should notify (via a small POST request) when a user follows a hyperlink. This is commonly used for click tracking and analytics. According to the HTML specification, every URL in the list must be a valid, non-empty URL that uses either the http or https scheme — no other schemes or relative paths are permitted.
This restriction exists for practical and security reasons. The ping mechanism is specifically designed for web-based tracking endpoints, so only web protocols make sense. Relative URLs are disallowed because the ping is sent as a separate request independent of normal navigation, and the specification requires absolute URLs to unambiguously identify the target server. Using invalid values won't produce the intended tracking behavior and will cause the browser to silently ignore the ping attribute entirely.
From an accessibility and standards compliance standpoint, ensuring valid ping values means your analytics will work reliably in browsers that support the attribute. Note that browser support varies — some browsers (notably Firefox) disable ping by default or hide it behind a preference — so you should not rely on it as your sole tracking mechanism.
How to fix it
- Replace relative URLs with absolute URLs. If you have a value like
/trackortrack.php, prepend the full origin (e.g.,https://example.com/track). - Remove non-HTTP schemes. Values like
mailto:someone@example.comorftp://example.com/logare not valid forping. - Ensure each URL in the list is properly formatted. Multiple URLs must be separated by spaces (not commas or semicolons), and each one must be a complete
httporhttpsURL.
Examples
Incorrect: relative URL
<ahref="https://example.com"ping="/track">Visit Example</a>
The value /track is a relative URL, which is not allowed in the ping attribute.
Incorrect: unsupported scheme
<ahref="https://example.com"ping="ftp://example.com/log">Visit Example</a>
The ftp: scheme is not permitted — only http and https are valid.
Incorrect: comma-separated URLs
<ahref="https://example.com"ping="https://example.com/track, https://analytics.example.com/log">Visit Example</a>
Multiple URLs must be space-separated, not comma-separated. The commas make each URL invalid.
Correct: single absolute URL
<ahref="https://example.com"ping="https://example.com/track">Visit Example</a>
Correct: multiple space-separated absolute URLs
<ahref="https://example.com"ping="https://example.com/track https://analytics.example.com/log">Visit Example</a>
Each URL is a fully qualified https URL, and they are separated by a single space. Both will receive a POST request when the link is clicked (in browsers that support the ping attribute).
The poster attribute specifies an image to display as a placeholder while the video is loading or before the user starts playback. Like all HTML attributes that accept URLs — such as src, href, and action — the value must conform to valid URI syntax as defined by RFC 3986. In this standard, a literal space character is not a legal character in any part of a URL. When the validator encounters a space in the poster attribute's value, it flags it as an illegal character in the path segment.
While most modern browsers are forgiving and will attempt to resolve URLs containing raw spaces by internally encoding them, relying on this behavior is problematic for several reasons:
- Standards compliance: The HTML specification requires valid URLs. Raw spaces violate this requirement.
- Interoperability: Not all user agents, HTTP clients, or content delivery systems handle unencoded spaces the same way. Some may truncate the URL at the first space or fail to resolve the resource entirely.
- Portability: If your HTML is consumed by tools, scrapers, or APIs that strictly parse URLs, unencoded spaces can cause silent failures.
- Consistency: Keeping URLs properly encoded prevents subtle bugs when paths are constructed dynamically in server-side or client-side code.
The fix is straightforward. You have two options:
- Percent-encode the spaces: Replace every space in the URL with
%20. This preserves the original file and folder names on the server while producing a valid URL in your HTML. - Eliminate spaces from file and folder names: Use hyphens (
-), underscores (_), or camelCase instead of spaces. This is generally considered best practice for web assets, as it avoids encoding issues across the board.
Note that this rule applies to the entire URL path, not just the filename. If any directory in the path contains a space, it must also be encoded or renamed. The same principle applies to other special characters that are reserved or disallowed in URLs, such as {, }, |, ^, and [.
Examples
Incorrect — space in the path
The folder name video images contains a space, which is illegal in a URL path segment.
<videocontrolsposter="/img/video images/snapshot.png">
<sourcesrc="/videos/sample.mp4"type="video/mp4">
</video>
Incorrect — space in the filename
The filename my poster.jpg also triggers the same error.
<videocontrolsposter="/img/my poster.jpg">
<sourcesrc="/videos/sample.mp4"type="video/mp4">
</video>
Fixed — percent-encoding the spaces
Each space is replaced with %20, producing a valid URL.
<videocontrolsposter="/img/video%20images/snapshot.png">
<sourcesrc="/videos/sample.mp4"type="video/mp4">
</video>
Fixed — removing spaces from the path
Renaming the folder to use a hyphen eliminates the need for encoding entirely.
<videocontrolsposter="/img/video-images/snapshot.png">
<sourcesrc="/videos/sample.mp4"type="video/mp4">
</video>
Fixed — removing spaces from the filename
<videocontrolsposter="/img/my-poster.jpg">
<sourcesrc="/videos/sample.mp4"type="video/mp4">
</video>
As a general best practice, avoid spaces in all file and folder names used on the web. Use hyphens or underscores instead. If you're working with files you can't rename — such as assets from a CMS or third-party system — always percent-encode spaces as %20 in your HTML. This applies not only to poster but to every attribute that takes a URL value, including src, href, action, data, and formaction.
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
<linkrel="styleshet"href="main.css">
The validator reports that styleshet is not a recognized keyword and is not an absolute URL.
Correct: Fixed spelling
<linkrel="stylesheet"href="main.css">
Incorrect: Non-standard keyword on an anchor
<ahref="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
<ahref="https://example.com"rel="noopener">Visit Example</a>
Incorrect: Relative URL as a custom link type
<linkrel="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:
<linkrel="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 -->
<linkrel="stylesheet"href="styles.css">
<!-- Linking a favicon -->
<linkrel="icon"href="favicon.ico">
<!-- Preloading a resource -->
<linkrel="preload"href="font.woff2"as="font"type="font/woff2"crossorigin>
<!-- Telling search engines not to follow a link -->
<ahref="https://example.com"rel="nofollow">Sponsored link</a>
<!-- Opening a link safely in a new tab -->
<ahref="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 -->
<ahref="https://example.com"target="_blank"rel="noopener noreferrer">External</a>
<!-- Incorrect: "popup" is not a recognized keyword or absolute URL -->
<ahref="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.
The sizes attribute works together with srcset to help the browser choose the most appropriate image source for the current layout. It accepts a comma-separated list of entries, where each entry is an optional media condition followed by a CSS length value (called a "source size value"). The browser evaluates these entries to determine the intended display width of the image before it downloads it. Valid length values include viewport-relative units like 100vw, absolute units like 472px, or calc() expressions like calc(100vw - 2rem).
The value auto was not part of the original HTML specification for the sizes attribute. However, the sizes="auto" value has been added to the HTML living standard specifically for use with lazy-loaded images (loading="lazy"). When both loading="lazy" and sizes="auto" are present, the browser can defer size calculation until layout time, since the image won't be fetched immediately anyway. Some validators may not yet recognize this newer addition, or the error may appear because auto is being used without loading="lazy", or combined incorrectly with other size entries like sizes="auto, 100vw".
This validation error matters for several reasons. First, if the browser doesn't understand the sizes value, it may fall back to a default of 100vw, which could cause it to download a larger image than necessary, hurting performance. Second, malformed attribute values can lead to unpredictable behavior across different browsers. Third, standards compliance ensures your markup works reliably now and in the future.
How to Fix
You have a few options depending on your situation:
Replace
autowith a valid CSS length. If you know the intended display size of the image, specify it directly. This is the most broadly compatible approach.Use
sizes="auto"only withloading="lazy". If you want the browser to automatically determine the size, ensure you also includeloading="lazy"and awidthattribute on the image. Note that some validators may still flag this until they update their rules.Remove
autofrom a comma-separated list. If you have something likesizes="auto, (max-width: 600px) 100vw, 50vw", remove theautoentry entirely.
Examples
Incorrect: Using auto without lazy loading
This triggers the validation error because auto is not a valid CSS length in the traditional sizes syntax.
<img
src="image.jpg"
srcset="image-small.jpg 300w, image-medium.jpg 600w, image-large.jpg 1000w"
sizes="auto, 100vw"
alt="A scenic mountain landscape"
>
Fixed: Using a valid CSS length value
Replace auto with a concrete size or a set of media-conditioned sizes.
<img
src="image.jpg"
srcset="image-small.jpg 300w, image-medium.jpg 600w, image-large.jpg 1000w"
sizes="(max-width: 472px) 100vw, 472px"
alt="A scenic mountain landscape"
>
In this example, when the viewport is 472 pixels wide or smaller, the image takes up the full viewport width (100vw). For wider viewports, the browser knows the image will display at 472px wide and selects the best source from srcset accordingly.
Fixed: Using auto with lazy loading
If you want the browser to determine the display size automatically, pair sizes="auto" with loading="lazy" and explicit dimensions.
<img
src="image.jpg"
srcset="image-small.jpg 300w, image-medium.jpg 600w, image-large.jpg 1000w"
sizes="auto"
width="600"
height="400"
loading="lazy"
alt="A scenic mountain landscape"
>
The width and height attributes help the browser reserve the correct space in the layout, and loading="lazy" allows the browser to defer both loading and size calculation until the image is near the viewport.
Fixed: Using calc() for dynamic sizing
If your image sits inside a container with padding, you can use calc() for a precise size hint.
<img
src="image.jpg"
srcset="image-small.jpg 300w, image-medium.jpg 600w, image-large.jpg 1000w"
sizes="calc(100vw - 2rem)"
alt="A scenic mountain landscape"
>
The sizes and srcset attributes work together to enable responsive images, but they serve distinct roles and use different syntax. The srcset attribute lists available image files along with their intrinsic widths using the w descriptor (e.g., 800w means the image file is 800 pixels wide). The sizes attribute, on the other hand, tells the browser how wide the image will actually be rendered in the layout, using standard CSS length units. The browser combines this information — knowing which files are available and how large the image will appear — to choose the most efficient file to download.
A common mistake is mixing up these two syntaxes, typically by copying a width descriptor like 860w from srcset and placing it into sizes. Since w is not a CSS unit, the validator rejects it. This matters because an invalid sizes value prevents the browser from correctly calculating which image source to use, potentially causing it to download an unnecessarily large image (wasting bandwidth) or a too-small image (resulting in poor quality).
How the sizes attribute works
The sizes attribute accepts a comma-separated list of media conditions paired with CSS lengths, plus an optional default length at the end. Each entry follows the pattern (media-condition) length. The browser evaluates the conditions in order and uses the length from the first matching condition. If none match, it uses the final default value.
Valid CSS length units include px, em, rem, vw, vh, ch, cm, mm, in, pt, pc, and CSS calc() expressions. You can combine units with calc() for more precise sizing — for example, calc(100vw - 2rem).
Examples
❌ Incorrect: using w in sizes
This triggers the validation error because 860w is a srcset width descriptor, not a CSS length:
<img
alt="A landscape photo"
sizes="860w"
srcset="photo-small.jpg 430w, photo-large.jpg 860w"
src="photo-large.jpg">
✅ Correct: using px in sizes
Replace the w value with a CSS length that reflects the image's actual display size:
<img
alt="A landscape photo"
sizes="860px"
srcset="photo-small.jpg 430w, photo-large.jpg 860w"
src="photo-large.jpg">
✅ Correct: responsive sizes with media conditions
Use media conditions to specify different display sizes at different viewport widths:
<img
alt="A landscape photo"
sizes="(min-width: 1024px) 860px, (min-width: 568px) 430px, 100vw"
srcset="photo-small.jpg 430w, photo-large.jpg 860w"
src="photo-large.jpg">
This tells the browser:
- On viewports 1024px and wider, the image displays at 860px wide.
- On viewports 568px and wider, the image displays at 430px wide.
- On smaller viewports, the image fills the full viewport width (
100vw).
✅ Correct: using calc() in sizes
When the image width depends on padding or margins, calc() provides precise sizing:
<img
alt="A landscape photo"
sizes="(min-width: 768px) calc(50vw - 2rem), calc(100vw - 1rem)"
srcset="photo-small.jpg 400w, photo-medium.jpg 800w, photo-large.jpg 1200w"
src="photo-medium.jpg">
Key takeaways
- The
wdescriptor belongs only insrcset, where it describes the intrinsic width of each image file. - The
sizesattribute uses CSS length units (px,vw,em,calc(), etc.) to describe how wide the image will appear on screen. - If your image always displays at a fixed width, a simple value like
sizes="300px"is sufficient. - If your image width varies by viewport, use media conditions with appropriate CSS lengths to give the browser the information it needs to select the best source.
Backslashes are not valid delimiters in URLs according to the URL Living Standard. While some browsers may silently normalize backslashes to forward slashes, this behavior is non-standard and should not be relied upon. Using backslashes in URLs can lead to broken images, unexpected behavior across different browsers, and failures in environments that strictly follow URL specifications (such as HTTP servers, CDNs, or validation tools).
This issue commonly arises when developers copy file paths directly from a Windows file system — where \ is the directory separator — and paste them into HTML src attributes. It can also happen when server-side code generates URLs using OS-level path functions instead of URL-building utilities.
Beyond standards compliance, this matters for several practical reasons:
- Cross-browser reliability: Not all browsers or HTTP clients normalize backslashes the same way.
- Server compatibility: Many web servers interpret backslashes literally, resulting in 404 errors.
- Portability: Code with backslash paths may work in local development on Windows but break when deployed to a Linux-based server.
To fix the issue, locate every backslash in the src attribute value and replace it with a forward slash. This applies to all URL contexts, not just img elements — though the validator specifically flags it here.
Examples
❌ Incorrect: backslashes in the src path
<imgsrc="images\photos\landscape.jpg"alt="Mountain landscape">
<imgsrc="https://example.com\img\small\photo.png"alt="Example image">
Both of these use backslashes as path delimiters, which triggers the validation error.
✅ Correct: forward slashes in the src path
<imgsrc="images/photos/landscape.jpg"alt="Mountain landscape">
<imgsrc="https://example.com/img/small/photo.png"alt="Example image">
Simply replacing \ with / resolves the issue and produces a valid, portable URL.
❌ Incorrect: mixed delimiters
<imgsrc="assets/images\banner\hero.webp"alt="Hero banner">
Even a single backslash in an otherwise valid path will trigger this error.
✅ Correct: consistent forward slashes
<imgsrc="assets/images/banner/hero.webp"alt="Hero banner">
Tips to avoid this issue
- Don't copy-paste Windows file paths directly into HTML. Always convert backslashes to forward slashes.
- Use your editor's find-and-replace to search for
\withinsrcattributes across your project. - If generating URLs in server-side code, use URL-building functions rather than file-system path functions. For example, in Node.js, use the
urlmodule or template literals with/instead ofpath.join(), which uses\on Windows. - Run the W3C validator regularly during development to catch issues like this before deployment.
The src attribute on a <script> element points to a URL whose path contains a character that is not allowed there unless it is percent-encoded.
A URL path may only use a restricted set of characters. Symbols such as ^, spaces, square brackets, and backticks have to be written in their percent-encoded form, so a caret becomes %5E. The validator names the exact character it rejected, so look at the path segment it points to and either encode that character or change the URL so it no longer appears.
A common source of the ^ character is a version range copied from a package manager. A specifier like @^2 belongs in a package.json file, not in a CDN URL. When you load a script straight from a CDN, pin an explicit version instead.
Invalid example
<scriptsrc="https://cdn.example.com/pkg@^2/dist/app.js"></script>
Valid example
<scriptsrc="https://cdn.example.com/pkg@2.1.0/dist/app.js"></script>
URLs follow strict syntax rules defined by RFC 3986, which does not permit literal space characters anywhere in the URI — including path segments, query strings, and fragment identifiers. When the W3C HTML Validator encounters a space in the src attribute of a <script> element, it flags it as an illegal character because the attribute value is not a valid URL.
While most modern browsers will silently fix this by encoding the space before making the request, relying on this behavior is problematic for several reasons:
- Standards compliance: The HTML specification requires that the
srcattribute contain a valid URL. A URL with a literal space is technically malformed. - Cross-browser reliability: Not all user agents, proxies, or CDNs handle malformed URLs the same way. What works in one browser may fail in another context.
- Interoperability: Other tools that consume your HTML — such as linters, crawlers, screen readers, and build pipelines — may not be as forgiving as browsers.
- Copy-paste and linking issues: Literal spaces in URLs cause problems when users copy links or when URLs appear in plain-text contexts like emails, where the space may break the URL in two.
How to fix it
You have three options, listed from most recommended to least:
- Rename the file or directory to eliminate spaces entirely (e.g., use hyphens or underscores). This is the cleanest solution.
- Percent-encode the space as
%20in thesrcattribute value. - Use a build tool or bundler that generates references with properly encoded or space-free paths automatically.
Avoid using + as a space replacement in path segments. The + character represents a space only in application/x-www-form-urlencoded query strings, not in URL path segments.
Examples
❌ Invalid: space in the path segment
<scriptsrc="https://example.com/media assets/app.js"></script>
The space between media and assets makes this an invalid URL.
✅ Fixed: percent-encode the space
<scriptsrc="https://example.com/media%20assets/app.js"></script>
Replacing the space with %20 produces a valid, standards-compliant URL.
✅ Better: rename to avoid spaces entirely
<scriptsrc="https://example.com/media-assets/app.js"></script>
Using a hyphen (or underscore) instead of a space is the preferred approach. It keeps URLs clean, readable, and free of encoding issues.
❌ Invalid: space in a local relative path
This issue isn't limited to absolute URLs. Relative paths trigger the same error:
<scriptsrc="js/my script.js"></script>
✅ Fixed: encode or rename the local file
<scriptsrc="js/my%20script.js"></script>
Or, better yet:
<scriptsrc="js/my-script.js"></script>
Multiple spaces and other special characters
If a URL contains multiple spaces or other special characters, each one must be individually encoded. For example, { becomes %7B and } becomes %7D. A quick reference for common characters:
| Character | Encoded form |
|---|---|
| Space | %20 |
[ | %5B |
] | %5D |
{ | %7B |
} | %7D |
<!-- Invalid -->
<scriptsrc="libs/my library [v2].js"></script>
<!-- Valid -->
<scriptsrc="libs/my%20library%20%5Bv2%5D.js"></script>
<!-- Best: rename the file -->
<scriptsrc="libs/my-library-v2.js"></script>
Note that this same rule applies to the src attribute on other elements like <img>, <iframe>, <audio>, and <video>, as well as the href attribute on <a> and <link>. Whenever you reference a URL in HTML, make sure it contains no literal spaces.
Spaces in the src attribute of a <source> element are not valid URL characters and must be encoded or removed.
URLs follow strict syntax rules defined in RFC 3986. A space character is not permitted in any part of a URL path. When a file name or path contains spaces, you must replace each space with %20 (the percent-encoded form) or rename the file to avoid spaces altogether.
This applies to all elements that accept a URL, including <source>, <img>, <a>, <script>, and <link>. Browsers often handle spaces gracefully by encoding them automatically, but the HTML is still technically invalid and can cause issues in some contexts, such as when URLs are copied, shared, or processed by other tools.
The best practice is to avoid spaces in file names entirely. Use hyphens (-) or underscores (_) instead. If you can't rename the files, percent-encode the spaces.
Bad Example
<videocontrols>
<sourcesrc="videos/my cool video.mp4"type="video/mp4">
</video>
Good Example — Percent-Encoded Spaces
<videocontrols>
<sourcesrc="videos/my%20cool%20video.mp4"type="video/mp4">
</video>
Good Example — No Spaces in File Name
<videocontrols>
<sourcesrc="videos/my-cool-video.mp4"type="video/mp4">
</video>
When the W3C HTML Validator reports "Expected a slash," it means the URL parser encountered an unexpected character where a / should appear. URLs follow a strict syntax defined by the URL Living Standard. For scheme-based URLs, the format requires the scheme (like https:) to be immediately followed by // and then the authority (hostname). Any deviation — such as a space, a missing slash, or an encoded character in the wrong place — will make the URL invalid.
This matters for several reasons. Browsers may attempt to correct malformed URLs, but their behavior is inconsistent and unpredictable. A broken src attribute can cause images not to load, scripts to fail silently, or media elements to show fallback content. Screen readers and assistive technologies rely on valid URLs to provide meaningful information to users. Search engine crawlers may also fail to follow or index resources with malformed URLs.
Common causes of this error include:
- Accidental spaces inserted within the URL, especially between the scheme and the double slashes (e.g.,
https: //instead ofhttps://). - Missing slashes in the scheme (e.g.,
https:/example.comwith only one slash). - Copy-paste artifacts where invisible characters or line breaks get embedded in the URL string.
- Template or CMS issues where dynamic URL generation introduces unexpected characters.
To fix the issue, carefully inspect the src attribute value and ensure it forms a valid, complete URL with no stray characters. If the URL is generated dynamically, check the code that constructs it.
Examples
Incorrect: space between scheme and slashes
<imgsrc="https: //example.com/photo.jpg"alt="A photo">
The space after https: breaks the URL. The validator expects a / immediately after the colon but finds a space instead.
Fixed: remove the space
<imgsrc="https://example.com/photo.jpg"alt="A photo">
Incorrect: single slash instead of double slash
<scriptsrc="https:/cdn.example.com/app.js"></script>
The URL scheme requires // after https:, but only one / is present.
Fixed: use the correct double slash
<scriptsrc="https://cdn.example.com/app.js"></script>
Incorrect: line break embedded in the URL
Sometimes copy-pasting or template rendering introduces hidden line breaks:
<videosrc="https:// example.com/video.mp4">
example.com/video.mp4
</video>
Fixed: ensure the URL is on a single line with no breaks
<videosrc="https://example.com/video.mp4"></video>
Incorrect: protocol-relative URL with a missing slash
<imgsrc="/example.com/logo.png"alt="Logo">
If the intent is a protocol-relative URL, it needs two slashes. With a single slash, this becomes an absolute path on the current domain rather than a reference to example.com.
Fixed: use two slashes for protocol-relative URLs
<imgsrc="//example.com/logo.png"alt="Logo">
Tip: If you're having trouble spotting the problem, paste the URL into your browser's address bar to see if it resolves correctly, or use a text editor that reveals invisible characters (like zero-width spaces or non-breaking spaces) that may be hiding in the string.
The srcset attribute allows you to provide multiple image sources so the browser can choose the most appropriate one based on the user's viewport size or screen density. There are two distinct modes for srcset:
- Width descriptor mode — each candidate specifies its intrinsic width using a
wdescriptor (e.g.,400w). This mode requires thesizesattribute so the browser knows how much space the image will occupy in the layout and can calculate which source to download. - Pixel density descriptor mode — each candidate specifies a pixel density using an
xdescriptor (e.g.,2x). This mode must not include asizesattribute.
When you include a sizes attribute but forget to add width descriptors to one or more srcset entries, the browser has incomplete information. The HTML specification explicitly states that if sizes is present, all image candidate strings must use width descriptors. An entry without a descriptor defaults to 1x (a pixel density descriptor), which conflicts with the width descriptor mode triggered by sizes. This mismatch causes the W3C validator to report the error.
Beyond validation, this matters for real-world performance. Responsive images are one of the most effective tools for reducing page weight on smaller screens. If the descriptors are missing or mismatched, browsers may download an image that is too large or too small, hurting both performance and visual quality.
How to fix it
You have two options depending on your use case:
Option 1: Add width descriptors to all srcset candidates
If you need the browser to select images based on viewport size (the most common responsive images pattern), keep the sizes attribute and ensure every srcset entry has a w descriptor that matches the image's intrinsic pixel width.
Option 2: Remove sizes and use pixel density descriptors
If you only need to serve higher-resolution images for high-DPI screens (e.g., Retina displays) and the image always renders at the same CSS size, remove the sizes attribute and use x descriptors instead.
Examples
❌ Incorrect: sizes present but srcset entry has no width descriptor
<img
src="photo-800.jpg"
srcset="photo-400.jpg, photo-800.jpg"
sizes="(min-width: 600px) 800px, 100vw"
alt="A mountain landscape">
Both srcset entries lack a width descriptor. Because sizes is present, the validator reports an error for each candidate.
✅ Correct: sizes present with width descriptors on every candidate
<img
src="photo-800.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w"
sizes="(min-width: 600px) 800px, 100vw"
alt="A mountain landscape">
Each candidate now specifies its intrinsic width (400w and 800w), which tells the browser the actual pixel width of each source file. The browser combines this with the sizes value to pick the best match.
❌ Incorrect: mixing width descriptors and bare entries
<img
src="photo-800.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg"
sizes="(min-width: 600px) 800px, 100vw"
alt="A mountain landscape">
The second candidate (photo-800.jpg) is missing its width descriptor. All candidates must have one when sizes is present — not just some of them.
✅ Correct: pixel density descriptors without sizes
<img
src="photo-800.jpg"
srcset="photo-800.jpg 1x, photo-1600.jpg 2x"
alt="A mountain landscape">
Here the sizes attribute is removed, and each srcset entry uses a pixel density descriptor (1x, 2x). This is valid and appropriate when the image always occupies the same CSS dimensions regardless of viewport width.
❌ Incorrect: using sizes with pixel density descriptors
<img
src="photo-800.jpg"
srcset="photo-800.jpg 1x, photo-1600.jpg 2x"
sizes="(min-width: 600px) 800px, 100vw"
alt="A mountain landscape">
The sizes attribute and x descriptors cannot be combined. Either switch to w descriptors or remove sizes.
Quick reference
| Pattern | srcset descriptor | sizes required? |
|---|---|---|
| Viewport-based selection | Width (w) | Yes |
| Density-based selection | Pixel density (x) | No — must be omitted |
Remember that the w value in srcset refers to the image file's intrinsic pixel width (e.g., an 800-pixel-wide image gets 800w), while values in sizes use CSS length units like px, vw, or em to describe how wide the image will render in the layout.
The srcset attribute lets browsers intelligently choose which image to load based on the viewport size and device pixel ratio. Each entry in a srcset consists of a URL followed by either a width descriptor (like 300w) or a pixel density descriptor (like 2x). When using width descriptors, the value represents the intrinsic pixel width of the image file — that is, the actual width of the image as stored on disk.
A width descriptor of 0w violates the HTML specification, which requires width descriptors to be integers greater than zero. A zero-width image cannot meaningfully participate in the browser's source selection process. The browser uses these width values in combination with the sizes attribute to calculate which image best fits the current layout — a value of zero would break this calculation entirely.
This issue commonly occurs when:
- Image dimensions are dynamically generated and a fallback of
0is used for missing data. - A placeholder or empty state is accidentally included in the
srcset. - A CMS or build tool outputs a
0wdescriptor for images whose dimensions weren't computed.
Why it matters
- Standards compliance: The HTML specification explicitly requires width descriptors to be positive integers. Validators will flag
0was an error. - Browser behavior: While browsers may silently ignore the invalid entry, you can't rely on consistent handling across all browsers and versions. The image selection algorithm may behave unpredictably.
- Performance: A well-formed
srcsetis key to responsive image loading. Invalid descriptors can prevent browsers from selecting the optimal image, leading to unnecessarily large downloads or poor image quality.
How to fix it
- Open the image file associated with the
0wdescriptor and check its actual pixel width using an image editor or the command line. - Replace
0wwith the correct width (e.g.,150wfor a 150-pixel-wide image). - If the image is truly zero-width or a placeholder, remove that entry from the
srcsetentirely. - Ensure every remaining entry has a unique, positive width descriptor.
Examples
❌ Invalid: width descriptor of 0w
<picture>
<source
srcset="/images/icon_placeholder.png 0w, /images/icon_large.png 600w"
/images/icon_large.png 600w
media="(max-width: 600px)">
<imgsrc="/images/icon_fallback.png"alt="App logo">
</picture>
The 0w descriptor triggers the validation error because zero is not a valid width.
✅ Fixed: all width descriptors are positive
<picture>
<source
srcset="/images/icon_small.png 300w, /images/icon_large.png 600w"
/images/icon_large.png 600w
media="(max-width: 600px)">
<imgsrc="/images/icon_fallback.png"alt="App logo">
</picture>
Each entry now has a meaningful width descriptor (300w and 600w) that reflects the actual pixel width of the corresponding image.
❌ Invalid: 0w on an <img> element
<img
srcset="/images/hero_tiny.jpg 0w, /images/hero_medium.jpg 800w, /images/hero_large.jpg 1200w"
/images/hero_medium.jpg 800w,
/images/hero_large.jpg 1200w
sizes="100vw"
src="/images/hero_medium.jpg"
alt="Mountain landscape">
✅ Fixed: placeholder entry removed or corrected
If the tiny image is 400 pixels wide, use 400w:
<img
srcset="/images/hero_tiny.jpg 400w, /images/hero_medium.jpg 800w, /images/hero_large.jpg 1200w"
/images/hero_medium.jpg 800w,
/images/hero_large.jpg 1200w
sizes="100vw"
src="/images/hero_medium.jpg"
alt="Mountain landscape">
Alternatively, if the image doesn't belong in the set at all, simply remove it:
<img
srcset="/images/hero_medium.jpg 800w, /images/hero_large.jpg 1200w"
/images/hero_large.jpg 1200w
sizes="100vw"
src="/images/hero_medium.jpg"
alt="Mountain landscape">
When using a build tool or CMS that generates srcset values dynamically, add a check to filter out any entries where the computed width is zero or missing before rendering the attribute. This prevents the invalid markup from reaching production.
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.
Pro Trial
Full Pro access. Cancel anytime.
Start Pro Trial →Join teams across 40+ countries