HTML Guides
Learn how to identify and fix common HTML validation errors flagged by the W3C Validator — so your pages are standards-compliant and render correctly across every browser. Also check our Accessibility Guides.
The <link> element is used to define relationships between the current document and external resources — most commonly stylesheets, icons, and preloaded assets. The href attribute specifies the URL of that external resource, and it is the core purpose of the element. An empty href attribute makes the <link> element meaningless because there is no resource to fetch or reference.
Why This Is a Problem
Standards compliance: The HTML specification requires the href attribute on <link> to be a valid, non-empty URL. An empty string does not qualify as a valid URL, so the validator flags it as an error.
Unexpected browser behavior: When a browser encounters an empty href, it may resolve it relative to the current document’s URL. This means the browser could end up making an unnecessary HTTP request for the current page itself, interpreting the HTML response as a stylesheet or other resource. This wastes bandwidth, can slow down page loading, and may trigger unexpected rendering issues.
Accessibility and semantics: An empty href provides no useful information to browsers, screen readers, or other user agents about the relationship between the document and an external resource. It adds noise to the DOM without contributing anything functional.
How to Fix It
- Provide a valid URL: If the <link> element is meant to reference a resource, set href to the correct URL of that resource.
- Remove the element: If no resource is needed, remove the entire <link> element rather than leaving it with an empty href.
- Check dynamic rendering: This issue often occurs when a templating engine or CMS outputs a <link> element with a variable that resolves to an empty string. Add a conditional check so the element is only rendered when a valid URL is available.
Examples
❌ Incorrect: Empty href attribute
<link rel="stylesheet" href="">
This triggers the validation error because href is empty.
❌ Incorrect: Empty href from a template
<!-- A template variable resolved to an empty string -->
<link rel="icon" type="image/png" href="">
✅ Correct: Valid href pointing to a resource
<link rel="stylesheet" href="/css/main.css">
✅ Correct: Valid href for a favicon
<link rel="icon" type="image/png" href="/images/favicon.png">
✅ Correct: Remove the element if no resource is needed
<!-- Simply omit the <link> element entirely -->
✅ Correct: Conditional rendering in a template
If you’re using a templating language, wrap the <link> in a conditional so it only renders when a URL is available. For example, in a Jinja2-style template:
{% if stylesheet_url %}
<link rel="stylesheet" href="{{ stylesheet_url }}">
{% endif %}
This ensures the <link> element is never output with an empty href.
In XML-based formats like XHTML 1.0 or other XML vocabularies, namespace declarations such as xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" were used to associate element or attribute prefixes with specific namespace URIs. This allowed different XML vocabularies to coexist in a single document without naming conflicts. The xmlns:dt namespace in particular was commonly associated with Microsoft’s XML data types, often seen in legacy ASP or older Microsoft-generated HTML.
HTML5, however, does not use the XML namespace mechanism. The HTML parser treats the document as HTML, not XML, and does not recognize or process custom namespace prefixes. The only xmlns-related attributes permitted in HTML5 are:
- xmlns on the <html> element, but only with the value http://www.w3.org/1999/xhtml (for compatibility with XHTML-serving scenarios).
- Implicit namespace handling for embedded <svg> and <math> elements, which the HTML parser manages automatically.
Any other xmlns:* attribute — such as xmlns:dt, xmlns:o, xmlns:v, or xmlns:st1 — is invalid in HTML5 and will trigger the W3C validator error: “Attribute with the local name ‘xmlns:dt’ is not serializable as XML 1.0.”
Why this is a problem
- Standards compliance: Custom XML namespace attributes violate the HTML5 specification. The HTML parser does not process them, so they serve no functional purpose.
- Serialization issues: If the document is ever round-tripped through an XML serializer (for example, when converting HTML to XHTML), these attributes cannot be properly serialized under XML 1.0 rules, potentially causing parsing failures.
- Legacy baggage: These attributes typically appear in documents generated by older tools (such as Microsoft Word’s “Save as HTML” feature) and carry over data-type or Office-specific namespace declarations that are meaningless in a modern web context.
- Document bloat: Keeping unused namespace declarations adds unnecessary bytes to your document without any benefit.
How to fix it
- Search your HTML for any attributes starting with xmlns: on the <html> element or elsewhere in the document.
- Remove them entirely. If your code relied on XML data types or Office-specific features tied to these namespaces, you’ll need to refactor that logic using standard HTML5 attributes (such as data-* attributes) or JavaScript.
- Ensure your document uses a standard HTML5 <!DOCTYPE html> declaration and a clean <html> tag.
If you’re working with content pasted from Microsoft Word or similar tools, consider running it through an HTML cleaner to strip out all Office-specific markup.
Examples
Incorrect — custom xmlns:dt attribute
This triggers the validation error because xmlns:dt is not a valid attribute in HTML5:
<html xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882">
<head>
<title>My Page</title>
</head>
<body>
<p>Hello world</p>
</body>
</html>
Incorrect — multiple custom namespace declarations
Documents exported from older tools often include several invalid namespace attributes at once:
<html xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<title>My Page</title>
</head>
<body>
<p>Hello world</p>
</body>
</html>
Correct — clean HTML5 document
Remove all custom xmlns:* attributes and use a standard HTML5 structure:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Page</title>
</head>
<body>
<p>Hello world</p>
</body>
</html>
Correct — using data attributes as a replacement
If you previously relied on namespace-prefixed attributes to store custom data on elements, use data-* attributes instead:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Page</title>
</head>
<body>
<p data-type="datetime">2024-01-15</p>
</body>
</html>
The xmlns attribute defines the XML namespace for an element. For SVG, the correct namespace is http://www.w3.org/2000/svg, declared with xmlns="http://www.w3.org/2000/svg". The xmlns:svg attribute attempts to declare an additional prefixed namespace binding — essentially mapping the prefix svg: to the same namespace URI. This is redundant because the default (unprefixed) namespace already covers all SVG elements.
In HTML5, the parser handles namespaces internally. The HTML specification only permits a small set of namespace attributes: xmlns on certain elements (like <svg> and <math>) and xmlns:xlink for legacy compatibility. Arbitrary prefixed namespace declarations like xmlns:svg are not part of the HTML serialization format. The W3C validator raises this error because attributes containing colons in their local names (other than the specifically allowed ones) cannot be round-tripped through the HTML parser and serializer — they are “not serializable as XML 1.0.”
Why this matters
- Standards compliance: HTML5 has strict rules about which namespace declarations are permitted. Using xmlns:svg violates these rules.
- Serialization issues: If a browser parses the HTML and then re-serializes it (e.g., via innerHTML), the xmlns:svg attribute may be lost, altered, or cause unexpected behavior because it falls outside the serializable attribute set.
- Redundancy: Even in pure XML/SVG documents, declaring xmlns:svg="http://www.w3.org/2000/svg" alongside xmlns="http://www.w3.org/2000/svg" is unnecessary. The default namespace already applies to the <svg> element and all its unprefixed descendants.
How to fix it
- Locate the <svg> element (or any element) that contains the xmlns:svg attribute.
- Remove xmlns:svg="http://www.w3.org/2000/svg" entirely.
- Ensure the standard xmlns="http://www.w3.org/2000/svg" attribute remains if needed (note that when embedding SVG inline in an HTML5 document, even xmlns is optional since the HTML parser infers the namespace automatically).
Examples
Incorrect: redundant prefixed namespace declaration
<svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="blue" />
</svg>
The xmlns:svg attribute triggers the validation error because it is not serializable in HTML.
Correct: standard namespace only
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="blue" />
</svg>
Correct: inline SVG in HTML5 without any namespace attribute
When SVG is embedded directly in an HTML5 document, the HTML parser automatically assigns the correct namespace, so you can omit xmlns altogether:
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="blue" />
</svg>
This is perfectly valid and is the most common pattern for inline SVG in modern HTML. The xmlns attribute is only strictly necessary when the SVG is served as a standalone XML file (with a .svg extension or an image/svg+xml content type).
The W3C HTML Validator raises this error when it encounters a namespaced attribute such as xmlns:m, xmlns:o, xmlns:v, or any other xmlns:* declaration in an HTML document. These prefixed namespace bindings are an XML concept defined in the XML Namespaces specification. In the HTML syntax (documents served with the text/html MIME type), the HTML parser does not treat these as namespace declarations — it treats them as regular attributes with a colon in the name. Because such attribute names are not serializable in XML 1.0, the validator reports the error.
This issue is extremely common with content exported from Microsoft Office (Word, Excel, PowerPoint). When you save or copy Office content as HTML, the generated markup often includes namespace declarations like xmlns:m (Office Math Markup Language), xmlns:o (Office namespace), xmlns:v (VML), and xmlns:w (Word-specific markup). These declarations were designed for older, XML-based HTML rendering and serve no purpose in modern HTML5 documents.
Why This Is a Problem
- Standards compliance: The HTML5 specification only permits the xmlns attribute (without a prefix) on the <html> element, and only with the value http://www.w3.org/1999/xhtml. Prefixed forms like xmlns:m are not allowed.
- Serialization: If a tool attempts to serialize the DOM as XML (for example, XMLSerializer), attributes with colons that aren’t properly bound namespaces can cause failures or unexpected output.
- No functional benefit: In an HTML document, the browser’s HTML parser ignores these namespace bindings. They don’t enable any special behavior — they’re dead weight in your markup.
- Maintainability: Leaving Office-generated namespace clutter in your HTML makes the code harder to read and maintain.
How to Fix It
- Remove the xmlns:* attribute from your HTML element (or whichever element it appears on). In most cases, this is all you need to do — the namespaced content from Office isn’t rendered by browsers anyway.
- Clean up Office-generated HTML by stripping out all proprietary namespaces, conditional comments, and Office-specific elements. Tools like HTML Tidy or your editor’s “paste as plain text” feature can help.
- Use native HTML5 equivalents where possible. HTML5 natively supports MathML and SVG without requiring explicit namespace declarations.
- Switch to XHTML only if you have a genuine need for XML namespaces. This means serving the document with the application/xhtml+xml MIME type and using well-formed XML syntax throughout.
Examples
Incorrect: Office-generated namespace declarations
This markup, typical of content exported from Microsoft Word, triggers the validation error:
<!DOCTYPE html>
<html xmlns:m="http://schemas.microsoft.com/office/2004/12/omml"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<title>Office Export</title>
</head>
<body>
<p>Content from Word</p>
</body>
</html>
Correct: Clean HTML5 without namespace declarations
Remove all xmlns:* attributes and any associated Office-specific markup:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Office Export</title>
</head>
<body>
<p>Content from Word</p>
</body>
</html>
Incorrect: Single namespace on a non-html element
The error can also appear on other elements if a tool inserts namespace attributes:
<div xmlns:custom="http://example.com/ns">
<p>Some content</p>
</div>
Correct: Remove the namespace attribute
<div>
<p>Some content</p>
</div>
Using MathML natively in HTML5
If the xmlns:m attribute was added to support math content, note that HTML5 supports MathML directly without any namespace declaration:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>MathML in HTML5</title>
</head>
<body>
<p>The quadratic formula:</p>
<math>
<mi>x</mi>
<mo>=</mo>
<mfrac>
<mrow>
<mo>-</mo>
<mi>b</mi>
<mo>±</mo>
<msqrt>
<msup><mi>b</mi><mn>2</mn></msup>
<mo>-</mo>
<mn>4</mn><mi>a</mi><mi>c</mi>
</msqrt>
</mrow>
<mrow>
<mn>2</mn><mi>a</mi>
</mrow>
</mfrac>
</math>
</body>
</html>
No xmlns:m attribute is needed — the browser recognizes <math> as MathML automatically.
The src attribute on an <iframe> tells the browser which document to load and display within the embedded frame. When you leave this attribute empty (src=""), the W3C HTML Validator reports an error because the HTML specification requires the value to be a valid, non-empty URL. An empty string doesn’t resolve to a meaningful resource.
This is more than a cosmetic validation issue. When a browser encounters src="", it typically interprets the empty string as a relative URL pointing to the current page, which causes the browser to re-fetch and embed the current document inside itself. This leads to unnecessary network requests, potential performance degradation, and unexpected rendering behavior. In some cases it can even cause infinite nesting loops or break page functionality.
From an accessibility standpoint, an empty <iframe> with no valid source provides no meaningful content to assistive technologies. Screen readers may announce the frame without being able to describe its purpose, creating a confusing experience for users.
How to fix it
- Provide a valid URL: Set the src attribute to the actual URL of the content you want to embed.
- Use about:blank for intentionally empty frames: If you need an <iframe> in the DOM but don’t have content to load yet (e.g., you plan to populate it later via JavaScript), use src="about:blank". This is a valid URL that loads a blank page without triggering extra requests.
- Remove the element: If the <iframe> isn’t needed, remove it from the markup entirely. You can dynamically create and insert it with JavaScript when you have content to load.
- Use srcdoc instead: If you want to embed inline HTML rather than loading an external URL, use the srcdoc attribute, which accepts an HTML string directly.
Examples
❌ Empty src attribute
<iframe src=""></iframe>
This triggers the validation error because the src value is an empty string.
❌ src attribute with only whitespace
<iframe src=" "></iframe>
Whitespace-only values are also invalid URLs and will produce the same error.
✅ Valid URL in src
<iframe src="https://example.com/map.html" title="Location map"></iframe>
A fully qualified URL is the most straightforward fix. Note the title attribute, which is recommended for accessibility so that assistive technologies can describe the frame’s purpose.
✅ Using about:blank for a placeholder frame
<iframe src="about:blank" title="Dynamic content area"></iframe>
This is a valid approach when you need the <iframe> in the DOM but plan to set its content later with JavaScript using contentDocument.write() or by updating the src attribute dynamically.
✅ Using srcdoc for inline content
<iframe srcdoc="<p>Hello, embedded world!</p>" title="Inline content"></iframe>
The srcdoc attribute lets you embed HTML directly without needing an external URL. When srcdoc is present, it takes priority over src.
✅ Dynamically creating the iframe with JavaScript
<div id="video-container"></div>
<script>
const iframe = document.createElement("iframe");
iframe.src = "https://example.com/video.html";
iframe.title = "Video player";
document.getElementById("video-container").appendChild(iframe);
</script>
If the source URL isn’t available at page load, creating the <iframe> dynamically avoids having an empty src in your HTML altogether.
The background-blend-mode property controls how an element’s background layers — including background images and background colors — blend with each other. Each value must be a valid blend mode keyword as defined in the CSS Compositing and Blending specification. The W3C validator flags this error when it encounters a value that doesn’t match any recognized keyword, which can happen due to typos, made-up values, or confusion with similar properties like mix-blend-mode.
While browsers typically ignore unrecognized CSS values and fall back to the default (normal), relying on this behavior is risky. It means the blending effect you intended simply won’t appear, and the silent failure can be hard to debug. Fixing validation errors ensures your styles work as intended across all browsers.
The complete list of valid values for background-blend-mode is:
- normal (default)
- multiply
- screen
- overlay
- darken
- lighten
- color-dodge
- color-burn
- hard-light
- soft-light
- difference
- exclusion
- hue
- saturation
- color
- luminosity
You can also specify multiple comma-separated values when an element has multiple background layers. Each value corresponds to a background layer in the same order.
Examples
Invalid values
These examples will trigger the validation error:
/* Typo: "multipley" is not a valid keyword */
.hero {
background-blend-mode: multipley;
}
/* "blend" is not a recognized value */
.banner {
background-blend-mode: blend;
}
/* Numeric values are not accepted */
.card {
background-blend-mode: 50%;
}
Corrected values
/* Fixed: correct spelling */
.hero {
background-blend-mode: multiply;
}
/* Fixed: use a valid blend mode keyword */
.banner {
background-blend-mode: overlay;
}
/* Fixed: use a keyword instead of a numeric value */
.card {
background-blend-mode: soft-light;
}
Multiple background layers
When you have multiple background images, provide a comma-separated list of valid blend modes:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Background Blend Mode Example</title>
<style>
.blended {
width: 300px;
height: 200px;
background-color: teal;
background-image: url("pattern.png"), url("photo.jpg");
background-blend-mode: screen, multiply;
}
</style>
</head>
<body>
<div class="blended">Blended background layers</div>
</body>
</html>
In this example, screen applies to the first background image layer and multiply applies to the second. Both are valid keywords, so no validation error is produced.
The W3C HTML Validator raises this error because HTML5 does not support arbitrary XML namespace declarations. In XML, the xmlns: prefix is used to bind namespace prefixes to URIs, allowing elements and attributes from different vocabularies to coexist. However, HTML5 uses its own parsing rules that are distinct from XML, and the only namespace attribute recognized in HTML5 is the plain xmlns attribute on the <html> element (set to http://www.w3.org/1999/xhtml). Colonized namespace attributes like xmlns:o, xmlns:v, xmlns:w, and others are not part of the HTML5 specification.
The xmlns:o="urn:schemas-microsoft-com:office:office" namespace specifically comes from Microsoft Office. When you save a Word document as HTML or copy-paste content from Office applications into an HTML editor, Office injects its own namespace declarations and proprietary markup. This markup is intended for round-tripping the document back into Office and serves no purpose on the web.
Beyond validation, leaving these attributes in place can cause practical problems. The HTML5 parser in browsers silently ignores or misinterprets these namespace declarations, meaning they add dead weight to your markup. They also increase file size unnecessarily and can confuse other tools that process your HTML, such as screen readers, search engine crawlers, or content management systems.
How to Fix
- Remove the xmlns:o attribute from any element where it appears (typically the <html> tag).
- Remove related Office namespace attributes such as xmlns:v, xmlns:w, xmlns:x, and xmlns:st1, as these will trigger similar errors.
- Remove any elements or attributes using those namespace prefixes, such as <o:p>, <v:shape>, or <w:wrap>, since they are not valid HTML5 elements and browsers do not render them meaningfully.
- Clean up Office-generated HTML thoroughly if you’re converting Word documents to web content. Consider using a dedicated HTML cleaning tool or a paste-as-plain-text option in your editor.
Examples
Incorrect: Office namespace attributes on the <html> element
This markup contains multiple Microsoft Office namespace declarations that trigger validation errors:
<!DOCTYPE html>
<html xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:w="urn:schemas-microsoft-com:office:word"
xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<title>My Document</title>
</head>
<body>
<h1>Meeting Notes</h1>
<p>Welcome to the meeting.<o:p></o:p></p>
</body>
</html>
Correct: Clean HTML5 without Office namespaces
Remove all xmlns: prefixed attributes and any Office-specific elements like <o:p>:
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Document</title>
</head>
<body>
<h1>Meeting Notes</h1>
<p>Welcome to the meeting.</p>
</body>
</html>
Incorrect: Office namespace on a non-root element
Sometimes Office markup appears deeper in the document:
<div xmlns:o="urn:schemas-microsoft-com:office:office">
<p>Some content<o:p></o:p></p>
</div>
Correct: Cleaned-up version
<div>
<p>Some content</p>
</div>
By stripping out all Microsoft Office namespace declarations and their associated proprietary elements, your HTML becomes standards-compliant, lighter, and more compatible across browsers and assistive technologies. If you frequently convert Office documents to HTML, consider using a cleanup tool like DirtyMarkup or the “Paste as plain text” feature in your content editor to avoid these issues from the start.
The poster attribute tells the browser which image to display as a preview frame before the user plays the video. According to the WHATWG HTML Living Standard, the poster attribute’s value must be a valid non-empty URL potentially surrounded by spaces. When you include poster="", the attribute is present but its value is an empty string, which is not a valid URL — triggering the W3C validator error: Bad value “” for attribute “poster” on element “video”: Must be non-empty.
This issue commonly occurs in a few scenarios:
- Template or CMS output where the poster URL is dynamically generated but the variable resolves to an empty string (e.g., poster="<?= $posterUrl ?>" when $posterUrl is empty).
- JavaScript frameworks that bind a value to the poster attribute, but the bound variable is null, undefined, or an empty string.
- Manual editing where a developer adds the attribute as a placeholder intending to fill it in later.
Why This Matters
While most browsers will gracefully handle an empty poster attribute by simply not displaying a poster image, there are good reasons to fix this:
- Standards compliance: An empty poster value violates the HTML specification. Valid markup ensures predictable behavior across all browsers and devices.
- Unnecessary network requests: Some browsers may attempt to resolve the empty string as a relative URL, potentially triggering a failed HTTP request to the current page’s URL. This wastes bandwidth and clutters developer tools and server logs with spurious errors.
- Accessibility: Screen readers and assistive technologies may interpret the empty attribute inconsistently, leading to confusing announcements for users.
How to Fix It
You have two straightforward options:
- Provide a valid image URL — If you want a poster frame, set the value to a real image path.
- Remove the attribute entirely — If you don’t need a poster image, simply omit poster. The browser will either show nothing or display the first frame of the video once enough data has loaded, depending on the preload attribute setting.
If the empty value comes from dynamic code, add a conditional check so the poster attribute is only rendered when a URL is actually available.
Examples
❌ Invalid: Empty poster attribute
<video controls poster="">
<source src="movie.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
This triggers the validation error because poster is present but has no value.
✅ Fixed: Poster attribute with a valid URL
<video controls poster="thumbnail.jpg">
<source src="movie.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
The poster attribute now points to a valid image file that the browser will display before playback begins.
✅ Fixed: Poster attribute removed entirely
<video controls>
<source src="movie.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
When no poster image is needed, omitting the attribute is the cleanest solution.
✅ Fixed: Conditional rendering in a template
If you’re using a templating language or framework, conditionally include the attribute only when a value exists. Here’s a conceptual example using a server-side template:
<!-- Pseudo-template syntax: only render poster when posterUrl is not empty -->
<video controls {{#if posterUrl}}poster="{{posterUrl}}"{{/if}}>
<source src="movie.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
This pattern prevents the empty poster="" from ever reaching the rendered HTML, keeping your output valid regardless of whether a poster URL is available.
The grid-template-columns property defines the column track sizes of a CSS grid container. When the W3C validator reports that a particular value “is not a grid-template-columns value,” it means the parser encountered something it cannot interpret as a valid track size or track listing.
This error can be triggered by many common mistakes: a simple typo (like auто instead of auto), using a CSS custom property (the validator may not resolve var() references), forgetting units on a length value (e.g., 100 instead of 100px), using JavaScript-like terms (e.g., undefined or null), or using newer syntax that the validator’s CSS parser doesn’t yet support.
While browsers are generally forgiving and will simply ignore an invalid grid-template-columns declaration, this means your grid layout silently breaks — the container won’t form a grid as intended, and content may stack in a single column. Fixing validation errors ensures your layout works predictably across browsers and makes your stylesheets easier to maintain.
Valid values
The grid-template-columns property accepts these value types:
- none — the default; no explicit grid columns are defined.
- Length and percentage values — px, em, rem, %, vh, vw, etc. (e.g., 200px, 50%).
- The fr unit — distributes remaining space proportionally (e.g., 1fr 2fr).
- Keywords — auto, min-content, max-content.
- The repeat() function — shorthand for repeated track patterns (e.g., repeat(3, 1fr)).
- The minmax() function — sets a minimum and maximum size for a track (e.g., minmax(150px, 1fr)).
- The fit-content() function — clamps the track to a given maximum (e.g., fit-content(300px)).
- Named grid lines — defined with square brackets (e.g., [sidebar-start] 200px [sidebar-end content-start] 1fr [content-end]).
- Any combination of the above.
Common causes
- Typos or made-up keywords — values like undefined, inherit-grid, or misspelled units.
- Missing units — writing 100 instead of 100px. The fr unit, px, and all other units are mandatory (only 0 can be unitless).
- Invalid function syntax — missing commas or parentheses in repeat() or minmax().
- CSS custom properties — var(--cols) may trigger validator warnings because the validator cannot resolve the variable at parse time. This is typically a false positive.
Examples
Incorrect: invalid keyword
<style>
.grid {
display: grid;
grid-template-columns: undefined;
}
</style>
Incorrect: missing unit on a length
<style>
.grid {
display: grid;
grid-template-columns: 200 1fr;
}
</style>
Incorrect: malformed repeat() syntax
<style>
.grid {
display: grid;
grid-template-columns: repeat(3 1fr);
}
</style>
Correct: using fr units
<style>
.grid {
display: grid;
grid-template-columns: 1fr 2fr;
}
</style>
Correct: mixing lengths, fr, and auto
<style>
.grid {
display: grid;
grid-template-columns: 250px 1fr auto;
}
</style>
Correct: using repeat() and minmax()
<style>
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
</style>
Correct: named grid lines with track sizes
<style>
.grid {
display: grid;
grid-template-columns: [sidebar] 240px [content] 1fr [aside] 200px;
}
</style>
If the validator flags a var() custom property usage and you’re confident the variable resolves to a valid value at runtime, the warning can generally be disregarded — this is a known limitation of static CSS validation. For all other cases, double-check spellings, ensure every numeric value (other than 0) has a unit, and verify that function syntax includes the correct commas and parentheses.
The action attribute tells the browser where to send form data when the form is submitted. According to the WHATWG HTML living standard, if the action attribute is specified, its value must be a valid non-empty URL potentially surrounded by spaces. An empty string ("") does not satisfy this requirement, which is why the W3C validator flags it.
While some developers use action="" intending the form to submit to the current page’s URL, this approach is non-conforming HTML. Browsers do typically interpret an empty action as “submit to the current URL,” but relying on this behavior is unnecessary since simply omitting the action attribute achieves the same result in a standards-compliant way. When no action attribute is present, the form submits to the URL of the page containing the form — this is the defined default behavior per the HTML specification.
This issue matters for several reasons:
- Standards compliance: Non-conforming HTML can lead to unexpected behavior across different browsers or future browser versions.
- Maintainability: Using the correct approach makes your intent clearer to other developers. Omitting action explicitly signals “submit to the current page,” while action="" looks like a mistake or a placeholder that was never filled in.
- Tooling: Build tools, linters, and automated testing pipelines that rely on valid HTML may flag or break on this error.
How to Fix
You have two options:
- Remove the action attribute if you want the form to submit to the current page URL.
- Provide a valid URL if the form should submit to a specific endpoint.
Examples
❌ Invalid: Empty action attribute
<form action="" method="post">
<label for="email">Email:</label>
<input type="email" id="email" name="email">
<button type="submit">Subscribe</button>
</form>
This triggers the error: Bad value “” for attribute “action” on element “form”: Must be non-empty.
✅ Fixed: Remove the action attribute
If you want the form to submit to the current page, simply omit action:
<form method="post">
<label for="email">Email:</label>
<input type="email" id="email" name="email">
<button type="submit">Subscribe</button>
</form>
✅ Fixed: Provide a valid URL
If the form should submit to a specific endpoint, supply the URL:
<form action="/subscribe" method="post">
<label for="email">Email:</label>
<input type="email" id="email" name="email">
<button type="submit">Subscribe</button>
</form>
✅ Fixed: Use a hash as a placeholder for JavaScript-handled forms
If your form is entirely handled by JavaScript and should not navigate anywhere, you can use a # as the action or, preferably, omit action and prevent submission in your script:
<form method="post" id="js-form">
<label for="query">Search:</label>
<input type="text" id="query" name="query">
<button type="submit">Search</button>
</form>
document.getElementById('js-form').addEventListener('submit', function(e) {
e.preventDefault();
// Handle form data with JavaScript
});
In this case, omitting action is the cleanest solution. The JavaScript preventDefault() call stops the browser from actually submitting the form, so the action value is never used.
The src attribute on a <script> element tells the browser where to fetch an external JavaScript file. According to the HTML specification, when the src attribute is present, its value must be a valid non-empty URL. An empty string does not qualify as a valid URL, so the validator flags it as an error.
This issue typically arises in a few common scenarios:
- Templating or CMS placeholders — A template engine or content management system outputs an empty src when no script URL is configured.
- Dynamic JavaScript — Client-side code is intended to set the src later, but the initial HTML ships with an empty value.
- Copy-paste mistakes — The attribute was added in anticipation of a script file that was never specified.
Beyond failing validation, an empty src causes real problems. Most browsers interpret an empty src as a relative URL that resolves to the current page’s URL. This means the browser will make an additional HTTP request to re-fetch the current HTML document and attempt to parse it as JavaScript, wasting bandwidth, slowing down page load, and potentially generating console errors. It can also cause unexpected side effects in server logs and analytics.
How to Fix It
Choose the approach that matches your intent:
- Provide a valid URL — If you need an external script, set src to the correct file path or full URL.
- Use an inline script — If your JavaScript is written directly in the HTML, remove the src attribute entirely. Note that when src is present, browsers ignore any content between the opening and closing <script> tags.
- Remove the element — If the script isn’t needed, remove the <script> element altogether.
Also keep in mind:
- Ensure the file path is correct relative to the HTML file’s location.
- If using an absolute URL, verify it is accessible and returns JavaScript content.
- If a script should only be loaded conditionally, handle the condition in your server-side or build logic rather than outputting an empty src.
Examples
❌ Invalid: Empty src attribute
<script src=""></script>
This triggers the validation error because the src value is an empty string.
❌ Invalid: Whitespace-only src attribute
<script src=" "></script>
A value containing only whitespace is also not a valid URL and will produce the same error.
✅ Fixed: Valid external script
<script src="js/app.js"></script>
The src attribute contains a valid relative URL pointing to an actual JavaScript file.
✅ Fixed: Valid external script with a full URL
<script src="https://example.com/js/library.min.js"></script>
✅ Fixed: Inline script without src
If you want to write JavaScript directly in your HTML, omit the src attribute:
<script>
console.log("This is an inline script.");
</script>
✅ Fixed: Conditionally omitting the element
If the script URL comes from a template variable that might be empty, handle it at the template level so the <script> element is only rendered when a URL is available. For example, in a templating language:
<!-- Pseudocode — only output the tag when the URL exists -->
<!-- {% if script_url %} -->
<script src="analytics.js"></script>
<!-- {% endif %} -->
This prevents the empty src from ever reaching the browser.
The xmlns:v attribute is a namespace declaration that binds the v prefix to Microsoft’s VML namespace (urn:schemas-microsoft-com:vml). VML was a proprietary vector graphics format used primarily by Internet Explorer (versions 5 through 9) for rendering shapes, lines, and other graphical elements. When Microsoft dropped VML support in favor of SVG starting with IE 9, the technology became obsolete.
In HTML5 (the HTML living standard), namespace declarations using the xmlns: prefix pattern are not permitted. The HTML parser does not process these as actual namespace bindings — they are treated as regular attributes with a colon in the name. The validator flags this because such attributes cannot be round-tripped through an XML 1.0 serializer. An attribute name containing a colon implies a namespace prefix in XML, but without a proper namespace declaration in the XML output, the serialization would be invalid. This means your document cannot be reliably converted between HTML and XML formats.
This issue commonly appears in pages generated by older versions of Microsoft Office (Word, Outlook) that export to HTML, or in legacy templates that were designed for IE compatibility. You may also see similar warnings for related attributes like xmlns:o (Office namespace) or xmlns:w (Word namespace).
Why this matters
- Standards compliance: HTML5 explicitly does not support custom namespace declarations. Only the built-in namespaces for SVG and MathML are recognized.
- No functional benefit: Since no modern browser supports VML, the attribute serves no purpose. It adds dead weight to your markup.
- Interoperability: Documents with non-serializable attributes cannot be cleanly processed by XML-based tools, XSLT transformations, or any system that needs valid XML serialization.
How to fix it
- Remove the xmlns:v attribute from your <html> element (or wherever it appears).
- Remove any other legacy Microsoft namespace declarations such as xmlns:o, xmlns:w, or xmlns:x.
- Remove any VML-specific elements (like <v:shape>, <v:oval>, etc.) from your document, as they are not recognized by modern browsers.
- Replace VML graphics with SVG if you still need vector graphics functionality. SVG is natively supported in all modern browsers and is part of the HTML standard.
Examples
Incorrect: legacy VML namespace declaration
<!DOCTYPE html>
<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" lang="en">
<head>
<title>Legacy VML Page</title>
</head>
<body>
<v:oval style="width:100px;height:75px" fillcolor="blue"></v:oval>
</body>
</html>
This triggers the validator warning for both xmlns:v and xmlns:o, and the <v:oval> element is not recognized by any modern browser.
Correct: namespace removed, VML replaced with SVG
<!DOCTYPE html>
<html lang="en">
<head>
<title>Modern SVG Page</title>
</head>
<body>
<svg width="100" height="75" xmlns="http://www.w3.org/2000/svg">
<ellipse cx="50" cy="37.5" rx="50" ry="37.5" fill="blue" />
</svg>
</body>
</html>
Correct: simple removal when no vector graphics are needed
If the namespace was included unnecessarily (common with auto-generated HTML), simply remove it:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Clean Page</title>
</head>
<body>
<p>No legacy namespace attributes needed.</p>
</body>
</html>
If your HTML was exported from Microsoft Office, consider running it through an HTML cleaner or manually stripping all xmlns:* attributes and proprietary elements. The resulting markup will be smaller, valid, and fully compatible with modern browsers.
Understanding the 502 Bad Gateway Error
When you submit a URL to the W3C HTML Validator, the validator makes an HTTP request to fetch your page so it can analyze the markup. A 502 Bad Gateway status code means that some server acting as a gateway or proxy between the validator and your origin server received an invalid or incomplete response. Because the validator never receives your HTML content, it cannot perform any validation and reports this retrieval error instead.
This is fundamentally different from an HTML issue. Your markup could be perfectly valid, but the validator simply cannot see it. The problem lies somewhere in the chain of servers that handle the request.
Common Causes
A 502 error can be triggered by several infrastructure-level issues:
- Origin server is down or crashing. If your web application (e.g., Node.js, PHP-FPM, Django) has crashed or is unresponsive, the reverse proxy (e.g., Nginx, Apache) sitting in front of it will return a 502.
- Server overload or timeout. If your application takes too long to generate a response, the gateway may time out and return a 502 before the response is ready.
- CDN or proxy misconfiguration. Services like Cloudflare, AWS CloudFront, or Varnish may return 502 if they cannot reach your origin server, or if SSL/TLS settings between the CDN and origin are mismatched.
- Firewall or IP blocking. Your server’s firewall or hosting provider may be blocking requests from the validator’s IP address, causing the gateway to fail.
- DNS propagation issues. If you recently changed DNS records, some servers in the chain may be resolving to an outdated or invalid address.
- Temporary networking issues. Transient network problems between servers can cause intermittent 502 errors.
How to Fix It
Since this is not an HTML problem, you won’t be editing any markup. Instead, follow these steps:
- Verify your site is accessible. Open the URL you’re trying to validate in your own browser. If you also see a 502 error, the problem is on your server and needs to be fixed there first.
- Check your server logs. Look at the error logs for your reverse proxy (Nginx, Apache) and your application server. They will usually indicate why the upstream connection failed.
- Inspect your CDN or proxy settings. If you use a CDN like Cloudflare, check its dashboard for error reports. Ensure the SSL mode matches your origin server’s configuration (e.g., “Full (Strict)” requires a valid certificate on your origin).
- Confirm no IP-based blocking. The W3C Validator makes requests from its own servers. Make sure your firewall, .htaccess rules, or hosting provider aren’t blocking external automated requests.
- Check for authentication barriers. If your site requires HTTP authentication, a login, or is behind a VPN, the validator cannot access it. You’ll need to use the validator’s “Validate by Direct Input” option instead, pasting your HTML directly.
- Wait and retry. If the issue is a temporary server overload or network blip, simply waiting a few minutes and trying again may resolve it.
- Contact your hosting provider. If the error persists and you can’t identify the cause, your hosting support team can investigate the server-side issue.
Alternatives When Your Server Is Unavailable
If you cannot resolve the server issue immediately but still need to validate your HTML, you have two options that bypass the URL fetch entirely:
Validate by Direct Input
Go to the W3C Validator, select the “Validate by Direct Input” tab, and paste your HTML source code directly.
Validate by File Upload
Select the “Validate by File Upload” tab and upload your .html file from your local machine.
Examples
Even though this error is not caused by HTML, here’s an illustration of valid markup you might be trying to validate when encountering this issue:
A valid HTML page that the validator cannot reach
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Page</title>
</head>
<body>
<h1>Welcome</h1>
<p>This page is perfectly valid, but the validator cannot fetch it due to a 502 error.</p>
</body>
</html>
If submitting the URL https://example.com/my-page produces the “HTTP resource not retrievable (502)” message, paste the above source code into the “Validate by Direct Input” field instead. The validator will then be able to check your markup without needing to fetch it from the server.
Hexadecimal color values in CSS must follow a specific format: the # symbol followed by exactly 3 or 6 hexadecimal digits. Each digit can be a number from 0 to 9 or a letter from A to F (case-insensitive). A 3-digit hex code is shorthand where each digit is expanded by duplication — for example, #F00 is equivalent to #FF0000. Common mistakes that trigger this error include using the wrong number of digits (e.g., 1, 2, 4, or 5), including non-hexadecimal characters (like G, Z, or special symbols), or omitting the # prefix.
The color CSS property sets the foreground color of an element’s text and text decorations. It also establishes the currentcolor value, which acts as an indirect value for other properties like border-color. Because color cascades to many visual aspects of an element, an invalid value can cause the entire declaration to be ignored, meaning the element may inherit an unexpected color or fall back to browser defaults.
This matters for consistency across browsers. While some browsers may attempt to guess what you meant with a malformed hex code, others will discard the value entirely. This leads to unpredictable rendering. Using valid color values ensures your styles are applied reliably everywhere.
Note that CSS also supports 4-digit and 8-digit hex values (which include an alpha/transparency channel, e.g., #F00A or #FF0000AA). However, the W3C validator’s inline style checker may not recognize these newer formats depending on the CSS level being validated. If you need transparency, consider using the rgba() function for broader validation compatibility.
Examples
Invalid hex color values
These examples will trigger the validation error:
<!-- Only 1 digit -->
<p style="color: #F;">Hello</p>
<!-- Only 2 digits -->
<p style="color: #FF;">Hello</p>
<!-- 4 digits (may not pass older CSS validation) -->
<p style="color: #FF00;">Hello</p>
<!-- 5 digits -->
<p style="color: #FF000;">Hello</p>
<!-- Non-hexadecimal character "G" -->
<p style="color: #FG0000;">Hello</p>
<!-- Missing the # prefix -->
<p style="color: FF0000;">Hello</p>
Valid hex color values
Use exactly 3 or 6 hexadecimal digits after the #:
<!-- 3-digit shorthand for red -->
<p style="color: #F00;">Hello</p>
<!-- 6-digit full form for red -->
<p style="color: #FF0000;">Hello</p>
<!-- 3-digit shorthand for white -->
<p style="color: #FFF;">Hello</p>
<!-- 6-digit full form for a dark gray -->
<p style="color: #333333;">Hello</p>
<!-- Lowercase is also valid -->
<p style="color: #3a7bd5;">Hello</p>
Alternative color formats
If hex values are causing issues, CSS offers several other valid ways to specify colors:
<!-- Named color -->
<p style="color: red;">Hello</p>
<!-- RGB function -->
<p style="color: rgb(255, 0, 0);">Hello</p>
<!-- RGBA function (with transparency) -->
<p style="color: rgba(255, 0, 0, 0.5);">Hello</p>
<!-- HSL function -->
<p style="color: hsl(0, 100%, 50%);">Hello</p>
HTTP status code 429 is defined in RFC 6585 and signals that a client has sent too many requests in a given time period. When the W3C Validator encounters your HTML, it doesn’t just parse the markup — it also attempts to retrieve linked resources like stylesheets, scripts, and images to perform thorough validation. If any of those resources are hosted on a server that enforces rate limits (which is common with CDNs, API providers, and popular hosting platforms), the server may reject the validator’s request with a 429 response.
This is not a syntax error in your HTML. Your markup may be perfectly valid. The issue is environmental: the remote server is temporarily refusing connections from the validator. However, because the validator cannot retrieve the resource, it cannot fully verify everything about your page, so it flags the problem.
Common Causes
- Rapid repeated validation: Running the validator many times in quick succession against pages that reference the same external resources.
- Shared rate limits: The W3C Validator service shares outbound IP addresses, so other users’ validation requests may count against the same rate limit on the remote server.
- Aggressive server-side rate limiting: The remote server or its CDN (e.g., Cloudflare, AWS CloudFront) has strict rate-limiting rules that block automated HTTP clients quickly.
- Many external resources: A page that references numerous resources from the same external host may trigger rate limits in a single validation pass.
How to Fix It
Wait and retry
Since 429 is a temporary condition, simply waiting a few minutes and revalidating is often enough. Some servers include a Retry-After header in the 429 response indicating how long to wait, though the validator may not surface this detail.
Reduce validation frequency
If you’re running automated validation (e.g., in a CI/CD pipeline), space out your requests. Avoid validating dozens of pages in rapid-fire succession.
Host resources locally
If you control the website, consider self-hosting critical resources rather than relying on third-party CDNs. This gives you full control over availability and eliminates third-party rate-limit issues during validation.
Adjust server rate limits
If you own the server that’s returning 429, review your rate-limiting configuration. You may want to whitelist the W3C Validator’s user agent or IP range, or relax limits that are overly aggressive for legitimate automated tools.
Examples
Page that may trigger the issue
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Page</title>
<link rel="stylesheet" href="https://cdn.example.com/styles/main.css">
<script src="https://cdn.example.com/scripts/app.js"></script>
</head>
<body>
<h1>Hello, world!</h1>
</body>
</html>
If cdn.example.com rate-limits the validator, both the stylesheet and script fetches could fail with a 429, producing two “HTTP resource not retrievable” messages.
Fix: Self-host the resources
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Page</title>
<link rel="stylesheet" href="/css/main.css">
<script src="/js/app.js"></script>
</head>
<body>
<h1>Hello, world!</h1>
</body>
</html>
By serving main.css and app.js from your own domain, you eliminate the dependency on a third-party server’s rate limits during validation.
Fix: Reduce external dependencies
If self-hosting isn’t feasible, minimize the number of external resources from a single host to lower the chance of hitting rate limits:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Page</title>
<link rel="stylesheet" href="https://cdn.example.com/bundle.css">
</head>
<body>
<h1>Hello, world!</h1>
<script src="https://cdn.example.com/bundle.js"></script>
</body>
</html>
Bundling multiple files into single resources reduces the total number of requests the validator needs to make to the external server, making rate-limit errors less likely.
The HTML specification restricts where <link> elements can appear based on their purpose. Links that load resources needed for rendering (like stylesheets and preloaded assets) or carry microdata (itemprop) are allowed in <body> because they have a direct relationship to the content around them. Other types of <link> elements—canonical URLs, icons, alternate versions—are document-level metadata and belong exclusively in <head>.
This matters for several reasons. Browsers may ignore or inconsistently handle <link> elements placed in unexpected locations, leading to issues like missing canonical signals for search engines or broken favicon references. Standards compliance also ensures your HTML is forward-compatible and behaves predictably across all browsers.
Common causes
Direct placement in <body>
The most straightforward cause is placing a metadata <link> directly inside <body>, often due to a CMS, template system, or plugin injecting it in the wrong location.
Implicit <body> creation by the parser
A subtler cause occurs when an element that’s only valid in <body> appears inside <head>. When the HTML parser encounters such an element (like <img>, <div>, or <p>), it implicitly closes the <head> and opens the <body>. Any <link> elements that follow are then treated as descendants of <body>, even though they appear to be inside <head> in your source code.
For example, an <img> tag in the <head> causes the parser to switch to body context, so the subsequent <link rel="canonical"> is interpreted as being inside <body> and triggers this error.
How to fix it
-
Move disallowed <link> elements to <head>: If a <link> with rel="canonical", rel="icon", rel="alternate", or similar values is in <body>, move it into <head>.
-
Check for body-only elements in <head>: Look for elements like <img>, <div>, <p>, <script> (without src), or text content that may have been accidentally placed in <head>. These cause the parser to implicitly close <head>, making everything after them part of <body>.
-
Use allowed rel values if a body placement is intentional: If you genuinely need a <link> in <body>, ensure it uses one of the permitted rel values (stylesheet, preload, prefetch, preconnect, dns-prefetch, modulepreload, pingback, prerender) or has an itemprop attribute.
Examples
❌ <link rel="canonical"> placed in <body>
<body>
<link rel="canonical" href="https://example.com/page">
<h1>Welcome</h1>
</body>
✅ Move it to <head>
<head>
<title>My Page</title>
<link rel="canonical" href="https://example.com/page">
</head>
<body>
<h1>Welcome</h1>
</body>
❌ An <img> in <head> forces implicit body context
Even though the <link> appears to be in <head>, the <img> causes the parser to switch to body context:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test</title>
<img src="photo.jpg" alt="A smiling cat">
<link rel="canonical" href="https://example.com/">
</head>
<body>
<p>Some content</p>
</body>
</html>
✅ Move the <img> to <body> where it belongs
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test</title>
<link rel="canonical" href="https://example.com/">
</head>
<body>
<img src="photo.jpg" alt="A smiling cat">
<p>Some content</p>
</body>
</html>
✅ Allowed <link> elements inside <body>
These are valid because they use permitted rel values:
<body>
<article>
<link rel="stylesheet" href="article-theme.css">
<h2>Article Title</h2>
<p>Content here.</p>
</article>
<link rel="prefetch" href="/next-page.html">
<link rel="preload" href="/font.woff2" as="font" type="font/woff2" crossorigin>
</body>
✅ Using itemprop for microdata
A <link> with an itemprop attribute is also valid inside <body>:
<body>
<div itemscope itemtype="https://schema.org/Product">
<span itemprop="name">Widget</span>
<link itemprop="availability" href="https://schema.org/InStock">
</div>
</body>
When the W3C Validator encounters a URL in your HTML — whether in a <link>, <script>, <img>, or any other element that references an external resource — it may attempt to retrieve that resource as part of the validation process. If the remote server returns an HTTP 503 status code, the validator cannot fetch the resource and raises this error. The 503 status code specifically means “Service Unavailable,” indicating a temporary condition on the server side.
This error is not a problem with your HTML syntax. It’s an infrastructure issue that can be caused by several factors:
- Server maintenance: The remote server is temporarily down for updates or scheduled maintenance.
- Server overload: The server is handling too many requests and cannot respond in time.
- Rate limiting: Some servers detect automated requests (like those from the validator) and respond with 503 to throttle traffic.
- CDN or hosting issues: The content delivery network or hosting provider is experiencing temporary problems.
- Incorrect URL: The resource URL may point to a server that no longer hosts the expected content.
While this isn’t a standards compliance issue per se, it’s important to address because unreachable resources can affect your page’s rendering, functionality, and accessibility. A missing stylesheet means unstyled content, a missing script means broken interactivity, and a missing image means absent visual information.
How to Fix It
- Verify the URL: Open the referenced URL directly in a browser to confirm it’s valid and accessible.
- Retry later: Since 503 is a temporary status, simply re-running the validator after some time often resolves the issue.
- Host resources locally: For critical assets like stylesheets and scripts, consider self-hosting them rather than relying on third-party servers.
- Use reliable CDNs: If you use a CDN, choose one with high uptime guarantees (e.g., established providers for popular libraries).
- Add fallbacks: For scripts loaded from external CDNs, consider including a local fallback.
Examples
External resource that may trigger a 503
<link rel="stylesheet" href="https://example.com/styles/main.css">
<script src="https://example.com/libs/library.js"></script>
If example.com is temporarily unavailable, the validator will report the 503 error for each of these resources.
Fix: Host resources locally
<link rel="stylesheet" href="/css/main.css">
<script src="/js/library.js"></script>
By hosting the files on your own server, you eliminate the dependency on a third-party server’s availability and ensure the validator (and your users) can always access them.
Fix: Use a reliable CDN with a local fallback
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
<script>
if (typeof jQuery === "undefined") {
var s = document.createElement("script");
s.src = "/js/jquery.min.js";
document.head.appendChild(s);
}
</script>
This approach loads jQuery from a well-known CDN but falls back to a local copy if the CDN is unavailable. While this fallback pattern doesn’t prevent the validator warning itself, it ensures your page works for real users even when the CDN is down.
Fix: Validate using “text input” mode
If the 503 errors persist and are caused by the validator’s requests being blocked or rate-limited, you can work around the issue by validating your HTML using the validator’s “Validate by Direct Input” option. Paste your HTML source code directly into the validator at https://validator.w3.org/#validate_by_input. This still validates your markup structure and syntax, though the validator may not check externally referenced resources.
Keep in mind that a 503 error during validation is almost always temporary. If you’ve confirmed your URLs are correct and the resources load fine in a browser, the safest approach is simply to wait and validate again later.
A 405 Method Not Allowed status means the server hosting the linked resource rejected the validator’s request, typically because it doesn’t allow the HTTP HEAD or GET method the validator uses to check the URL.
This is not an error in your HTML code. It’s a server-side issue with the remote resource you’re referencing. The W3C validator tries to fetch external resources (stylesheets, scripts, images, etc.) to verify they exist and are valid. When a remote server blocks those requests, the validator reports this warning.
Common causes include:
- The remote server has strict firewall or bot-protection rules that block requests from the validator.
- The server requires authentication or specific headers to serve the resource.
- The URL points to an API endpoint or resource that only accepts POST requests.
You can safely ignore this warning if the resource loads correctly in a browser. However, if you want to resolve it, consider these options:
- Host the resource yourself instead of relying on the remote server.
- Verify the URL is correct — make sure it points to a publicly accessible file.
- If it’s your own server, ensure it responds to HEAD and GET requests for that resource.
HTML Example
A typical case where this happens:
<!-- A remote stylesheet hosted on a server that blocks validator requests -->
<link rel="stylesheet" href="https://example.com/restricted/styles.css">
Fix: Host the Resource Locally
<!-- Download the file and serve it from your own domain -->
<link rel="stylesheet" href="/css/styles.css">
Self-hosting the resource eliminates the dependency on the remote server and ensures the validator can access it.
The aria-owns attribute is used to define parent-child relationships in the accessibility tree that aren’t reflected in the DOM structure. For example, if a menu visually “owns” items that are placed elsewhere in the markup (perhaps for layout reasons), aria-owns tells assistive technologies that those elements should be treated as children of the owning element. The attribute accepts a space-separated list of one or more IDs.
According to the WAI-ARIA specification, each ID referenced by aria-owns must correspond to an element in the same document. If no matching element is found, the relationship is broken and the accessibility tree becomes inaccurate. This can confuse screen readers and other assistive technologies, potentially making parts of your page inaccessible or misrepresented to users who rely on them.
Common causes of this error:
- Typos in the ID value — a small misspelling like aria-owns="drpdown-menu" instead of aria-owns="dropdown-menu".
- Referencing an element that doesn’t exist — the target element was removed or never added to the page.
- Referencing an element in a different document — the target lives inside an <iframe> or a shadow DOM, which are separate document contexts.
- Dynamic content timing — the referenced element is added to the DOM by JavaScript after validation or after the aria-owns relationship is evaluated.
How to fix it:
- Check that every ID in the aria-owns value exactly matches an id attribute on an element in the same document.
- Look for typos or case mismatches — IDs are case-sensitive.
- If the target element is inside an <iframe>, restructure your markup so both elements are in the same document, or use a different ARIA approach.
- If the element is added dynamically, ensure it exists in the DOM before the aria-owns relationship is needed by assistive technologies.
Examples
Incorrect — referencing a nonexistent ID
The element with id="dropdown-items" does not exist anywhere in the document, so the aria-owns reference is broken.
<button aria-owns="dropdown-items">Open Menu</button>
<!-- No element with id="dropdown-items" exists -->
Incorrect — typo in the referenced ID
The button references "menu-lst" but the actual element has id="menu-list".
<button aria-owns="menu-lst">Options</button>
<ul id="menu-list">
<li>Option A</li>
<li>Option B</li>
</ul>
Correct — referenced ID exists in the same document
The aria-owns value matches the id of an element present in the same page.
<button aria-owns="menu-list">Options</button>
<ul id="menu-list">
<li>Option A</li>
<li>Option B</li>
</ul>
Correct — multiple IDs referenced
When aria-owns lists multiple IDs, each one must have a corresponding element in the document.
<div role="tree" aria-owns="branch1 branch2 branch3">
File Explorer
</div>
<div role="treeitem" id="branch1">Documents</div>
<div role="treeitem" id="branch2">Pictures</div>
<div role="treeitem" id="branch3">Music</div>
Complete valid example
<!DOCTYPE html>
<html lang="en">
<head>
<title>aria-owns Example</title>
</head>
<body>
<div role="combobox" aria-expanded="true" aria-owns="suggestions">
<input type="text" aria-autocomplete="list">
</div>
<ul id="suggestions" role="listbox">
<li role="option">Apple</li>
<li role="option">Banana</li>
<li role="option">Cherry</li>
</ul>
</body>
</html>
In this example, the combobox uses aria-owns to claim the suggestions listbox as its child in the accessibility tree, even though the <ul> is a sibling in the DOM. Because the id="suggestions" element exists in the same document, the reference is valid and assistive technologies can correctly associate the two.
This error is not about your HTML syntax being invalid — it’s about a broken reference. The W3C Validator follows URLs it encounters in your markup (or a URL you submit directly for validation) and checks whether the server can actually deliver the resource. When the remote server returns an HTTP 404 (Not Found) response, the validator flags the issue because the referenced resource is missing or unreachable.
There are several common causes for this error:
- Typos in the URL — A misspelled filename, path, or domain name.
- Moved or deleted resources — The file existed at one point but has since been removed or relocated.
- Case sensitivity — Many web servers treat Image.png and image.png as different files. A mismatch in letter casing can produce a 404.
- Incorrect relative paths — A relative URL that resolves differently than expected based on the document’s location.
- External resources no longer available — Third-party CDNs or hosted files that have been taken down.
This matters because broken references degrade the user experience. Missing stylesheets can leave a page unstyled, missing scripts can break functionality, and missing images display broken image icons. Search engines also penalize pages with excessive broken links, and screen readers may announce confusing or unhelpful content when resources fail to load.
How to Fix It
- Check the URL carefully. Copy the full URL from your HTML, paste it into a browser, and see if it loads. If it returns a 404 page, the URL is wrong.
- Verify the file exists on the server. If you control the server, confirm the file is in the expected directory with the exact filename and extension.
- Fix case sensitivity issues. Ensure the capitalization in your URL matches the actual filename on the server.
- Update moved resources. If a file was relocated, update the href or src attribute to point to the new location.
- Replace unavailable external resources. If a third-party resource is no longer available, find an alternative source, host a copy yourself, or remove the reference.
Examples
Broken image reference (triggers the error)
<img src="https://example.com/images/photo.jpeg" alt="A scenic landscape">
If photo.jpeg doesn’t exist at that path (perhaps the actual file is named photo.jpg), the validator will report a 404 error.
Fixed image reference
<img src="https://example.com/images/photo.jpg" alt="A scenic landscape">
Broken stylesheet reference (triggers the error)
<link rel="stylesheet" href="/css/Styles.css">
If the file on the server is actually named styles.css (lowercase), a case-sensitive server will return a 404.
Fixed stylesheet reference
<link rel="stylesheet" href="/css/styles.css">
Broken script reference with incorrect path (triggers the error)
<script src="/assets/js/old-directory/app.js"></script>
If the script was moved to a different directory, this path no longer resolves.
Fixed script reference
<script src="/assets/js/app.js"></script>
Using a relative path incorrectly (triggers the error)
If your HTML file is at /pages/about.html and you reference an image like this:
<img src="images/logo.png" alt="Company logo">
The browser will look for /pages/images/logo.png. If the image actually lives at /images/logo.png, this will fail.
Fixed with a root-relative path
<img src="/images/logo.png" alt="Company logo">
The leading / ensures the path is resolved from the root of the site, regardless of where the HTML document is located.
The aria-describedby attribute is a core part of WAI-ARIA, the Web Accessibility Initiative’s specification for making web content more accessible. It works by creating a relationship between an element and one or more other elements that provide additional descriptive text. Screen readers and other assistive technologies use this relationship to announce the descriptive text when a user interacts with the element.
When you set aria-describedby="some-id", the browser looks for an element with id="some-id" in the same document. If no matching element exists, the reference is broken. This means assistive technologies cannot find the description, and the attribute silently does nothing. The W3C validator flags this as an error because a dangling reference indicates a bug — either the referenced element was removed, renamed, or was never added.
This issue commonly arises due to:
- Typos in the id value — the aria-describedby value doesn’t match the target element’s id exactly (the match is case-sensitive).
- Dynamic content — the described-by element is rendered conditionally or injected by JavaScript after validation.
- Copy-paste errors — markup was copied from another page or component, but the referenced element wasn’t included.
- Refactoring — an element’s id was changed or the element was removed, but the aria-describedby reference wasn’t updated.
Multiple id values can be listed in aria-describedby, separated by spaces. Every single id in that list must resolve to an element in the document. If even one is missing, the validator will report an error for that reference.
How to fix it
- Check for typos. Compare the value in aria-describedby against the id of the target element. Remember that id matching is case-sensitive — helpText and helptext are different.
- Add the missing element. If the descriptive element doesn’t exist yet, create it with the matching id.
- Remove stale references. If the description is no longer needed, remove the aria-describedby attribute entirely rather than leaving a broken reference.
- Verify all IDs in a multi-value list. If aria-describedby contains multiple space-separated IDs, confirm each one exists.
Examples
Broken reference (triggers the error)
In this example, aria-describedby points to password-help, but no element with that id exists in the document:
<label for="password">Password</label>
<input type="password" id="password" aria-describedby="password-help">
Fixed by adding the referenced element
Adding an element with id="password-help" resolves the issue:
<label for="password">Password</label>
<input type="password" id="password" aria-describedby="password-help">
<p id="password-help">Must be at least 8 characters with one number.</p>
Broken reference due to a typo
Here the aria-describedby value uses a different case than the element’s id:
<input type="text" id="email" aria-describedby="emailHelp">
<small id="emailhelp">We'll never share your email.</small>
The fix is to make the id values match exactly:
<input type="text" id="email" aria-describedby="email-help">
<small id="email-help">We'll never share your email.</small>
Multiple IDs with one missing
When listing multiple descriptions, every id must be present:
<!-- "format-hint" exists but "length-hint" does not — this triggers the error -->
<input type="text" id="username" aria-describedby="format-hint length-hint">
<span id="format-hint">Letters and numbers only.</span>
Fix it by adding the missing element:
<input type="text" id="username" aria-describedby="format-hint length-hint">
<span id="format-hint">Letters and numbers only.</span>
<span id="length-hint">Between 3 and 20 characters.</span>
Removing the attribute when no description is needed
If the descriptive content has been removed and is no longer relevant, simply remove the aria-describedby attribute:
<input type="text" id="search">
The aria-controls attribute establishes a programmatic relationship between a controlling element (like a button, tab, or scrollbar) and the element it controls (like a panel, region, or content area). Assistive technologies such as screen readers use this relationship to help users navigate between related elements — for example, announcing that a button controls a specific panel and allowing the user to jump to it.
When the id referenced in aria-controls doesn’t exist in the document, the relationship is broken. Screen readers may attempt to locate the target element and fail silently, or they may announce a control relationship that leads nowhere. This degrades the experience for users who rely on assistive technology and violates the WAI-ARIA specification, which requires that the value of aria-controls be a valid ID reference list pointing to elements in the same document.
Common causes of this error include:
- Typos in the id or the aria-controls value.
- Dynamically generated content where the controlled element hasn’t been rendered yet or has been removed from the DOM.
- Copy-paste errors where aria-controls was copied from another component but the corresponding id was not updated.
- Referencing elements in iframes or shadow DOM, which are considered separate document contexts.
The aria-controls attribute accepts one or more space-separated ID references. Every listed ID must match an element in the same document.
How to Fix
- Verify the target element exists in the document and has the exact id that aria-controls references.
- Check for typos — ID matching is case-sensitive, so mainPanel and mainpanel are not the same.
- If the controlled element is added dynamically, ensure it is present in the DOM before or at the same time as the controlling element, or update aria-controls programmatically when the target becomes available.
- If the controlled element is genuinely absent (e.g., conditionally rendered), remove the aria-controls attribute until the target element exists.
Examples
Incorrect: aria-controls references a non-existent ID
<button aria-controls="info-panel" aria-expanded="false">
Toggle Info
</button>
<div id="infopanel">
<p>Here is some additional information.</p>
</div>
This triggers the error because aria-controls="info-panel" does not match the actual id of "infopanel" (note the missing hyphen).
Correct: aria-controls matches an existing element’s ID
<button aria-controls="info-panel" aria-expanded="false">
Toggle Info
</button>
<div id="info-panel">
<p>Here is some additional information.</p>
</div>
Correct: Tab and tab panel relationship
<div role="tablist">
<button role="tab" aria-controls="tab1-panel" aria-selected="true">
Overview
</button>
<button role="tab" aria-controls="tab2-panel" aria-selected="false">
Details
</button>
</div>
<div id="tab1-panel" role="tabpanel">
<p>Overview content goes here.</p>
</div>
<div id="tab2-panel" role="tabpanel" hidden>
<p>Details content goes here.</p>
</div>
Both aria-controls values — tab1-panel and tab2-panel — correctly correspond to elements present in the document.
Correct: Custom scrollbar controlling a region
<div role="scrollbar" aria-controls="main-content" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" aria-orientation="vertical"></div>
<div id="main-content" role="region" aria-label="Main content">
<p>Scrollable content goes here.</p>
</div>
Correct: Controlling multiple elements
The aria-controls attribute can reference multiple IDs separated by spaces. Each ID must exist in the document.
<button aria-controls="section-a section-b">
Expand All Sections
</button>
<div id="section-a">
<p>Section A content.</p>
</div>
<div id="section-b">
<p>Section B content.</p>
</div>
The background CSS property accepts a variety of value types: named colors (red, blue), hexadecimal codes (#fff, #ff0000), color functions (rgb(), hsl(), rgba()), gradient functions (linear-gradient(), radial-gradient()), image URLs, and CSS keywords like none, transparent, or inherit. The word from is not among these valid values.
Why this happens
This error most commonly appears in one of these scenarios:
-
Legacy WebKit gradient syntax. Older versions of Safari and Chrome used a proprietary syntax: -webkit-gradient(linear, left top, right top, from(#fff), to(#000)). The from() and to() functions are part of this deprecated, non-standard format. If this syntax is used without the -webkit- prefix—or if the validator encounters it—the word from gets flagged as an invalid color value.
-
Incorrectly written gradient shorthand. Some developers unfamiliar with the CSS gradient specification write something resembling natural language, like background: from #fff to #000, which has no meaning in CSS.
-
CSS from keyword in relative color syntax. CSS Color Level 5 introduces relative color syntax using the from keyword (e.g., rgb(from red r g b / 50%)). This is a newer feature that may not yet be recognized by the W3C CSS validator, which can lag behind the latest specifications. If you’re using this syntax intentionally, the error may be a false positive from the validator, but be aware that browser support may still be limited.
Why it matters
Invalid CSS values are silently ignored by browsers, meaning your intended background styling won’t be applied. The element will fall back to its default or inherited background, which can result in broken layouts, missing visual cues, or poor contrast that harms readability and accessibility. Using standard, valid CSS ensures consistent rendering across all browsers.
How to fix it
- Replace legacy -webkit-gradient() syntax with the standard linear-gradient() or radial-gradient() functions.
- Use valid color formats for solid backgrounds: hex codes, named colors, or color functions.
- If using relative color syntax (from keyword in CSS Color Level 5), understand that the validator may not yet support it. Consider adding a fallback value for broader compatibility.
Examples
Incorrect: legacy WebKit gradient syntax
The from() and to() functions in -webkit-gradient() are non-standard and will trigger this error if used as a background value:
<style>
.banner {
/* Non-standard syntax; "from" is not a valid CSS value */
background: -webkit-gradient(linear, left top, right top, from(#fff), to(#000));
}
</style>
<div class="banner">Legacy gradient</div>
Incorrect: made-up gradient shorthand
Writing gradient-like syntax without a proper CSS function is invalid:
<style>
.banner {
/* "from" and "to" are not valid CSS keywords here */
background: from #fff to #000;
}
</style>
<div class="banner">Invalid gradient</div>
Correct: standard linear gradient
Use linear-gradient() with a direction and comma-separated color stops:
<style>
.banner {
background: linear-gradient(to right, #fff, #000);
}
</style>
<div class="banner">Standard gradient</div>
Correct: solid color background
For a simple solid color, use any valid CSS color value:
<style>
.banner {
background: #fff;
}
</style>
<div class="banner">White background</div>
Correct: gradient with a fallback for older browsers
When using gradients, it’s good practice to provide a solid color fallback:
<style>
.banner {
background: #fff;
background: linear-gradient(to bottom, #ffffff, #cccccc);
}
</style>
<div class="banner">Gradient with fallback</div>
Correct: relative color syntax with a fallback
If you intentionally use CSS Color Level 5 relative color syntax and the validator flags from, provide a fallback and be aware of current browser support:
<style>
.banner {
background: rgb(255, 0, 0);
background: rgb(from red r g b / 50%);
}
</style>
<div class="banner">Relative color with fallback</div>
Always verify that your background values use standard CSS syntax. When in doubt, test your styles in the W3C CSS Validator and check browser support on Can I Use.
The W3C HTML Validator doesn’t just parse your markup—it also attempts to retrieve external resources referenced in elements like <link>, <script>, <img>, and <iframe>. When a server returns a 403 status code, it’s telling the validator “you are not authorized to access this resource.” The validator then reports this as an informational message because it cannot fully validate the referenced content.
Common Causes
Several server-side configurations can trigger this issue:
- Hotlink protection — Many servers and CDNs block requests that don’t originate from an approved domain. Since the validator’s requests come from validator.w3.org, they get rejected.
- IP-based restrictions or firewalls — The remote server may restrict access to specific IP ranges, blocking the validator’s servers.
- User-Agent filtering — Some servers reject requests from bots or non-browser User-Agents, which includes the validator.
- Authentication requirements — The resource may sit behind a login wall or require API keys or tokens.
- Geographic restrictions — The server may use geo-blocking that prevents access from the validator’s location.
Why This Matters
While this is typically an informational warning rather than a hard HTML error, it has practical implications:
- Incomplete validation — The validator can’t check the referenced resource for errors. For example, if a CSS file returns 403, the validator can’t verify the stylesheet for issues that might affect your page.
- Potential broken references — A 403 may indicate that the resource URL is incorrect or outdated, meaning real users could also experience issues depending on their browser or network configuration.
- Accessibility and reliability concerns — If the resource is intermittently blocked, some users may not receive critical stylesheets or scripts, leading to a degraded experience.
How to Fix It
- Verify the URL is correct — Double-check for typos or outdated paths.
- Host resources on your own server — Download the resource and serve it locally or from a CDN you control.
- Use a CDN that permits open access — Popular open CDNs like cdnjs, jsDelivr, or unpkg are designed to allow unrestricted access.
- Configure the remote server — If you control the server hosting the resource, whitelist the validator’s User-Agent or allow requests from validator.w3.org.
- Remove unnecessary references — If the resource isn’t actually needed, remove the element entirely.
Examples
Resource blocked by remote server
This references a stylesheet on a server that returns 403 to the validator:
<link rel="stylesheet" href="https://restricted-server.example.com/styles/main.css">
Fixed: Host the resource locally
Download the stylesheet and serve it from your own domain:
<link rel="stylesheet" href="/css/main.css">
Fixed: Use a publicly accessible CDN
Switch to a well-known open CDN that doesn’t block external requests:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
Script blocked by hotlink protection
<script src="https://protected-site.example.com/js/library.min.js"></script>
Fixed: Use a reliable public source
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
Image blocked by access restrictions
<img src="https://private-cdn.example.com/photos/banner.jpg" alt="Welcome banner">
Fixed: Host the image yourself
<img src="/images/banner.jpg" alt="Welcome banner">
Keep in mind that this warning only appears during validation—it doesn’t necessarily mean your end users are experiencing the same 403 error. Browsers send different headers (including cookies, referrers, and User-Agent strings) than the validator does, so the resource may load fine for visitors. However, it’s still good practice to ensure your referenced resources are reliably accessible and that validation can complete fully.
The aria-labelledby attribute creates a relationship between an element and the text content that labels it. It works by pointing to the id of one or more elements whose text should be used as the accessible name. When the validator reports that aria-labelledby must point to an element in the same document, it means at least one of the id values you referenced doesn’t correspond to any element on the page.
This typically happens for a few reasons:
- Typo in the id — the aria-labelledby value doesn’t exactly match the target element’s id (remember, IDs are case-sensitive).
- The referenced element was removed — the labeling element existed at some point but was deleted or moved, and the reference wasn’t updated.
- The id exists in a different document — aria-labelledby cannot reference elements across pages, iframes, or shadow DOM boundaries. The target must be in the same document.
- Dynamic content not yet rendered — the element is inserted by JavaScript after the validator parses the static HTML.
This is primarily an accessibility problem. Screen readers and other assistive technologies rely on aria-labelledby to announce meaningful labels to users. When the reference is broken, the element effectively has no accessible name, which can make it impossible for users to understand its purpose. Browsers won’t throw a visible error, so the issue can go unnoticed without validation or accessibility testing.
To fix the issue, verify that every id referenced in aria-labelledby exists in the same HTML document. Double-check spelling and casing. If you reference multiple IDs (space-separated), each one must resolve to an existing element.
Examples
Incorrect — referencing a non-existent id
The aria-labelledby attribute points to "dialog-title", but no element with that id exists:
<div role="dialog" aria-labelledby="dialog-title">
<h2 id="dlg-title">Confirm deletion</h2>
<p>Are you sure you want to delete this item?</p>
</div>
Correct — matching id values
Ensure the id in the referenced element matches exactly:
<div role="dialog" aria-labelledby="dialog-title">
<h2 id="dialog-title">Confirm deletion</h2>
<p>Are you sure you want to delete this item?</p>
</div>
Incorrect — referencing multiple IDs where one is missing
When using multiple IDs, every one must be present. Here, "note-desc" doesn’t exist:
<section aria-labelledby="note-heading note-desc">
<h3 id="note-heading">Important note</h3>
<p id="note-description">Please read carefully before proceeding.</p>
</section>
Correct — all referenced IDs exist
<section aria-labelledby="note-heading note-description">
<h3 id="note-heading">Important note</h3>
<p id="note-description">Please read carefully before proceeding.</p>
</section>
Incorrect — case mismatch
IDs are case-sensitive. "Main-Title" and "main-title" are not the same:
<nav aria-labelledby="Main-Title">
<h2 id="main-title">Site navigation</h2>
<ul>
<li><a href="/">Home</a></li>
</ul>
</nav>
Correct — consistent casing
<nav aria-labelledby="main-title">
<h2 id="main-title">Site navigation</h2>
<ul>
<li><a href="/">Home</a></li>
</ul>
</nav>
If you don’t have a visible labeling element on the page and don’t want to add one, consider using aria-label instead, which accepts a string value directly rather than referencing another element:
<nav aria-label="Site navigation">
<ul>
<li><a href="/">Home</a></li>
</ul>
</nav>
Ready to validate your sites?
Start your free trial today.