HTML Guides for img
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.
A <source> element inside a <picture> that is followed by another <source> or an <img> with srcset must include a media and/or type attribute.
The <source> element is used inside <picture> for responsive images, allowing different resources to be loaded based on conditions such as viewport width (media) or image format (type). According to the HTML standard, when multiple <source> elements are present (or a following <img> with srcset), each <source> must provide a media and/or type attribute so the browser can choose the appropriate resource based on those hints.
Without media or type, the browser cannot distinguish when to use each source, which can lead to validation errors and unexpected rendering behavior.
Incorrect example (causes the validator error):
<picture>
<source srcset="image1.webp">
<source srcset="image2.jpg">
<img alt="" src="fallback.jpg" srcset="fallback2x.jpg 2x">
</picture>
Correct examples (fixing the error):
<picture>
<source srcset="image1.webp" type="image/webp">
<source srcset="image2.jpg" type="image/jpeg">
<img alt="" src="fallback.jpg" srcset="fallback2x.jpg 2x">
</picture>
or
<picture>
<source srcset="image-small.jpg" media="(max-width: 600px)">
<source srcset="image-large.jpg" media="(min-width: 601px)">
<img alt="" src="fallback.jpg" srcset="fallback2x.jpg 2x">
</picture>
By specifying the media and/or type attributes for each <source>, you satisfy the HTML standard and resolve the W3C validator issue.
The alt attribute is one of the most important accessibility features in HTML. When a screen reader encounters an <img> element, it reads the alt text aloud so that visually impaired users understand the image’s content and purpose. Without it, screen readers may fall back to reading the file name (e.g., “DSC underscore 0042 dot jpeg”), which is meaningless and confusing. Search engines also use alt text to understand and index image content, so including it benefits SEO as well.
The HTML specification requires the alt attribute on all <img> elements, with only narrow exceptions (such as when the image’s role is explicitly overridden via certain ARIA attributes, or when the image is inside a <figure> with a <figcaption> that fully describes it—though even then, including alt is strongly recommended).
How to choose the right alt text
The value of the alt attribute depends on the image’s purpose:
- Informative images — Describe the content concisely. For example, a photo of a product should describe the product.
- Functional images — Describe the action or destination, not the image itself. For example, a search icon used as a button should have alt="Search", not alt="Magnifying glass".
- Decorative images — Use an empty alt attribute (alt=""). This tells screen readers to skip the image entirely. Do not omit the attribute—use an empty string.
- Complex images (charts, diagrams) — Provide a brief summary in alt and a longer description elsewhere on the page or via a link.
Examples
❌ Missing alt attribute
This triggers the W3C validation error:
<img src="photo.jpg">
A screen reader has no useful information to convey, and the validator flags this as an error.
✅ Informative image with descriptive alt
<img src="photo.jpg" alt="Person holding an orange tabby cat in a sunlit garden">
The alt text describes what the image shows, giving screen reader users meaningful context.
✅ Decorative image with empty alt
<img src="decorative-border.png" alt="">
When an image is purely decorative and adds no information, an empty alt attribute tells assistive technology to ignore it. This is valid and preferred over omitting the attribute.
✅ Functional image inside a link
<a href="/home">
<img src="logo.svg" alt="Acme Corp — Go to homepage">
</a>
Because the image is the only content inside the link, the alt text must describe the link’s purpose.
✅ Image inside a <figure> with <figcaption>
<figure>
<img src="chart.png" alt="Bar chart showing quarterly revenue growth from Q1 to Q4 2024">
<figcaption>Quarterly revenue growth for fiscal year 2024.</figcaption>
</figure>
Even when a <figcaption> is present, including a descriptive alt attribute is best practice. The alt should describe the image itself, while the <figcaption> provides additional context visible to all users.
Common mistakes to avoid
- Don’t start with “Image of” or “Picture of” — Screen readers already announce the element as an image. Write alt="Golden retriever playing fetch", not alt="Image of a golden retriever playing fetch".
- Don’t use the file name — alt="IMG_4392.jpg" is not helpful.
- Don’t duplicate surrounding text — If the text next to the image already describes it, use alt="" to avoid redundancy.
- Don’t omit alt thinking it’s optional — A missing alt attribute and alt="" are fundamentally different. Missing means the screen reader may announce the file path; empty means the screen reader intentionally skips the image.
An empty alt attribute on an img element is a deliberate signal that the image is purely decorative and carries no meaningful content. According to the WHATWG HTML specification, this maps the element to the “presentation” role in the accessibility tree, effectively hiding it from screen readers and other assistive technologies.
When you add a role attribute to an img with alt="", you’re sending contradictory instructions: the empty alt says “this image is decorative, ignore it,” while the role attribute says “this image has a specific semantic purpose.” Browsers and assistive technologies cannot reliably resolve this conflict, which can lead to confusing or inconsistent behavior for users who rely on screen readers.
This rule exists to enforce clarity in how images are exposed to the accessibility tree. If an image is truly decorative, it should have alt="" and no role. If an image serves a functional or semantic purpose — such as acting as a button, link, or illustration — it should have both a descriptive alt value and, if needed, an appropriate role.
How to fix it
You have two options depending on the image’s purpose:
-
The image is decorative: Remove the role attribute entirely. The empty alt attribute already communicates that the image should be ignored by assistive technologies.
-
The image is meaningful: Provide a descriptive alt value that explains the image’s purpose. If a specific role is genuinely needed, keep it alongside the non-empty alt.
Examples
❌ Incorrect: empty alt with a role attribute
<img src="icon.png" alt="" role="img">
<img src="banner.jpg" alt="" role="presentation">
Even role="presentation" is redundant and invalid here — the empty alt already implies presentational semantics.
✅ Correct: decorative image with no role
<img src="icon.png" alt="">
If the image is decorative, simply remove the role attribute. The empty alt is sufficient.
✅ Correct: meaningful image with a descriptive alt and a role
<img src="warning-icon.png" alt="Warning" role="img">
If the image conveys information, give it a descriptive alt value. The role="img" is typically unnecessary here since img elements already have an implicit role of img when alt is non-empty, but it is at least valid.
✅ Correct: meaningful image used in a specific context
<button>
<img src="search-icon.png" alt="Search">
</button>
Here the image has a descriptive alt and doesn’t need an explicit role because its purpose is conveyed through the alt text and its context within the button.
The <noscript> element provides fallback content for users whose browsers don’t support scripting or have JavaScript disabled. When <noscript> appears inside <head>, the HTML specification restricts its contents to metadata elements only — specifically <link>, <style>, and <meta>. An <img> tag is flow content, not metadata content, so it is not permitted inside <head> regardless of whether it’s wrapped in <noscript>.
When <noscript> appears inside <body>, however, it can contain any flow content that would normally be valid at that position — including <img> elements. This is why the fix is simply to relocate the <noscript> block from the <head> to the <body>.
This issue is extremely common with third-party tracking pixels. Services like Facebook Pixel, LinkedIn Insight Tag, Google Tag Manager, and similar tools often instruct you to paste a <noscript> block containing a 1×1 tracking <img> directly after the opening <head> tag. While this works in browsers (they are forgiving with invalid HTML), it does not conform to the HTML specification and will trigger a validation error.
Beyond standards compliance, keeping your HTML valid ensures predictable behavior across all browsers and assistive technologies. Invalid markup can lead to unexpected DOM construction, which may cause subtle issues with CSS selectors, JavaScript DOM queries, or accessibility tools.
How to Fix It
- Locate the <noscript> block that contains the <img> element inside your <head>.
- Move that entire <noscript> block to somewhere inside the <body> — typically right after the opening <body> tag.
- Keep any associated <script> tag in the <head> if desired; only the <noscript> with the <img> needs to move.
Examples
❌ Invalid: <img> inside <noscript> in <head>
This is the pattern commonly provided by third-party tracking pixel instructions:
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Page</title>
<script>
/* tracking script */
</script>
<noscript>
<img src="https://example.com/pixel?id=123" height="1" width="1" alt="">
</noscript>
</head>
<body>
<h1>Welcome</h1>
</body>
</html>
The validator reports “Bad start tag in img in noscript in head” because <img> is not valid metadata content.
✅ Valid: <noscript> with <img> moved to <body>
Move the <noscript> block into the <body> while leaving the <script> in the <head>:
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Page</title>
<script>
/* tracking script */
</script>
</head>
<body>
<noscript>
<img src="https://example.com/pixel?id=123" height="1" width="1" alt="">
</noscript>
<h1>Welcome</h1>
</body>
</html>
The <img> is now inside <body> (via <noscript>), where flow content is permitted. The tracking pixel will still function correctly for users with JavaScript disabled.
✅ Valid: metadata-only <noscript> in <head>
If you only need metadata fallback content in the <head>, elements like <meta>, <link>, and <style> are allowed:
<head>
<title>My Page</title>
<noscript>
<style>
.js-only { display: none; }
</style>
</noscript>
</head>
This is valid because <style> is metadata content. Use this pattern when you need to apply fallback styles for non-JavaScript users, and reserve <noscript> blocks with <img> or other flow content for the <body>.
The HTML specification defines the height attribute on <img> as a “valid non-negative integer,” which means it must consist only of digits — no units, no percentage signs. When you write height="100%", the validator expects a digit character but encounters %, producing this error. The same rule applies to the width attribute.
This matters for several reasons. First, browsers use the width and height attributes to reserve the correct amount of space for an image before it loads, which prevents layout shifts (a key performance metric known as Cumulative Layout Shift). When the value contains invalid characters like %, browsers may ignore the attribute entirely or interpret it unpredictably, undermining this layout reservation. Second, invalid HTML can cause inconsistent rendering across different browsers and assistive technologies. Third, the percentage-based sizing you likely intended simply isn’t supported through HTML attributes — it requires CSS.
The fix depends on what you’re trying to achieve:
- Fixed dimensions: Replace the percentage with a plain integer representing the image’s intrinsic or desired pixel size (e.g., height="300").
- Responsive or percentage-based sizing: Remove the height attribute (or set it to the image’s intrinsic pixel dimensions) and use CSS to control how the image scales within its container.
It’s a good practice to always include both width and height attributes with the image’s actual intrinsic dimensions, then use CSS to override the display size. This gives browsers the aspect ratio information they need to reserve space while still allowing flexible layouts.
Examples
Incorrect: percentage value in the height attribute
This triggers the validation error because % is not a valid digit:
<img src="photo.jpg" width="100%" height="100%" alt="A landscape photo">
Fixed: using integer pixel values
Specify the image’s intrinsic dimensions as plain numbers:
<img src="photo.jpg" width="800" height="600" alt="A landscape photo">
Fixed: combining HTML attributes with CSS for responsive sizing
Set the intrinsic dimensions in HTML for layout stability, then use CSS to make the image responsive:
<style>
.responsive-img {
width: 100%;
height: auto;
}
</style>
<img
src="photo.jpg"
width="800"
height="600"
class="responsive-img"
alt="A landscape photo">
With this approach, the browser knows the image’s aspect ratio (800×600) and reserves the appropriate space, while CSS ensures the image scales fluidly to fill its container. The height: auto rule maintains the correct aspect ratio as the width changes.
Fixed: filling a container with CSS only
If you don’t know the image’s intrinsic dimensions and simply want it to fill a container, you can omit the HTML attributes and rely entirely on CSS:
<style>
.image-container {
width: 300px;
height: 200px;
}
.image-container img {
width: 100%;
height: 100%;
object-fit: cover;
}
</style>
<div class="image-container">
<img src="photo.jpg" alt="A landscape photo">
</div>
The object-fit: cover property ensures the image fills the container without distortion, cropping as needed. Note that omitting width and height attributes means the browser cannot reserve space before the image loads, so this approach may cause layout shifts. Where possible, prefer including the intrinsic dimensions as shown in the previous example.
The sizes attribute works together with the srcset attribute to help the browser choose the most appropriate image source for the current viewport and layout. Its value must follow a specific syntax: a comma-separated list where each entry is an optional media condition followed by a CSS length value. The final entry acts as a default and should be a length value without a media condition.
The value "auto" is not recognized as a valid CSS length or source size descriptor by the current HTML specification. While there is a newer sizes="auto" feature being developed for use specifically with loading="lazy" images, it is not yet universally part of the validated standard, and the W3C validator flags it as invalid. When the validator encounters auto, it expects a number (like 100vw or 50px) or a minus sign, but instead finds the letter “a”, producing this error.
Why This Matters
- Standards compliance: Using invalid attribute values means your HTML doesn’t conform to the specification, which could lead to unpredictable behavior across browsers.
- Responsive image loading: When sizes contains an invalid value, browsers may fall back to the default value of 100vw, which can cause them to download unnecessarily large images, hurting performance.
- Accessibility and tooling: Invalid HTML can cause issues with assistive technologies and automated tools that rely on well-formed markup.
How to Fix It
You have several options depending on your use case:
- Specify explicit sizes — Provide a valid source size list that describes how wide the image will be displayed at various viewport widths.
- Use a simple default — Set sizes to a single CSS length like "100vw" if the image always spans the full viewport width.
- Remove sizes entirely — If you don’t use srcset with width descriptors, the sizes attribute is unnecessary and can be removed.
- Use sizes="auto" with loading="lazy" — If you intentionally want the browser to determine sizes automatically for lazy-loaded images, be aware this is a newer feature that the validator may not yet support. You can suppress this specific warning if you’ve confirmed browser support meets your needs.
Examples
❌ Invalid: Using auto as the sizes value
<img
src="photo.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w"
sizes="auto"
alt="A scenic mountain view">
This triggers the error because auto is not a valid CSS length or source size descriptor.
✅ Fixed: Using a responsive source size list
<img
src="photo.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw, (max-width: 1000px) 50vw, 33vw"
alt="A scenic mountain view">
This tells the browser: use 100% of the viewport width on screens up to 600px, 50% on screens up to 1000px, and 33% on larger screens. The browser then picks the best image from srcset.
✅ Fixed: Using a simple default size
<img
src="photo.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w"
sizes="100vw"
alt="A scenic mountain view">
If the image always fills the full viewport width, 100vw is a straightforward valid value.
✅ Fixed: Removing sizes when srcset is not used
<img src="photo.jpg" alt="A scenic mountain view">
If you’re not using srcset with width descriptors, the sizes attribute serves no purpose and can be safely removed. Without it, the browser defaults to 100vw when interpreting any srcset width descriptors.
✅ Fixed: Using a fixed pixel width
<img
src="photo.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w"
sizes="400px"
alt="A scenic mountain view">
If the image is always displayed at a fixed width regardless of viewport size, you can specify that width directly as a CSS length value.
The HTML Living Standard defines the width and height attributes on img elements as accepting only valid non-negative integers. These values represent pixel dimensions and must consist solely of digits (e.g., "200", "1024"). Setting width="auto" causes a validation error because "auto" is not a number — the parser expects a digit as the first character but encounters the letter "a" instead.
This confusion often arises because auto is a perfectly valid value in CSS (e.g., width: auto;), but HTML attributes and CSS properties follow different rules. The width HTML attribute is strictly for declaring the image’s intrinsic pixel dimensions, while CSS handles flexible, responsive, or automatic sizing.
Why this matters
- Standards compliance: Invalid attribute values can cause unpredictable rendering behavior across different browsers. While most browsers will simply ignore an invalid width value, relying on error recovery is fragile and not guaranteed.
- Layout stability: The width and height HTML attributes help browsers reserve the correct amount of space for an image before it loads, preventing Cumulative Layout Shift (CLS). When these values are invalid, the browser can’t calculate the aspect ratio in advance, leading to content jumping as images load.
- Accessibility and tooling: Screen readers, search engine crawlers, and other automated tools may rely on valid markup to correctly interpret page content.
How to fix it
-
If you know the image’s pixel dimensions, set width and height to the actual values. This is the recommended approach because it gives browsers the aspect ratio needed to reserve space during loading.
-
If you want the image to resize fluidly, remove the width attribute (or keep it as a pixel value for aspect-ratio hints) and use CSS instead — for example, width: 100%; or max-width: 100%; height: auto;.
-
If you want the browser to determine the size automatically, simply omit the width attribute. The browser will use the image’s native dimensions by default.
Examples
❌ Invalid: using "auto" as the width value
<img src="photo.jpg" alt="A sunset over the ocean" width="auto" height="400">
This triggers the error because "auto" is not a valid non-negative integer.
✅ Fixed: using a pixel value
<img src="photo.jpg" alt="A sunset over the ocean" width="600" height="400">
Both width and height are set to integers representing pixel dimensions. This also lets the browser calculate a 3:2 aspect ratio to reserve space before the image loads.
✅ Fixed: omitting width and using CSS for responsive sizing
<img src="photo.jpg" alt="A sunset over the ocean" width="600" height="400" style="width: 100%; height: auto;">
Here, the HTML attributes still declare the image’s natural dimensions (for aspect-ratio calculation), while CSS overrides the rendered size to make the image responsive. The height: auto in CSS ensures the aspect ratio is preserved — this is the CSS equivalent of the "auto" behavior you may have been looking for.
✅ Fixed: omitting both attributes entirely
<img src="photo.jpg" alt="A sunset over the ocean">
If you don’t specify width or height, the browser renders the image at its native size. This is valid, though you lose the layout-shift prevention benefit.
Recommended pattern for responsive images
For the best combination of validity, performance, and responsiveness, include the pixel dimensions in HTML and apply responsive styles via CSS:
<img
src="photo.jpg"
alt="A sunset over the ocean"
width="1200"
height="800"
style="max-width: 100%; height: auto;">
This approach tells the browser the image’s intrinsic aspect ratio (via width and height), prevents layout shifts, and allows the image to scale down gracefully within its container.
An empty sizes attribute on an img element is invalid; the attribute must contain a valid value or be omitted.
The sizes attribute specifies the slot width that the browser should use for selecting the appropriate image from those available in srcset. It should only be used when the srcset attribute is present. An empty string is not a valid value—if you do not have any sizes to specify, the attribute should be removed entirely.
Correct usage:
- Remove the empty sizes and srcset attributes if not needed.
- If specifying, provide valid values such as "100vw" or "(max-width: 600px) 100vw, 50vw".
Incorrect example:
<img src="photo.jpg" srcset="photo-small.jpg 480w, photo-large.jpg 1200w" sizes="" alt="">
Corrected example (with a valid sizes value):
<img src="photo.jpg" srcset="photo-small.jpg 480w, photo-large.jpg 1200w" sizes="100vw" alt="">
Square brackets in an img src query string must be percent-encoded to be valid.
The src attribute on img must be a valid URL. In URL query strings, characters like [ and ] are not allowed unescaped per URL syntax. When present (often from frameworks adding array-like params), they must be percent-encoded as [ -> %5B and ] -> %5D. Alternatively, remove brackets from the query or use a server-side/route format that avoids them.
HTML examples
Example causing the validator error
<img src="/images/photo.jpg?size[width]=300&size[height]=200" alt="Sample">
Fixed example using percent-encoding
<img src="/images/photo.jpg?size%5Bwidth%5D=300&size%5Bheight%5D=200" alt="Sample">
Fixed example by avoiding brackets in params
<img src="/images/photo.jpg?size_width=300&size_height=200" alt="Sample">
The W3C HTML validator raises this error when the src attribute of an <img> element contains characters that are not permitted in a valid URI. The most common culprit is the < character, but other characters like >, {, }, |, \, ^, and backticks are also illegal in URIs according to RFC 3986.
This issue typically occurs in a few common scenarios:
- Template syntax left unresolved: Server-side or client-side template tags (e.g., <%= imageUrl %>, {{ image.src }}) appear literally in the HTML output instead of being processed into actual URLs.
- Copy-paste errors: HTML markup or angle brackets accidentally end up inside a src value.
- Malformed dynamic URLs: JavaScript or server-side code incorrectly constructs a URL that includes raw HTML or special characters.
This matters because browsers may fail to load the image or interpret the URL unpredictably. Invalid URIs can also cause issues with screen readers and assistive technologies that try to resolve the src to provide context about the image. Keeping your markup standards-compliant ensures consistent behavior across all browsers and environments.
How to fix it
- Check for unprocessed template tags. If you see template syntax like <%, {{, or similar in the rendered HTML, ensure your templating engine is running correctly and outputting the resolved URL.
- Use valid, well-formed URLs. The src value should be a properly formatted absolute or relative URL.
- Percent-encode special characters. If a special character is genuinely part of the URL (which is rare for <), encode it: < becomes %3C, > becomes %3E, and so on.
- Inspect your generated HTML. View the page source in your browser to confirm the actual output, rather than relying on what your code looks like before processing.
Examples
Incorrect — template syntax in src
The template tag was not processed, leaving a < character in the src attribute:
<img src="<%= user.avatar %>" alt="User avatar">
Incorrect — HTML accidentally inside src
Angle brackets from stray markup ended up in the URL:
<img src="images/<thumbnail>/photo.jpg" alt="Photo">
Correct — a valid relative URL
<img src="images/photo.jpg" alt="Photo">
Correct — a valid absolute URL
<img src="https://example.com/images/photo.jpg" alt="Photo">
Correct — special characters percent-encoded
If the URL genuinely requires characters that are not allowed in a URI, percent-encode them:
<img src="https://example.com/search?q=a%3Cb" alt="Search result">
In this case, %3C represents the < character in the query string, making the URI valid.
The HTML specification requires the src attribute of an <img> element to be a valid, non-empty URL. When you set src="", the browser has no resource to fetch, but many browsers will still attempt to make a request — often resolving the empty string relative to the current page URL. This means the browser may re-request the current HTML document as if it were an image, wasting bandwidth and potentially causing unexpected server-side behavior.
Beyond the technical waste, an empty src is problematic for accessibility. Screen readers rely on the <img> element to convey meaningful content. An image with no source provides no value and can confuse assistive technology users. Search engine crawlers may also flag this as a broken resource, negatively affecting SEO.
This issue commonly arises in a few scenarios:
- Placeholder images — developers leave src empty intending to set it later via JavaScript.
- Template rendering — a server-side template or frontend framework outputs an <img> tag before the image URL is available.
- Lazy loading implementations — the real source is stored in a data-src attribute while src is left empty.
How to fix it
The simplest fix is to provide a valid image URL in the src attribute. If the image source isn’t available yet, consider these alternatives:
- Don’t render the <img> element at all until a valid source is available.
- Use a small placeholder image (such as a transparent 1×1 pixel GIF or a lightweight SVG) as a temporary src.
- Use native lazy loading with loading="lazy" and a real src, letting the browser handle deferred loading instead of relying on an empty src.
Examples
❌ Bad: empty src attribute
<img src="" alt="Profile photo">
This triggers the validation error because the src value is an empty string.
❌ Bad: src with only whitespace
<img src=" " alt="Profile photo">
Whitespace-only values are also considered invalid and will produce a similar error.
✅ Good: valid image path
<img src="photo.jpg" alt="Profile photo">
✅ Good: placeholder image for lazy loading
If you’re implementing lazy loading and need a lightweight placeholder, use a small inline data URI or a real placeholder file:
<img
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
data-src="photo.jpg"
alt="Profile photo">
✅ Good: native lazy loading with a real src
Modern browsers support the loading attribute, eliminating the need for an empty src workaround:
<img src="photo.jpg" alt="Profile photo" loading="lazy">
✅ Good: conditionally render the element
If the image URL might not be available, avoid rendering the <img> tag entirely. For example, in a template:
<!-- Only include the img element when a source exists -->
<img src="photo.jpg" alt="Profile photo">
In frameworks like React, Vue, or server-side templating engines, use conditional logic to skip the <img> element when the URL is empty rather than outputting a tag with an empty src.
Replace square brackets in srcset URLs or percent-encode them.
The img element’s srcset expects valid URLs for each image candidate. According to the URL Standard, unescaped square brackets are not allowed in the path or query of an HTTP(S) URL used in HTML attributes like srcset. They must be either removed, replaced, or percent-encoded.
- Use safe characters in query parameters (e.g., hyphens or underscores instead of brackets).
- If brackets must remain for backend reasons, percent-encode them: [ -> %5B, ] -> %5D.
- Ensure each image candidate follows the URL [whitespace] descriptor pattern (e.g., 2x, 300w) with commas separating candidates.
HTML examples
Example causing the error
<img
src="image.jpg"
srcset="image.jpg?size=[small] 1x, image@2x.jpg?size=[large] 2x"
alt="Sample">
Corrected example (encode brackets)
<img
src="image.jpg"
srcset="image.jpg?size=%5Bsmall%5D 1x, image@2x.jpg?size=%5Blarge%5D 2x"
alt="Sample">
Corrected example (avoid brackets)
<img
src="image.jpg"
srcset="image.jpg?size=small 1x, image@2x.jpg?size=large 2x"
alt="Sample">
srcset contains candidates without a width descriptor while sizes is present, so each candidate must use a width (w) descriptor.
When an img has sizes, every srcset candidate must include a width descriptor like 320w, not a pixel density descriptor like 1x. Mixed descriptors are not allowed in the same srcset. Use either:
- Width descriptors with sizes (e.g., 320w, 640w, 1024w)
- Density descriptors without sizes (e.g., 1x, 2x)
The browser uses sizes to map CSS layout width to the best w candidate. Without sizes, density (x) can be used, but not together with sizes.
HTML examples
Reproduce the issue (invalid: sizes + x descriptors)
<img
src="photo-640.jpg"
srcset="photo-640.jpg 1x, photo-1280.jpg 2x"
sizes="(max-width: 600px) 100vw, 600px"
alt="Sample photo">
Fix using width descriptors with sizes (valid)
<img
src="photo-640.jpg"
srcset="photo-320.jpg 320w, photo-640.jpg 640w, photo-1280.jpg 1280w"
sizes="(max-width: 600px) 100vw, 600px"
alt="Sample photo">
Alternative fix: remove sizes and use density descriptors (valid)
<img
src="photo-640.jpg"
srcset="photo-640.jpg 1x, photo-1280.jpg 2x"
alt="Sample photo">
The HTML specification requires that the width and height attributes on <img> elements, when present, contain a string representing a non-negative integer — that is, a sequence of one or more ASCII digits like "0", "150", or "1920". An empty string ("") does not satisfy this requirement, so the W3C validator flags it as an error.
This issue commonly arises when:
- A CMS or templating engine outputs width="" or height="" because no dimension value was configured.
- JavaScript dynamically sets img.setAttribute("width", "") instead of removing the attribute.
- A developer adds the attributes as placeholders intending to fill them in later but forgets to do so.
Why it matters
Providing valid width and height attributes is one of the most effective ways to prevent Cumulative Layout Shift (CLS). Browsers use these values to calculate the image’s aspect ratio and reserve the correct amount of space before the image loads. When the values are empty strings, the browser cannot determine the aspect ratio, so no space is reserved — leading to layout shifts as images load in, which hurts both user experience and Core Web Vitals scores.
Beyond performance, invalid attribute values can cause unpredictable rendering behavior across browsers. Some browsers may ignore the attribute, others may interpret the empty string as 0, collapsing the image to zero pixels in that dimension. Standards-compliant HTML also improves accessibility by ensuring assistive technologies can parse the document reliably.
Examples
❌ Invalid: empty string values
<img src="photo.jpg" alt="A sunset over the ocean" width="" height="">
Both width and height are set to empty strings, which is not valid.
✅ Fixed: provide actual dimensions
<img src="photo.jpg" alt="A sunset over the ocean" width="800" height="600">
Replace the empty strings with the image’s actual pixel dimensions. These values should reflect the image’s intrinsic (natural) size. CSS can still be used to scale the image visually — the browser will use the width and height ratio to reserve the correct space.
✅ Fixed: remove the attributes entirely
<img src="photo.jpg" alt="A sunset over the ocean">
If you don’t know the dimensions or prefer to handle sizing purely through CSS, remove the attributes altogether. An absent attribute is valid; an empty one is not.
❌ Invalid: only one attribute is empty
<img src="banner.jpg" alt="Promotional banner" width="1200" height="">
Even if only one attribute has an empty value, the validation error will be triggered for that attribute.
✅ Fixed: both attributes with valid values
<img src="banner.jpg" alt="Promotional banner" width="1200" height="400">
Fixing dynamic/template-generated markup
If a template language is outputting empty attributes, use a conditional to omit them when no value is available. For example, in a template:
<!-- Instead of always outputting the attributes: -->
<img src="photo.jpg" alt="Description" width="" height="">
<!-- Conditionally include them only when values exist: -->
<img src="photo.jpg" alt="Description" width="800" height="600">
If you’re setting dimensions via JavaScript, remove the attribute rather than setting it to an empty string:
// ❌ Don't do this
img.setAttribute("width", "");
// ✅ Do this instead
img.removeAttribute("width");
// ✅ Or set a valid value
img.setAttribute("width", "800");
A note on values
The width and height attributes only accept non-negative integers — whole numbers without units, decimals, or percentage signs. Values like "100px", "50%", or "3.5" are also invalid. Use plain integers like "100" or "600". If you need responsive sizing with percentages or other CSS units, apply those through CSS styles instead.
Use role="none" (or remove the role) instead of role="presentation" on an img.
The role attribute maps elements to ARIA roles. For images, the valid way to make an image decorative is to either omit the ARIA role and use an empty alt attribute (alt=""), or use the ARIA role none. In ARIA 1.1, role="none" and role="presentation" are equivalent, but the W3C HTML Validator flags role="presentation" on img because the correct, accessible pattern is an empty alt to hide the image from assistive tech. Use alt="" alone, or pair it with role="none" if you need an explicit ARIA role. If the image conveys meaning, provide a descriptive alt and no role.
HTML Examples
Example that reproduces the issue
<img src="avatar.png" alt="" role="presentation">
Fixed examples
Decorative image (preferred minimal fix):
<img src="avatar.png" alt="">
Informative image:
<img src="chart.png" alt="Quarterly sales trend line chart">
The loading attribute on the img element only accepts the values eager or lazy.
The loading attribute is used to specify lazy loading behavior for images and only accepts the values "lazy" or "eager" (which is the default).
Explanation:
- The loading attribute hints to the browser whether to load the image immediately ("eager"), defer until it reaches a calculated distance from the viewport ("lazy").
- Using values other than "lazy", "eager" results in validation errors and browsers ignore the attribute.
- Never use numeric values or case-variants; always use the lower-case string values above.
Incorrect Example:
<img src="logo.png" alt="Company logo" loading="1">
Correct Example:
<img src="logo.png" alt="Company logo" loading="lazy">
Replace the incorrect value with loading="lazy" or loading="eager", or remove the attribute, to resolve the validator issue.
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 auto with 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 with loading="lazy". If you want the browser to automatically determine the size, ensure you also include loading="lazy" and a width attribute on the image. Note that some validators may still flag this until they update their rules.
-
Remove auto from a comma-separated list. If you have something like sizes="auto, (max-width: 600px) 100vw, 50vw", remove the auto entry 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"
>
sizes contains an invalid media condition; the value must be a comma-separated list of media conditions with corresponding slot sizes, ending with an optional fallback length.
Detailed explanation:
- The sizes attribute on img pairs a media condition with a slot size that represents the layout width of the image for that condition. Syntax: (<media-condition>) <length>[, ...], <length> where the last item can be a bare length fallback.
- A media condition uses the same grammar as CSS media queries. It must be valid CSS, e.g., (min-width: 600px) or (width <= 50rem) and (orientation: landscape). Each condition must be enclosed in parentheses unless using logical operators combining proper conditions.
Common parse errors:
- Missing parentheses: use (min-width: 600px), not min-width: 600px.
- Invalid units or tokens: use px, em, rem, vw, etc.; avoid % in media conditions.
- Missing slot size after a condition: (min-width: 600px) must be followed by a length like 600px.
- Using px only for slot size without units or using percentages: slot size must be a length like 300px, 50vw, not 300.
- Trailing comma or extra commas.
- Misusing comparison syntax: use modern range syntax like (600px <= width <= 1000px) or the traditional form (min-width: 600px) and (max-width: 1000px). Do not write (min-width <= 600px).
- Slot sizes must be lengths: px, em, rem, vw, vh, vmin, vmax, ch. Percentages are not allowed in sizes slot sizes.
- The srcset widths (w descriptors) must correspond to the intrinsic widths of the image candidates, e.g., 400w, 800w. The browser picks one based on sizes.
HTML examples:
-
Correct usage with media conditions and fallback:
<img src="image-800.jpg" srcset="image-400.jpg 400w, image-800.jpg 800w, image-1200.jpg 1200w" sizes="(min-width: 900px) 50vw, (min-width: 600px) 66vw, 100vw" alt="Decorative pattern"> -
Using range syntax and avoiding common mistakes:
<img src="hero-1600.jpg" srcset="hero-800.jpg 800w, hero-1200.jpg 1200w, hero-1600.jpg 1600w" sizes="(800px <= width < 1200px) 80vw, (width >= 1200px) 50vw, 100vw" alt="Hero banner"> -
Minimal fixed example for a typical error (missing parentheses and slot size): Incorrect:
<img src="pic-800.jpg" srcset="pic-400.jpg 400w, pic-800.jpg 800w" sizes="min-width: 600px, 100vw" alt="Sample">Correct:
<img src="pic-800.jpg" srcset="pic-400.jpg 400w, pic-800.jpg 800w" sizes="(min-width: 600px) 50vw, 100vw" alt="Sample"> -
Example avoiding invalid tokens and commas:
<img src="avatar-256.png" srcset="avatar-128.png 128w, avatar-256.png 256w" sizes="(orientation: landscape) 30vw, 50vw" alt="User avatar">
The sizes attribute contains an empty source size, probably due to a trailing comma.
According to the HTML standard, the sizes attribute on the img element must contain a valid source size list. Each size is separated by a comma, and there must not be a trailing comma because that would create an empty entry, which is not allowed.
An example of an invalid sizes attribute:
<img
src="image.jpg"
alt=""
sizes="(min-width: 2200px) 100vw, (min-width: 856px) 461px, (min-width: 784px) 615px, "
srcset="image-2200.jpg 2200w, image-856.jpg 856w">
To fix the validation error, remove the trailing comma so the size list does not end with an empty value:
<img
src="image.jpg"
alt=""
sizes="(min-width: 2200px) 100vw, (min-width: 856px) 461px, (min-width: 784px) 615px"
srcset="image-2200.jpg 2200w, image-856.jpg 856w">
Each value in the sizes attribute should be a valid media condition and source size, separated only by commas, with no trailing or empty values.
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 w descriptor belongs only in srcset, where it describes the intrinsic width of each image file.
- The sizes attribute 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.
Invalid sizes syntax in the <img> sizes attribute causes the validator error; each size value must be a valid CSS length with units and optional media condition.
Detailed explanation
The sizes attribute on responsive images (used with srcset) tells the browser how wide the image will be in CSS pixels under certain media conditions so it can choose the best candidate from srcset. It must be a comma-separated list of one or more size descriptors:
- Optional media condition followed by a length: (<media-condition>) <length>
- Or a final fallback length without media condition: <length>
Valid length units include: em, ex, ch, rem, cap, ic, vw, svw, lvw, dvw, vh, svh, lvh, dvh, vi, svi, lvi, dvi, vb, svb, lvb, dvb, vmin, svmin, lvmin, dvmin, vmax, svmax, lvmax, dvmax, cm, mm, q, in, pc, pt, px. Percentages are not allowed in sizes. Bare numbers without units are invalid. The media condition must be valid per CSS Media Queries (e.g., (max-width: 600px)), and each list item must have exactly one length value.
Common mistakes that trigger this error:
- Missing units: sizes="(max-width: 600px) 100" → must be 100px or another unit.
- Using %: sizes="(max-width: 600px) 100%" → percentages are invalid in sizes.
- Multiple lengths per item: sizes="(min-width: 800px) 50vw 400px" → only one length per item.
- Typos in units: 100 pxx, 100vws.
- Missing fallback: while not strictly required if all conditions cover all cases, providing a final fallback length avoids unexpected behavior.
Ensure sizes pairs correctly with srcset; srcset provides image candidates with width descriptors (w), and sizes expresses the slot width the image will occupy.
HTML examples
Correct usage with media conditions and fallback
<img
src="img-400.jpg"
srcset="img-400.jpg 400w, img-800.jpg 800w, img-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw, (max-width: 900px) 50vw, 33vw"
alt="Sample image">
Fixing missing units (was 100 -> 100px)
<img
src="avatar-200.jpg"
srcset="avatar-200.jpg 200w, avatar-400.jpg 400w"
sizes="(max-width: 480px) 100vw, 200px"
alt="User avatar">
Avoiding invalid percentages (use vw instead)
<img
src="banner-800.jpg"
srcset="banner-800.jpg 800w, banner-1600.jpg 1600w"
sizes="(max-width: 700px) 100vw, 80vw"
alt="Promotional banner">
Single unconditional size (no media condition)
<img
src="thumb-320.jpg"
srcset="thumb-320.jpg 320w, thumb-640.jpg 640w"
sizes="320px"
alt="Thumbnail">
Correct srcset with sizes alignment
<img
src="photo-640.jpg"
srcset="photo-640.jpg 640w, photo-960.jpg 960w, photo-1280.jpg 1280w"
sizes="(min-width: 1200px) 800px, (min-width: 800px) 60vw, 90vw"
alt="Landscape photo">
If the validator pinpoints a position (Z), check that token for a missing or invalid unit, extra tokens after the length, or malformed media condition.
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
<img src="images\photos\landscape.jpg" alt="Mountain landscape">
<img src="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
<img src="images/photos/landscape.jpg" alt="Mountain landscape">
<img src="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
<img src="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
<img src="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 \ within src attributes 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 url module or template literals with / instead of path.join(), which uses \ on Windows.
- Run the W3C validator regularly during development to catch issues like this before deployment.
URLs follow strict syntax rules defined by RFC 3986. Within a URL’s path segment, only a specific set of characters is allowed to appear literally. When a character falls outside this allowed set, it must be percent-encoded — represented as a % sign followed by two hexadecimal digits corresponding to the character’s ASCII code. The W3C validator checks that every URL in your HTML conforms to these rules, and it flags any src value that contains raw illegal characters.
Characters that commonly trigger this error include:
| Character | Percent-encoded |
|---|---|
| (space) | %20 |
| [ | %5B |
| ] | %5D |
| { | %7B |
| } | %7D |
| | | %7C |
| ^ | %5E |
| ` | %60 |
Other reserved characters like ?, #, @, !, $, &, ', (, ), *, +, ,, ;, and = also need encoding when used as literal data in a path segment rather than as URL delimiters. The % character itself must be encoded as %25 if it appears literally.
Why This Is a Problem
- Browser inconsistency: While many modern browsers silently fix malformed URLs, not all do. Some browsers, older user agents, or HTTP clients may fail to load the resource or interpret the URL differently, leading to broken images.
- Standards compliance: Invalid URLs violate the HTML specification, which requires that attribute values containing URLs conform to valid URL syntax.
- Interoperability: Servers, CDNs, proxies, and caching layers may handle illegal characters unpredictably, causing intermittent failures that are difficult to debug.
- Accessibility: If a URL is malformed and the image fails to load, users relying on assistive technologies may not receive the intended content, even when appropriate alt text is provided.
How to Fix It
You have two main approaches:
- Percent-encode the illegal characters in the src value. Replace each offending character with its %XX equivalent.
- Rename the file to use only URL-safe characters. Stick to lowercase letters, digits, hyphens (-), underscores (_), and dots (.). This is the cleanest long-term solution.
If you’re generating URLs programmatically, use your language’s built-in URL encoding functions (e.g., encodeURIComponent() in JavaScript, urlencode() in PHP, or urllib.parse.quote() in Python).
Examples
Illegal characters in the filename
The square brackets in the src value are not allowed in a URL path segment:
<!-- ❌ Invalid: raw [ and ] in URL path -->
<img src="image[00].svg" alt="Company logo">
Fix by percent-encoding:
<!-- ✅ Valid: [ and ] are percent-encoded -->
<img src="image%5B00%5D.svg" alt="Company logo">
Fix by renaming the file:
<!-- ✅ Valid: filename uses only safe characters -->
<img src="image-00.svg" alt="Company logo">
Spaces in the filename
Spaces are one of the most common causes of this error:
<!-- ❌ Invalid: space in URL path -->
<img src="my photo.jpg" alt="Vacation photo">
<!-- ✅ Valid: space encoded as %20 -->
<img src="my%20photo.jpg" alt="Vacation photo">
<!-- ✅ Valid: filename uses a hyphen instead of a space -->
<img src="my-photo.jpg" alt="Vacation photo">
Curly braces in a template-like path
Sometimes filenames or paths contain curly braces from templating artifacts or naming conventions:
<!-- ❌ Invalid: raw { and } in URL path -->
<img src="icons/{home}.png" alt="Home icon">
<!-- ✅ Valid: curly braces percent-encoded -->
<img src="icons/%7Bhome%7D.png" alt="Home icon">
Best practice for file naming
The simplest way to avoid this error entirely is to adopt a consistent file naming convention that only uses URL-safe characters:
<!-- ✅ Valid: clean, URL-safe filenames -->
<img src="images/hero-banner-2024.webp" alt="Welcome banner">
<img src="photos/team_photo_01.jpg" alt="Our team">
URLs follow strict syntax rules defined by RFC 3986, which does not allow literal space characters in any part of a URL — whether in the path, query string, or fragment. While many browsers will silently handle spaces by encoding them before making a request, the raw HTML is still invalid. The W3C HTML validator flags this because the src attribute expects a valid URL, and a URL containing a raw space does not conform to the standard.
This issue commonly appears in two scenarios: spaces in file paths (e.g., my image.jpg) and spaces in query string values (e.g., ?search=my term). Both must be percent-encoded. The percent-encoded form of a space is %20. In query strings specifically, you may also see + used to represent spaces (as defined by application/x-www-form-urlencoded), which is also valid in that context.
Beyond standards compliance, raw spaces in URLs can cause real problems. Some older browsers or HTTP clients may truncate the URL at the first space, leading to broken images or failed resource loads. Spaces can also cause issues with link sharing, copy-pasting, and server-side URL parsing. Proper encoding ensures your URLs work reliably across all environments.
How to fix it
- Replace spaces with %20 in all parts of the URL. This is the universally safe approach.
- Rename files to avoid spaces altogether. Use hyphens (-) or underscores (_) instead of spaces in file and directory names.
- Use + in query strings if you prefer, though %20 works everywhere in a URL.
If you’re generating URLs programmatically, use built-in encoding functions like JavaScript’s encodeURI() or encodeURIComponent() to handle this automatically.
Examples
Spaces in the file path
This triggers the validation error because the file name contains a space:
<!-- ❌ Invalid: space in path segment -->
<img src="/images/my photo.jpg" alt="A vacation photo">
Fix it by encoding the space:
<!-- ✅ Valid: space encoded as %20 -->
<img src="/images/my%20photo.jpg" alt="A vacation photo">
Or better yet, rename the file to avoid spaces:
<!-- ✅ Valid: no spaces in file name -->
<img src="/images/my-photo.jpg" alt="A vacation photo">
Spaces in the query string
This triggers the error because the query parameter value contains a space:
<!-- ❌ Invalid: space in query string -->
<img src="https://example.com/image?title=sunset beach" alt="Sunset at the beach">
Fix by percent-encoding the space:
<!-- ✅ Valid: space encoded as %20 -->
<img src="https://example.com/image?title=sunset%20beach" alt="Sunset at the beach">
Using + is also acceptable in query strings:
<!-- ✅ Valid: space encoded as + in query string -->
<img src="https://example.com/image?title=sunset+beach" alt="Sunset at the beach">
Multiple spaces in a URL
When a URL has multiple spaces, each one must be encoded:
<!-- ❌ Invalid: multiple spaces -->
<img src="/uploads/user photos/trip to paris.jpg" alt="Trip to Paris">
<!-- ✅ Valid: all spaces encoded -->
<img src="/uploads/user%20photos/trip%20to%20paris.jpg" alt="Trip to Paris">
URLs follow a strict syntax defined by the URL Living Standard and RFC 3986. Only a specific set of characters are allowed to appear literally in a URL’s query string. Characters outside this set — such as pipes, square brackets, curly braces, and certain other symbols — must be percent-encoded. Percent-encoding replaces the character with a % sign followed by its two-digit hexadecimal ASCII code.
When the W3C HTML Validator encounters an <img> tag whose src attribute contains an illegal character in the query portion of the URL, it raises this error. The query string is the part of the URL that comes after the ? character.
Why this matters
- Browser inconsistency: While many modern browsers will silently fix malformed URLs, not all browsers or HTTP clients handle illegal characters the same way. Some may misinterpret the URL or fail to load the resource entirely.
- Standards compliance: Valid URLs are a foundational requirement for interoperable web content. Using illegal characters violates both the HTML and URL specifications.
- Interoperability: Automated tools, web crawlers, proxies, and content delivery networks may reject or mangle URLs containing unencoded special characters, leading to broken images.
- Accessibility: Screen readers and assistive technologies rely on valid markup. Malformed URLs can cause unexpected behavior in these tools.
Common illegal characters and their encodings
Here are characters frequently flagged by the validator:
| Character | Percent-encoded |
|---|---|
| | (pipe) | %7C |
| [ | %5B |
| ] | %5D |
| { | %7B |
| } | %7D |
| ^ | %5E |
| ` (backtick) | %60 |
| (space) | %20 |
How to fix it
Identify the illegal characters in the src URL and replace each one with its corresponding percent-encoded value. If you’re generating URLs dynamically with a programming language, use the language’s built-in URL-encoding function (e.g., encodeURI() or encodeURIComponent() in JavaScript, urlencode() in PHP, urllib.parse.quote() in Python).
Examples
❌ Incorrect: unencoded pipe character in query string
<img src="https://example.com/image?filter=red|blue" alt="Filtered image">
The | character is not allowed literally in the query string.
✅ Correct: pipe character percent-encoded
<img src="https://example.com/image?filter=red%7Cblue" alt="Filtered image">
❌ Incorrect: unencoded square brackets in query string
<img src="https://example.com/image?size[width]=300&size[height]=200" alt="Resized image">
The [ and ] characters must be encoded.
✅ Correct: square brackets percent-encoded
<img src="https://example.com/image?size%5Bwidth%5D=300&size%5Bheight%5D=200" alt="Resized image">
❌ Incorrect: space in query string
<img src="https://example.com/image?caption=hello world" alt="Captioned image">
✅ Correct: space percent-encoded
<img src="https://example.com/image?caption=hello%20world" alt="Captioned image">
Encoding URLs dynamically
If your URLs are built in JavaScript, use encodeURIComponent() for individual query parameter values:
const filter = "red|blue";
const url = `https://example.com/image?filter=${encodeURIComponent(filter)}`;
// Result: "https://example.com/image?filter=red%7Cblue"
This ensures that any special characters in user-provided or dynamic values are properly encoded before being placed into the HTML.
Ready to validate your sites?
Start your free trial today.