HTML Guides for a
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 fragment portion of a URL (everything after the # symbol) follows the same encoding rules as the rest of the URL — literal space characters are not permitted. When a browser encounters a space in a URL fragment, it may try to correct it automatically, but this behavior is not guaranteed across all browsers and contexts. Relying on browser error-correction leads to fragile links that may break unpredictably.
This issue matters for several reasons. First, it produces invalid HTML that fails W3C validation. Second, fragment navigation (jumping to a specific section of a page) may not work correctly if the browser doesn't auto-correct the space. Third, assistive technologies like screen readers rely on well-formed URLs to announce link destinations accurately. Finally, tools that parse or process HTML programmatically — such as crawlers, link checkers, and content management systems — may misinterpret or reject malformed URLs.
The most common scenario for this error is linking to an id attribute on the same page or another page where the id contains spaces. However, it's worth noting that id values themselves cannot contain spaces in valid HTML (a space in an id would make it multiple invalid tokens). So if you're writing both the link and the target, the best fix is often to correct the id itself by using hyphens or underscores instead of spaces.
How to Fix It
There are two main approaches:
- Percent-encode the spaces — Replace each space with
%20in thehrefvalue. - Use space-free identifiers — Change the target
idto use hyphens or camelCase, then update the fragment to match.
The second approach is strongly preferred because it fixes the root problem and produces cleaner, more readable markup.
Examples
❌ Invalid: Space in the fragment
<a href="https://example.com/page#some term">Go to section</a>
<a href="#my section">Jump to My Section</a>
✅ Fixed: Percent-encode the space
If you cannot control the target id (e.g., linking to an external page), percent-encode the space:
<a href="https://example.com/page#some%20term">Go to section</a>
✅ Better fix: Use a hyphenated id and matching fragment
When you control both the link and the target, use a space-free id:
<h2 id="my-section">My Section</h2>
<p>Some content here.</p>
<a href="#my-section">Jump to My Section</a>
❌ Invalid: Space in fragment linking to another page
<a href="/docs/getting-started#quick start guide">Quick Start Guide</a>
✅ Fixed: Matching hyphenated id
<!-- On the target page (/docs/getting-started): -->
<h2 id="quick-start-guide">Quick Start Guide</h2>
<!-- On the linking page: -->
<a href="/docs/getting-started#quick-start-guide">Quick Start Guide</a>
A note on id naming conventions
Since fragment identifiers reference id attributes, adopting a consistent id naming convention avoids this issue entirely. Common patterns include:
<!-- Hyphens (most common, used by many static site generators) -->
<section id="getting-started">
<!-- camelCase -->
<section id="gettingStarted">
<!-- Underscores -->
<section id="getting_started">
All three are valid and will never trigger a space-related validation error in your fragment links.
Spaces in the href attribute of an <a> element are not valid URL characters and must be encoded as %20.
URLs follow the syntax defined in RFC 3986, which does not allow literal space characters in any component of a URI. When a browser encounters a space in an href value, it often silently corrects it, but the HTML is still technically invalid. The W3C validator flags this because the raw space violates the URL specification.
To fix the issue, replace each space in the URL with %20. This is the percent-encoded form of the space character. If the URL points to a local file or path that genuinely contains spaces, the encoding is still required.
Some common situations where this appears:
- Links to files with spaces in their names, like
my document.pdf. - Paths that include folder names with spaces, like
/my folder/page.html. - Query parameters that contain unencoded spaces.
HTML examples
Invalid: space in href
<a href="/my folder/my document.pdf">Download</a>
Valid: spaces encoded as %20
<a href="/my%20folder/my%20document.pdf">Download</a>
If you control the file or directory names, renaming them to avoid spaces altogether is a simpler long term fix. Use hyphens or underscores instead:
<a href="/my-folder/my-document.pdf">Download</a>
Square brackets ([ and ]) are not valid characters in a URL path and must be percent-encoded.
The HTML specification requires that the href attribute on an <a> element contains a valid URL. According to RFC 3986, square brackets are reserved characters that are only permitted in the host component of a URI (specifically for IPv6 addresses like [::1]). When they appear in the path, query, or fragment components, they must be percent-encoded as %5B for [ and %5D for ].
This commonly happens with URLs that contain array-like query parameters (e.g., filter[status]=active) or paths that include brackets for some framework-specific routing convention.
To fix the issue, replace every [ with %5B and every ] with %5D in the URL path or query string.
HTML examples
Invalid: square brackets in the URL
<a href="/products?filter[color]=red&filter[size]=large">
Red, large products
</a>
Valid: percent-encoded square brackets
<a href="/products?filter%5Bcolor%5D=red&filter%5Bsize%5D=large">
Red, large products
</a>
The encoded version is functionally equivalent. Most web servers and frameworks will decode %5B and %5D back to [ and ] automatically when processing the request.
Square brackets ([ and ]) are not valid characters in URLs according to RFC 3986 and must be percent-encoded in the href attribute of <a> elements.
URLs have a strict set of allowed characters. Square brackets are reserved exclusively for IPv6 addresses in the host portion of a URL (e.g., http://[::1]/). Anywhere else in a URL, including query strings, they must be percent-encoded: [ becomes %5B and ] becomes %5D.
This issue commonly appears when linking to URLs that use square brackets in query parameters, a pattern found in some PHP frameworks and APIs (e.g., ?filter[name]=value). While most browsers handle unencoded brackets without problems, the HTML specification requires valid URLs, and the W3C validator flags the violation.
Invalid example
<a href="https://example.com/results?filter[status]=active&filter[role]=admin">
View results
</a>
Valid example
Replace [ with %5B and ] with %5D:
<a href="https://example.com/results?filter%5Bstatus%5D=active&filter%5Brole%5D=admin">
View results
</a>
The browser will decode the percent-encoded characters when sending the request, so the server receives the same query string either way.
The | (pipe) character is not allowed in URLs and must be percent-encoded as %7C in the href attribute of an <a> element.
URLs follow the syntax rules defined in RFC 3986. The pipe character is not among the allowed characters in the query component of a URI. When a URL contains a literal |, browsers will often handle it gracefully, but the HTML is technically invalid. The fix is to replace every | with its percent-encoded equivalent %7C.
This commonly appears when linking to external APIs, legacy systems, or content management platforms that use | as a delimiter in query strings.
HTML examples
Invalid example
<a href="https://example.com/search?filters=color|size|price">Search</a>
Valid example
<a href="https://example.com/search?filters=color%7Csize%7Cprice">Search</a>
If the URLs are generated dynamically in JavaScript, encodeURI() or encodeURIComponent() will handle the encoding automatically:
const filters = "color|size|price";
const url = `https://example.com/search?filters=${encodeURIComponent(filters)}`;
// Result: https://example.com/search?filters=color%7Csize%7Cprice
URLs follow strict syntax rules defined by RFC 3986, which does not permit literal space characters anywhere in a URI. When the W3C validator encounters a space in the href attribute of an <a> element — particularly within the query string (the part after the ?) — it flags it as an illegal character.
While most modern browsers will silently fix malformed URLs by encoding spaces for you, relying on this behavior is problematic for several reasons:
- Standards compliance: The HTML specification requires that
hrefvalues contain valid URLs. A space makes the URL syntactically invalid. - Interoperability: Not all user agents, crawlers, or HTTP clients handle malformed URLs the same way. Some may truncate the URL at the first space or reject it entirely.
- Accessibility: Screen readers and assistive technologies may struggle to interpret or announce links with invalid URLs.
- Link sharing and copy-pasting: If a user copies the link from the source or if the URL is used in an API or redirect, the unencoded space can cause breakage.
To fix this issue, replace every literal space character in the URL with %20. Within query string values, you can also use + as a space encoding (this is the application/x-www-form-urlencoded format commonly used in form submissions). If you're generating URLs dynamically, use your programming language's URL encoding function (e.g., encodeURIComponent() in JavaScript, urlencode() in PHP, or urllib.parse.quote() in Python).
Examples
Incorrect — spaces in the query string
<a href="https://example.com/search?query=hello world&lang=en">Search</a>
The space between hello and world is an illegal character in the URL.
Correct — space encoded as %20
<a href="https://example.com/search?query=hello%20world&lang=en">Search</a>
Correct — space encoded as + in query string
<a href="https://example.com/search?query=hello+world&lang=en">Search</a>
Using + to represent a space is valid within query strings and is commonly seen in URLs generated by HTML forms.
Incorrect — spaces in the path and query
<a href="https://example.com/my folder/page?name=John Doe">Profile</a>
Correct — all spaces properly encoded
<a href="https://example.com/my%20folder/page?name=John%20Doe">Profile</a>
Generating safe URLs in JavaScript
If you're building URLs dynamically, use encodeURIComponent() for individual parameter values:
<script>
const query = "hello world";
const url = "https://example.com/search?query=" + encodeURIComponent(query);
// Result: "https://example.com/search?query=hello%20world"
</script>
Note that encodeURIComponent() encodes spaces as %20, which is safe for use in any part of a URL. Avoid using encodeURI() for query values, as it does not encode certain characters like & and = that may conflict with query string syntax.
A URL in an href attribute contains a character that must be percent-encoded before it can appear in a query string.
URLs follow the syntax rules defined in RFC 3986. The query component of a URL (everything after the ?) allows a specific set of characters: unreserved characters (A-Z, a-z, 0-9, -, ., _, ~), sub-delimiters (!, $, &, ', (, ), *, +, ,, ;, =), plus :, @, /, and ?. Characters outside this set, such as {, }, |, ^, [, ], spaces, and non-ASCII characters like é or ñ, are illegal in their raw form and must be percent-encoded.
Percent-encoding replaces each disallowed byte with a % followed by its two-digit hexadecimal value. For example, a space becomes %20, a pipe | becomes %7C, and { becomes %7B.
Common causes of this error include:
- Pasting a URL directly from a browser address bar or CMS that displays decoded characters.
- Using curly braces for template placeholders in URLs without encoding them.
- Including spaces or non-ASCII characters in query parameter values.
HTML examples
Invalid: raw illegal characters in the query string
<a href="https://example.com/search?q=hello world&cat=sci|tech">Search</a>
The space between hello and world and the pipe | between sci and tech are not allowed in a URL query.
Valid: percent-encoded characters
<a href="https://example.com/search?q=hello%20world&cat=sci%7Ctech">Search</a>
Each illegal character is replaced with its percent-encoded equivalent: space is %20 and | is %7C.
A < character in an href attribute value is not allowed because it is a reserved character in URLs and must be percent-encoded.
The href attribute on an <a> element must contain a valid URL or URL fragment. Characters like <, >, {, }, and others are not permitted directly in URLs according to RFC 3986. The < character is especially problematic because browsers and parsers interpret it as the start of an HTML tag, which can break your markup and introduce security risks like XSS vulnerabilities.
This error commonly appears when template syntax (e.g., {{variable}}), unescaped user input, or malformed URLs end up inside an href value. If you genuinely need a < in a URL, it must be percent-encoded as %3C. Similarly, > should be encoded as %3E.
Example with the issue
<a href="https://example.com/search?q=<value>">Search</a>
How to fix it
Percent-encode the special characters in the URL:
<a href="https://example.com/search?q=%3Cvalue%3E">Search</a>
If you're generating URLs dynamically on the server side, use your language's built-in URL encoding function (e.g., encodeURIComponent() in JavaScript, urlencode() in PHP) to ensure all special characters are properly escaped before inserting them into href attributes.
The URL standard (defined by WHATWG) specifies a strict set of characters allowed in each part of a URL. A space character is not among them. When the validator encounters a literal space in an href value, it reports the error "Illegal character in scheme data: space is not allowed." This applies to spaces anywhere in the URL — the path, query string, fragment, or even after the scheme (e.g., https:).
While most modern browsers are forgiving and will attempt to fix malformed URLs by encoding spaces automatically, relying on this behavior is problematic for several reasons:
- Standards compliance: Invalid URLs violate the HTML specification, and markup that depends on browser error-correction is fragile and unpredictable.
- Accessibility: Assistive technologies, such as screen readers, may not handle malformed URLs the same way browsers do. This can result in broken links for users relying on these tools.
- Interoperability: Non-browser consumers of your HTML — search engine crawlers, link checkers, email clients, RSS readers, and APIs — may not perform the same auto-correction, leading to broken links or missed content.
- Copy-paste and sharing: When users copy a malformed URL from the source, the space can cause the link to break when pasted into other applications.
How to fix it
The fix depends on where the space appears:
- In the path or fragment: Replace each space with
%20. For example,/my file.htmlbecomes/my%20file.html. - In the query string: You can use
%20or, if the value is part ofapplication/x-www-form-urlencodeddata,+is also acceptable for spaces within query parameter values. However,%20is universally safe. - Programmatically: Use
encodeURI()in JavaScript to encode a full URL (it preserves structural characters like/,?, and#). UseencodeURIComponent()to encode individual query parameter values. On the server side, use your language's equivalent URL-encoding function.
If you're writing URLs by hand in HTML, simply find every space and replace it with %20. If URLs are generated dynamically (from a database, CMS, or user input), ensure your templating or server-side code encodes them before inserting into the markup.
Examples
Invalid — space in the path
<a href="https://example.com/docs/My Report.pdf">Download Report</a>
The literal space between "My" and "Report" triggers the validator error.
Fixed — space encoded as %20
<a href="https://example.com/docs/My%20Report.pdf">Download Report</a>
Invalid — space in a query parameter
<a href="https://example.com/search?q=hello world">Search</a>
Fixed — space encoded in the query string
<a href="https://example.com/search?q=hello%20world">Search</a>
Invalid — multiple spaces in different URL parts
<a href="https://example.com/my folder/page two.html?ref=some value#my section">Link</a>
Fixed — all spaces encoded
<a href="https://example.com/my%20folder/page%20two.html?ref=some%20value#my%20section">Link</a>
Encoding URLs with JavaScript
If you're building URLs dynamically, use the built-in encoding functions rather than doing manual string replacement:
<script>
// encodeURI encodes a full URL but preserves :, /, ?, #, etc.
const url = encodeURI("https://example.com/docs/My Report.pdf");
// Result: "https://example.com/docs/My%20Report.pdf"
// encodeURIComponent encodes a single value (for query params)
const query = encodeURIComponent("hello world");
// Result: "hello%20world"
</script>
Note that encodeURI() is appropriate for encoding a complete URL, while encodeURIComponent() should be used for individual components like query parameter values — it encodes characters such as / and ? that have structural meaning in a URL.
The W3C HTML Validator checks that URLs provided in href attributes conform to the URL specification. Square brackets ([ and ]) are reserved characters with very specific, limited uses in URLs — they are only permitted in the host portion of a URL to denote IPv6 addresses (e.g., http://[::1]/). When they appear elsewhere, such as in the scheme data, path, or query string without being percent-encoded, the URL is considered malformed.
This commonly happens in a few scenarios:
- Mailto links where someone wraps an email address in brackets, like
mailto:[user@example.com]. - Template variables that haven't been processed, leaving literal bracket syntax (e.g.,
{{,[,]) in the rendered HTML. - Manually constructed URLs where brackets are mistakenly used as part of the path or query string instead of being percent-encoded as
%5Band%5D.
Using invalid URLs can cause browsers to misinterpret the link destination, break navigation, or cause unexpected behavior. Assistive technologies such as screen readers also rely on well-formed URLs to correctly communicate link targets to users. Keeping your URLs standards-compliant ensures consistent, predictable behavior across all browsers and devices.
How to fix it
- Remove unnecessary brackets. If the brackets are not part of the actual data (e.g., decorative brackets around an email address), simply delete them.
- Percent-encode brackets when they are part of the data. If you genuinely need square brackets in a URL's path or query string, encode
[as%5Band]as%5D. - Check your templating engine output. If you use a templating system, inspect the final rendered HTML in a browser to make sure template syntax has been fully replaced with valid values.
Examples
Invalid: square brackets in a mailto URL
The brackets around the email address are not valid URL characters in this context.
<a href="mailto:[user@example.com]">Email Us</a>
Fixed: remove the brackets
<a href="mailto:user@example.com">Email Us</a>
Invalid: square brackets in a query string
<a href="https://example.com/search?filter[status]=active">Search</a>
Fixed: percent-encode the brackets
<a href="https://example.com/search?filter%5Bstatus%5D=active">Search</a>
Invalid: unprocessed template syntax in rendered HTML
If your templating engine fails to replace a variable, the final HTML may contain bracket characters:
<a href="mailto:[% user.email %]">Email Us</a>
Fixed: ensure the template renders a valid URL
Make sure the template variable resolves correctly. The rendered output should look like:
<a href="mailto:user@example.com">Email Us</a>
In your template source, this might be written as:
<a href="mailto:{{ user.email }}">Email Us</a>
The key is that the final HTML delivered to the browser must contain a valid, bracket-free URL (unless the brackets are properly percent-encoded). Always validate your rendered output, not just your template source, to catch issues like this.
A URL is made up of several parts: scheme, host (domain), path, query, and fragment. While some of these parts allow certain special characters (often percent-encoded), the host portion has strict rules. Domain names follow the DNS naming conventions, which only permit ASCII letters (a-z, A-Z), digits (0-9), hyphens (-), and dots (.) as label separators. Spaces are categorically forbidden.
This validation error typically occurs in two scenarios:
- A literal space appears in the domain, e.g.,
http://my domain.com. This is often a typo or a copy-paste error. - A percent-encoded space (
%20) appears in the domain, e.g.,http://my%20domain.com. While%20is valid in URL paths and query strings, it is not valid in the host portion. Percent-encoding does not make a space legal in a domain name — it still resolves to a space character, which DNS cannot handle.
Why this is a problem
- Broken links: Browsers cannot resolve a domain with spaces to an actual server. Users clicking the link will get an error or be taken nowhere.
- Accessibility: Screen readers and assistive technologies may announce the link, but users will encounter a dead end, creating a frustrating experience.
- Standards compliance: The WHATWG URL Standard explicitly forbids spaces in the host component. The W3C validator flags this to help you catch what is almost certainly a mistake.
- SEO impact: Search engine crawlers will treat the URL as invalid and will not follow or index it.
How to fix it
- Check for typos: The most common fix is to correct the domain to the actual, valid domain name you intended.
- Replace spaces with hyphens: If the intended domain genuinely has a word separator, the standard convention is to use hyphens (e.g.,
my-domain.com). - Remove spaces entirely: Sometimes spaces are accidentally introduced and simply need to be removed (e.g.,
mydomain.com). - Check the path vs. host: If the space belongs in a file path or query parameter rather than the domain, make sure it's in the correct part of the URL and properly percent-encoded there.
Examples
❌ Literal space in the domain
<a href="http://my domain.com/page">Visit site</a>
❌ Percent-encoded space in the domain
<a href="http://my%20domain.com/page">Visit site</a>
✅ Fixed: use a hyphen in the domain
<a href="http://my-domain.com/page">Visit site</a>
✅ Fixed: remove the space entirely
<a href="http://mydomain.com/page">Visit site</a>
✅ Spaces are fine in the path (percent-encoded)
Note that %20 is valid in the path portion of a URL — just not in the domain:
<a href="http://mydomain.com/my%20page">Visit page</a>
Common mistake: space before or after the domain
Sometimes the space is hard to spot because it's at the beginning or end of the URL, or between the scheme and domain:
<!-- ❌ Trailing space in domain -->
<a href="http://mydomain.com /page">Visit site</a>
<!-- ✅ Fixed -->
<a href="http://mydomain.com/page">Visit site</a>
If your URLs are generated dynamically (e.g., from a CMS or database), make sure to trim whitespace from the domain portion before constructing the full URL. A quick way to catch these issues during development is to validate your HTML regularly with the W3C Markup Validation Service.
The URL in this href has a slash or backslash where the parser did not expect one, usually too many slashes after the scheme or a backslash standing in for the // separator.
After a scheme like https: the URL syntax expects exactly two forward slashes before the host: https://example.com. Adding a third slash (https:///example.com) leaves an empty host, and writing the separator with backslashes (https:\\example.com) uses a character the URL grammar does not allow there. In both cases the validator reports an unexpected slash or backslash.
This often comes from string concatenation that joins a base URL already ending in / with a path that also starts with /, or from copying a path that used backslashes. Browsers may paper over some of these, but the link can still resolve to the wrong place or fail in non-browser clients.
Invalid example
<a href="https:///example.com/page">Visit the page</a>
Valid example
<a href="https://example.com/page">Visit the page</a>
Keep exactly two forward slashes after the scheme, and use forward slashes throughout the URL.
A URL in an href attribute contains a username, password, or both (e.g., http://user:pass@example.com), which the W3C validator rejects because embedded credentials in URLs are a security risk.
Browsers have largely deprecated support for credentials in URLs. When a URL like http://user:password@example.com/page appears in HTML, the credentials are visible in the page source and can be leaked through referrer headers, proxy logs, or browser history. The WHATWG URL Standard defines username and password as URL components, but the HTML specification forbids their use in href attributes. Most modern browsers will strip the credentials or show a warning prompt rather than silently authenticate.
Remove the credentials from the URL. If authentication is required, handle it server side (redirects, session tokens, or an authentication flow) rather than embedding secrets in markup.
HTML examples
Invalid: URL with embedded credentials
<a href="https://admin:s3cret@example.com/dashboard">Dashboard</a>
Valid: URL without credentials
<a href="https://example.com/dashboard">Dashboard</a>
The ping attribute specifies a space-separated list of URLs that the browser should notify (via a small POST request) when a user follows a hyperlink. This is commonly used for click tracking and analytics. According to the HTML specification, every URL in the list must be a valid, non-empty URL that uses either the http or https scheme — no other schemes or relative paths are permitted.
This restriction exists for practical and security reasons. The ping mechanism is specifically designed for web-based tracking endpoints, so only web protocols make sense. Relative URLs are disallowed because the ping is sent as a separate request independent of normal navigation, and the specification requires absolute URLs to unambiguously identify the target server. Using invalid values won't produce the intended tracking behavior and will cause the browser to silently ignore the ping attribute entirely.
From an accessibility and standards compliance standpoint, ensuring valid ping values means your analytics will work reliably in browsers that support the attribute. Note that browser support varies — some browsers (notably Firefox) disable ping by default or hide it behind a preference — so you should not rely on it as your sole tracking mechanism.
How to fix it
- Replace relative URLs with absolute URLs. If you have a value like
/trackortrack.php, prepend the full origin (e.g.,https://example.com/track). - Remove non-HTTP schemes. Values like
mailto:someone@example.comorftp://example.com/logare not valid forping. - Ensure each URL in the list is properly formatted. Multiple URLs must be separated by spaces (not commas or semicolons), and each one must be a complete
httporhttpsURL.
Examples
Incorrect: relative URL
<a href="https://example.com" ping="/track">Visit Example</a>
The value /track is a relative URL, which is not allowed in the ping attribute.
Incorrect: unsupported scheme
<a href="https://example.com" ping="ftp://example.com/log">Visit Example</a>
The ftp: scheme is not permitted — only http and https are valid.
Incorrect: comma-separated URLs
<a href="https://example.com" ping="https://example.com/track, https://analytics.example.com/log">Visit Example</a>
Multiple URLs must be space-separated, not comma-separated. The commas make each URL invalid.
Correct: single absolute URL
<a href="https://example.com" ping="https://example.com/track">Visit Example</a>
Correct: multiple space-separated absolute URLs
<a href="https://example.com" ping="https://example.com/track https://analytics.example.com/log">Visit Example</a>
Each URL is a fully qualified https URL, and they are separated by a single space. Both will receive a POST request when the link is clicked (in browsers that support the ping attribute).
The target attribute specifies where to display the linked resource. The HTML specification defines a set of reserved keywords that all begin with an underscore: _blank, _self, _parent, and _top. Any other value starting with an underscore is considered invalid because the underscore prefix is reserved for current and future keywords defined by the specification.
This matters for several reasons. First, browsers may handle unrecognized underscore-prefixed values inconsistently — some might treat them like _blank, while others might ignore them entirely or treat them as named browsing contexts. This leads to unpredictable behavior across different browsers. Second, using reserved but undefined keywords signals a likely typo or misunderstanding of the attribute, which could cause navigation to behave differently than intended. Standards compliance ensures your links work reliably for all users.
The valid keywords and their meanings are:
_self— Opens the link in the current browsing context (the default behavior)._blank— Opens the link in a new, unnamed browsing context (typically a new tab)._parent— Opens the link in the parent browsing context, or_selfif there is no parent._top— Opens the link in the topmost browsing context, or_selfif there is no ancestor.
If you need to target a specific named frame or window, simply use a name without a leading underscore. Any string that doesn't start with _ is treated as a valid named browsing context.
Examples
Incorrect: Invalid reserved keyword
These examples use underscore-prefixed values that are not recognized keywords:
<!-- Typo: "_blanks" is not a valid keyword -->
<a href="https://example.com" target="_blanks">Example</a>
<!-- "_new" is not a valid keyword -->
<a href="https://example.com" target="_new">Open in new tab</a>
<!-- "_tab" is not a valid keyword -->
<a href="https://example.com" target="_tab">Open link</a>
Correct: Using a valid keyword
<!-- Use "_blank" to open in a new tab -->
<a href="https://example.com" target="_blank">Example</a>
<!-- Use "_self" to open in the same tab (also the default) -->
<a href="https://example.com" target="_self">Example</a>
<!-- Use "_parent" to open in the parent frame -->
<a href="https://example.com" target="_parent">Example</a>
<!-- Use "_top" to open in the topmost frame -->
<a href="https://example.com" target="_top">Example</a>
Correct: Using a custom named browsing context
If you intend to target a specific named window or frame rather than using a keyword, remove the underscore prefix:
<!-- Valid: "myframe" is a custom browsing context name -->
<a href="https://example.com" target="myframe">Open in myframe</a>
<!-- Valid: targeting a named iframe -->
<iframe name="content-frame" src="about:blank"></iframe>
<a href="https://example.com" target="content-frame">Load in iframe</a>
A common mistake is using _new with the intention of opening a link in a new tab. While some browsers may treat _new similarly to _blank, it is not a valid keyword. Use _blank instead. Note that when using target="_blank", it's a good security practice to also include rel="noopener" (though modern browsers now do this by default):
<a href="https://example.com" target="_blank" rel="noopener">Example</a>
The type attribute on an <a> element is an advisory hint that tells the browser what media type (MIME type) to expect at the linked resource. A valid MIME type follows a strict format: a type, a / separator, and a subtype (e.g., text/html, application/pdf, image/png). Each part must consist of token characters — letters, digits, and certain symbols — but not spaces.
This validation error occurs when the MIME type value contains a space or other unexpected character in a position where only token characters or a / are allowed. Common causes include:
- Accidental spaces within the MIME type (e.g.,
application/ pdforapplication /pdf). - Multiple MIME types separated by spaces (e.g.,
text/html text/plain), which is not valid since the attribute accepts only a single MIME type. - Typos or copy-paste errors that introduce whitespace or non-token characters.
While the type attribute is purely advisory and browsers won't refuse to follow a link based on it, an invalid value defeats its purpose and signals sloppy markup. Standards-compliant HTML ensures your pages are interpreted consistently and avoids confusing tools, screen readers, or other user agents that may parse this attribute.
Examples
Incorrect: Space within the MIME type
<a href="report.pdf" type="application/ pdf">Download Report</a>
The space after the / makes this an invalid MIME type.
Incorrect: Multiple MIME types separated by a space
<a href="data.csv" type="text/csv text/plain">Download Data</a>
The type attribute only accepts a single MIME type. The space between text/csv and text/plain triggers the error.
Incorrect: Leading or trailing spaces
<a href="photo.jpg" type=" image/jpeg ">View Photo</a>
Spaces before or after the MIME type are not permitted.
Correct: Valid MIME type with no spaces
<a href="report.pdf" type="application/pdf">Download Report</a>
Correct: Other common valid MIME types
<a href="data.csv" type="text/csv">Download Data</a>
<a href="photo.jpg" type="image/jpeg">View Photo</a>
<a href="archive.zip" type="application/zip">Download Archive</a>
Correct: MIME type with a parameter
MIME types can include parameters separated by a semicolon — no spaces are required, though a single space after the semicolon is permitted per the MIME specification:
<a href="page.html" type="text/html; charset=utf-8">View Page</a>
How to Fix
- Inspect the
typevalue — look for any spaces within the type or subtype portions (before or after the/). - Remove extra spaces — ensure the value is a single, properly formatted MIME type like
type/subtype. - Use only one MIME type — if you've listed multiple types, pick the one that accurately describes the linked resource.
- Verify the MIME type is valid — consult the IANA Media Types registry to confirm you're using a recognized type.
- Consider removing the attribute — since
typeis purely advisory on<a>elements, if you're unsure of the correct MIME type, omitting the attribute entirely is perfectly valid.
A MIME type (also called a media type) always follows the format type/subtype, such as text/html, application/pdf, or image/jpeg. The "type" part indicates the general category (e.g., text, image, application, audio, video), and the "subtype" specifies the exact format within that category. When the validator reports "Subtype missing," it means the value you provided either lacks the /subtype portion or isn't a valid MIME type structure at all.
A common cause of this error is misunderstanding the purpose of the type attribute on <a> elements. The type attribute is not used to change the behavior or appearance of the link (the way type works on <input> or <button> elements). Instead, it serves as an advisory hint to the browser about what kind of resource the link points to. The browser may use this information to adjust its UI — for example, showing a download prompt for application/pdf — but it is not required to act on it.
Because of this misunderstanding, developers sometimes write type="button" on an <a> element, thinking it will make the link behave like a button. The value button is not a valid MIME type (it has no subtype), so the validator flags it. If you need a button, use a <button> element instead. If you need a styled link that looks like a button, keep the <a> element and use CSS for styling.
Why this matters
- Standards compliance: The HTML specification requires the
typeattribute on<a>to be a valid MIME type string. An invalid value violates the spec and may be ignored by browsers or cause unexpected behavior. - Accessibility and semantics: Using
type="button"on a link can create confusion about the element's role. Screen readers and assistive technologies rely on correct semantics to convey meaning to users. - Browser behavior: While browsers are generally forgiving, an invalid
typevalue provides no useful information and could interfere with how the browser handles the linked resource.
How to fix it
- If you intended to hint at the linked resource's MIME type, make sure you provide a complete
type/subtypevalue — for example,application/pdfrather than justapplication. - If you used
typeto try to style or change the link's behavior, remove thetypeattribute entirely. Use CSS for visual styling or switch to a more appropriate element like<button>. - If you don't need the
typeattribute, simply remove it. It's entirely optional on<a>elements.
Examples
Incorrect: missing subtype
<a href="report.pdf" type="application">Download report</a>
The value application is incomplete — it's missing the subtype portion after the slash.
Incorrect: not a MIME type at all
<a href="/order.php" type="button">Submit</a>
The value button is not a MIME type. This often stems from confusing the type attribute on <a> with the type attribute on <input> or <button>.
Correct: valid MIME type
<a href="report.pdf" type="application/pdf">Download report</a>
<a href="photo.jpeg" type="image/jpeg">See a photo</a>
The type attribute uses a properly formatted MIME type with both a type and subtype.
Correct: removing the attribute entirely
<a href="/order.php">Submit</a>
If the type attribute isn't serving a real purpose, the simplest fix is to remove it.
Correct: using a button element instead
<button type="submit">Submit</button>
If you need actual button behavior (such as submitting a form), use a <button> element rather than an <a> element with an invalid type.
According to the HTML specification, the <a> element can exist without an href attribute, but in that case it represents a placeholder where a link might otherwise have been placed. However, the validator flags this as an issue because an <a> element without href and without role is ambiguous — browsers won't treat it as a link (it won't be focusable or keyboard-accessible), and assistive technologies won't know how to present it to users.
This matters for several reasons:
- Accessibility: Without
href, the<a>element loses its implicitrole="link"and is no longer announced as a link by screen readers. It also won't appear in the tab order, making it invisible to keyboard users. - Semantics: If the element is styled and scripted to behave like a button or link but lacks the proper attributes, it creates a disconnect between what users see and what the browser understands.
- Standards compliance: The spec expects you to be explicit about the element's purpose when
hrefis absent.
The most common cause of this issue is using <a> elements as JavaScript-only click targets without providing an href, or using them as styled containers without any interactive purpose.
How to Fix It
There are several approaches depending on your intent:
- Add an
hrefattribute if the element should be a link. This is the most common and recommended fix. - Add a
roleattribute if you're deliberately using<a>withouthreffor a specific purpose, such asrole="button". - Use a different element entirely. If it's not a link, consider using a
<button>,<span>, or another semantically appropriate element.
Examples
❌ Missing both href and role
<a>Click here</a>
This triggers the validator error because the <a> has neither href nor role.
❌ JavaScript-only handler without href
<a onclick="doSomething()">Submit</a>
Even with an event handler, the element lacks href and role, so it fails validation and is inaccessible to keyboard users.
✅ Fix by adding an href
<a href="/about">About us</a>
Adding href makes it a proper hyperlink — focusable, keyboard-accessible, and recognized by screen readers.
✅ Fix by adding role for a non-link purpose
<a role="button" tabindex="0" onclick="doSomething()">Submit</a>
If you must use <a> without href as a button, add role="button" and tabindex="0" to ensure it's focusable and properly announced. However, consider using a real <button> instead.
✅ Better: use the right element
<button type="button" onclick="doSomething()">Submit</button>
If the element triggers an action rather than navigating somewhere, a <button> is the correct semantic choice. It's focusable by default, responds to keyboard events, and doesn't need extra attributes.
✅ Placeholder anchor (intentional non-link)
<a role="link" aria-disabled="true">Coming soon</a>
If you're intentionally showing a placeholder where a link will eventually appear, you can add a role and indicate its disabled state for assistive technologies. Alternatively, use a <span> with appropriate styling to avoid the issue altogether.
The HTML specification explicitly forbids nesting <a> elements inside other <a> elements. This is defined as part of the content model for the <a> element — it is "transparent" but must not contain any interactive content, which includes other <a> elements. When the validator encounters a closing </a> tag that would violate these nesting rules, it raises this error.
This typically happens in one of two scenarios:
- A missing closing
</a>tag — You forget to close one link, so the next link appears to be nested inside it. - Intentionally wrapping one link inside another — You try to place a clickable link inside a larger clickable area, which is invalid HTML.
Why this matters
When an <a> element is nested inside another <a> element, browsers must guess what you intended. Different browsers may handle this differently — some will auto-close the first link before starting the second, while others may produce unexpected DOM structures. This leads to:
- Unpredictable behavior — Click targets may not work as expected across different browsers.
- Accessibility issues — Screen readers rely on a well-structured DOM. Ambiguous nesting confuses assistive technologies and makes navigation difficult for users who depend on them.
- Broken styling — CSS selectors that depend on proper parent-child relationships may not apply correctly.
How to fix it
- Find the offending
<a>tags — The validator will point to the line with the problematic closing</a>. Look at that line and the lines above it to find where the nesting issue begins. - Add missing closing tags — If you forgot a
</a>, add it before the next<a>opens. - Restructure if needed — If you intended to have a link inside a larger clickable area, redesign the markup so that the links are siblings rather than nested.
Examples
❌ Missing closing tag causes implicit nesting
The first <a> is never closed, so the second <a> appears to be nested inside it:
<nav>
<a href="one.html">Page 1
<a href="two.html">Page 2</a>
</nav>
✅ Fixed by adding the missing closing tag
<nav>
<a href="one.html">Page 1</a>
<a href="two.html">Page 2</a>
</nav>
❌ Intentionally nesting links (invalid)
Wrapping a link inside a larger link is not allowed, even if it seems useful for UI purposes:
<a href="/article">
<h2>Article Title</h2>
<p>A short summary of the article.</p>
<a href="/author">Author Name</a>
</a>
✅ Fixed by restructuring with sibling links
Use CSS positioning or a different layout strategy to achieve the same visual result without nesting:
<article>
<a href="/article">
<h2>Article Title</h2>
<p>A short summary of the article.</p>
</a>
<p>By <a href="/author">Author Name</a></p>
</article>
❌ Forgetting to close links in a list
This is especially common in navigation menus built with lists:
<ul>
<li><a href="/home">Home</li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
Here, the first <a> is never closed. The </li> tag implicitly closes the <li>, but the <a> remains open, causing nesting issues with the subsequent links.
✅ Fixed by properly closing each link
<ul>
<li><a href="/home">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
A good habit is to write both the opening and closing <a> tags together before filling in the content. This prevents accidental omission and keeps your HTML well-structured.
The rel attribute on an <a> element has the value noindex, which is not a recognized link type. The validator matched it against the registered keywords and guessed it might be a misspelling of index.
In practice noindex is rarely a typo for index. Authors usually write rel="noindex" because they want to keep a page out of search results, but rel cannot do that. The attribute describes the relationship between the current document and the linked resource, not whether a page should be indexed.
Indexing is a page-level directive. To keep a page out of search results, add a robots meta tag to that page's <head>, or send the equivalent X-Robots-Tag HTTP header. Both apply to the page being excluded, not to a link pointing at it.
If your goal is to stop search engines from passing ranking through a link, the correct rel value is nofollow.
Invalid example
<a href="/private" rel="noindex">Members area</a>
Valid example
Use rel="nofollow" on the link when you want search engines to ignore it:
<a href="/private" rel="nofollow">Members area</a>
To keep the destination page itself out of search results, control indexing on that page instead:
<meta name="robots" content="noindex">
The HTML specification defines <a> elements as having a transparent content model, but with specific exceptions — most notably, they must not contain interactive content, which includes other <a> elements. This means you can never legally nest one link inside another, regardless of how deeply nested or how the markup is structured.
This error most commonly occurs for one of two reasons:
- A missing closing tag: You forgot to close a previous
<a>element with</a>, so the browser (and the validator) considers the next<a>tag to be nested inside the still-open one. - Intentionally nested links: You tried to place a link inside another link, perhaps to create a card component where both the card and an inner element are clickable.
Why this matters
- Unpredictable browser behavior: When browsers encounter nested
<a>elements, they attempt to fix the invalid markup using error-recovery algorithms, but different browsers may handle it differently. This can lead to broken links, missing content, or unexpected DOM structures. - Accessibility issues: Screen readers and other assistive technologies rely on a well-formed DOM. Nested links create confusing navigation — a user tabbing through links may encounter unexpected behavior or miss content entirely.
- Standards compliance: The WHATWG HTML specification explicitly states that
<a>elements must not have interactive content descendants, including other<a>elements.
Examples
Missing closing tag (most common cause)
The most frequent trigger is simply forgetting to close an <a> tag. The validator sees the second <a> as being inside the first:
<!-- ❌ Bad: first <a> is never closed -->
<nav>
<a href="one.html">Page 1
<a href="two.html">Page 2</a>
</nav>
Add the missing </a> to fix it:
<!-- ✅ Good: both <a> elements are properly closed -->
<nav>
<a href="one.html">Page 1</a>
<a href="two.html">Page 2</a>
</nav>
Intentionally nested links
Sometimes developers try to nest links when building clickable card components:
<!-- ❌ Bad: <a> nested inside another <a> -->
<a href="/article" class="card">
<h2>Article Title</h2>
<p>A short description of the article.</p>
<a href="/author">Author Name</a>
</a>
There are several valid approaches to fix this. One common pattern is to make the card a non-link container and use CSS to stretch the primary link over the entire card:
<!-- ✅ Good: no nested links, card is still fully clickable -->
<div class="card">
<h2><a href="/article" class="card-link">Article Title</a></h2>
<p>A short description of the article.</p>
<a href="/author">Author Name</a>
</div>
.card {
position: relative;
}
.card-link::after {
content: "";
position: absolute;
inset: 0;
}
.card a:not(.card-link) {
position: relative;
z-index: 1;
}
This technique uses a pseudo-element to expand the primary link's clickable area over the entire card, while the secondary link (Author Name) sits above it via z-index, remaining independently clickable.
Links wrapping block-level content
In HTML5, <a> elements can wrap block-level elements like <div> and <h2>, which is perfectly valid. Just make sure you don't accidentally nest another <a> inside:
<!-- ✅ Good: <a> wrapping block content with no nested links -->
<a href="/article">
<div class="card">
<h2>Article Title</h2>
<p>A short description of the article.</p>
</div>
</a>
Quick checklist
- Search your HTML for every
<atag and verify it has a corresponding</a>. - If you're generating HTML dynamically (with a CMS, templating engine, or JavaScript), check that your template logic doesn't produce unclosed or nested anchors.
- If you need multiple clickable areas within a single component, use the CSS pseudo-element overlay technique shown above instead of nesting links.
The HTML <table> element follows a rigid structure. A <table> can only contain <caption>, <colgroup>, <thead>, <tbody>, <tfoot>, and <tr> as direct children. A <tr> can only contain <td> and <th> elements. Placing an <a> tag (or any other inline/flow content) directly inside <table>, <tr>, or other table-structural elements violates this content model, triggering the "Start tag a seen in table" validation error.
This matters for several reasons. Browsers handle invalid table markup inconsistently — some may silently move the misplaced <a> element outside the table entirely, while others may render it in unexpected positions. This leads to unpredictable layouts and broken links. Screen readers and other assistive technologies rely on proper table structure to navigate content, so misplaced elements can make the page confusing or inaccessible. Additionally, search engine crawlers may not correctly associate the link with its intended context.
The fix is straightforward: always wrap inline content like <a> inside a <td> or <th> element. Every piece of visible content in a table must live inside a table cell.
Examples
❌ Incorrect: <a> directly inside <tr>
<table>
<tr>
<a href="/details">View details</a>
</tr>
</table>
The <a> is a direct child of <tr>, which only allows <td> and <th> children.
❌ Incorrect: <a> directly inside <table>
<table>
<a href="/details">View details</a>
<tr>
<td>Data</td>
</tr>
</table>
The <a> is a direct child of <table>, which does not allow inline content.
❌ Incorrect: <a> directly inside <tbody>
<table>
<tbody>
<a href="/details">View details</a>
<tr>
<td>Data</td>
</tr>
</tbody>
</table>
The <a> is placed directly inside <tbody>, which only allows <tr> and <script>/<template> elements.
✅ Correct: <a> inside a <td>
<table>
<tr>
<td>
<a href="/details">View details</a>
</td>
</tr>
</table>
✅ Correct: <a> inside a <th>
<table>
<thead>
<tr>
<th>
<a href="/details">View details</a>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>Data</td>
</tr>
</tbody>
</table>
✅ Correct: wrapping an entire row's content in a link
If you want to make an entire row clickable, place the <a> inside each <td> rather than wrapping the <tr>:
<table>
<tr>
<td><a href="/item/1">Product name</a></td>
<td><a href="/item/1">$19.99</a></td>
<td><a href="/item/1">In stock</a></td>
</tr>
</table>
This keeps the table structure valid while still providing clickable content across the row. You can use CSS to remove the visual link styling and make each <a> fill its entire cell for a seamless clickable-row effect.
An aria-label attribute on an <a> element is only valid when the link has an accessible role that supports naming — which means the <a> must have an href attribute or an explicit role that accepts a label.
When an <a> element lacks an href attribute, it has the implicit role of generic. The generic role is in the list of roles that do not support naming, so applying aria-label to it is invalid. This is because a generic element has no semantic meaning, and screen readers wouldn't know how to announce the label in a meaningful way.
The most common cause of this error is using <a> as a placeholder or JavaScript-only trigger without an href. An <a> with an href has the implicit role of link, which does support aria-label, so the error won't appear.
You have a few ways to fix this:
- Add an
hrefto make it a proper link (most common fix). - Add an explicit role that supports naming, such as
role="button", if the element acts as a button. - Use a
<button>instead if the element triggers an action rather than navigation. - Remove
aria-labelif it's not needed, and use visible text content instead.
HTML Examples
❌ Invalid: aria-label on an <a> without href
<a aria-label="Close menu" onclick="closeMenu()">✕</a>
The <a> has no href, so its implicit role is generic, which does not support naming.
✅ Fix option 1: Add an href
<a href="/close" aria-label="Close menu">✕</a>
✅ Fix option 2: Use a <button> instead
<button aria-label="Close menu" onclick="closeMenu()">✕</button>
✅ Fix option 3: Add an explicit role that supports naming
<a role="button" tabindex="0" aria-label="Close menu" onclick="closeMenu()">✕</a>
Using a <button> (option 2) is generally the best choice for interactive elements that perform actions rather than navigate to a URL.
The WAI-ARIA specification defines strict rules about which elements can be children of interactive roles. An element with role="button" is treated as an interactive control, and the <a> element (when it has an href) is also an interactive control. Nesting one interactive element inside another creates what's known as an interactive content nesting violation. Screen readers and other assistive technologies cannot reliably determine user intent when they encounter this pattern — should they announce a button, a link, or both? The result is unpredictable behavior that can make your content inaccessible.
This issue commonly appears when developers wrap links inside styled containers that have been given role="button", or when using component libraries that apply button semantics to wrapper elements containing anchor tags.
Beyond accessibility, browsers themselves handle nested interactive elements inconsistently. Some may ignore the outer interactive role, while others may prevent the inner link from functioning correctly. This makes the pattern unreliable even for users who don't rely on assistive technologies.
How to fix it
There are several approaches depending on your intent:
- If the element should navigate to a URL, use an
<a>element and style it to look like a button. Remove therole="button"from the parent or eliminate the parent wrapper entirely. - If the element should perform an action (not navigation), use a
<button>element or an element withrole="button", and handle the action with JavaScript. Remove the nested<a>tag. - If you need both a button container and a link, flatten the structure so they are siblings rather than nested.
When using role="button" on a non-button element, remember that you must also handle keyboard interaction (Enter and Space key presses) and include tabindex="0" to make it focusable.
Examples
❌ Incorrect: link nested inside a button role
<div role="button">
<a href="/dashboard">Go to Dashboard</a>
</div>
This triggers the validation error because the <a> is a descendant of an element with role="button".
✅ Fix 1: Use a styled link instead
If the intent is navigation, remove the button role and let the <a> element do the job. Style it to look like a button with CSS.
<a href="/dashboard" class="btn">Go to Dashboard</a>
This is the simplest and most semantically correct fix when the purpose is to navigate the user to another page.
✅ Fix 2: Use a real button for actions
If the intent is to trigger an action (not navigate), replace the entire structure with a <button> element.
<button type="button" class="btn" onclick="navigateToDashboard()">
Go to Dashboard
</button>
✅ Fix 3: Use a div with role="button" without nested interactive elements
If you need a custom button using role="button", make sure it contains no interactive descendants.
<div role="button" tabindex="0">
Go to Dashboard
</div>
When using this approach, you must also add keyboard event handlers for Enter and Space to match native button behavior.
❌ Incorrect: link inside a button element
The same principle applies to native <button> elements, not just elements with role="button":
<button>
<a href="/settings">Settings</a>
</button>
✅ Fixed: choose one interactive element
<a href="/settings" class="btn">Settings</a>
❌ Incorrect: deeply nested link
The error applies to any level of nesting, not just direct children:
<div role="button">
<span class="icon-wrapper">
<a href="/help">Help</a>
</span>
</div>
✅ Fixed: flatten the structure
<a href="/help" class="btn">
<span class="icon-wrapper">Help</span>
</a>
As a rule of thumb, every interactive element in your page should have a single, clear role. If something looks like a button but navigates to a URL, make it an <a> styled as a button. If it performs an in-page action, make it a <button>. Keeping these roles distinct ensures your HTML is valid, accessible, and behaves consistently across browsers and assistive technologies.
The HTML living standard defines the content model of the <a> element as "transparent," meaning it can contain whatever its parent element allows — with one critical exception: it must not contain any interactive content, which includes other <a> elements, <button>, <input>, <select>, <textarea>, and similar elements. This restriction exists at every level of nesting, not just direct children. If an <a> appears anywhere inside another <a>, even deeply nested within <div> or <span> elements, the markup is invalid.
Why This Is a Problem
Unpredictable browser behavior: When browsers encounter nested anchors, they don't agree on how to handle them. Most browsers will attempt to "fix" the invalid markup by automatically closing the outer <a> before opening the inner one, but the resulting DOM structure may not match your intentions at all. This means your page layout, styling, and link targets can all break in unexpected ways.
Accessibility failures: Screen readers rely on the DOM tree to announce links to users. Nested anchors create ambiguous link boundaries — assistive technology may announce the wrong link text, skip links entirely, or confuse users about which link they're activating. Keyboard navigation can also become unreliable, since tab order depends on a well-formed link structure.
Broken click targets: When links overlap, it's unclear which link should activate when a user clicks the shared area. This results in a poor user experience where clicks may navigate to the wrong destination.
Common Causes
This error often arises in a few typical scenarios:
- Card components where the entire card is wrapped in an
<a>, but individual elements inside (like a title or button) also need their own links. - Navigation menus generated by CMS platforms or templating systems that accidentally produce nested link structures.
- Copy-paste mistakes where anchor tags get duplicated during content editing.
How to Fix It
The core fix is straightforward: ensure no <a> element exists as a descendant of another <a> element. Depending on your situation, you can:
- Separate the links so they are siblings rather than nested.
- Remove the outer link if only the inner link is needed.
- Use CSS and JavaScript for card-style patterns where the entire container needs to be clickable but inner links must remain independent.
Examples
Incorrect: Nested Anchors
<a href="/products">
Browse our <a href="/products/new">new arrivals</a> today
</a>
This is invalid because the inner <a> is a descendant of the outer <a>.
Fix: Separate the Links
<p>
<a href="/products">Browse our products</a> or check out our
<a href="/products/new">new arrivals</a> today.
</p>
Incorrect: Clickable Card with a Nested Link
<a href="/post/123" class="card">
<h2><a href="/post/123">Article Title</a></h2>
<p>A brief summary of the article content.</p>
</a>
Fix: Remove the Redundant Inner Link
If both links point to the same destination, simply remove the inner one:
<a href="/post/123" class="card">
<h2>Article Title</h2>
<p>A brief summary of the article content.</p>
</a>
Fix: Card with Multiple Distinct Links
When a card needs an overall clickable area and independent inner links, avoid wrapping everything in an <a>. Instead, use CSS positioning to stretch the primary link over the card:
<div class="card">
<h2><a href="/post/123" class="card-link">Article Title</a></h2>
<p>A brief summary of the article content.</p>
<p>Published by <a href="/author/jane">Jane Doe</a></p>
</div>
.card {
position: relative;
}
.card-link::after {
content: "";
position: absolute;
inset: 0;
}
.card a:not(.card-link) {
position: relative;
z-index: 1;
}
This approach makes the entire card clickable via the stretched ::after pseudo-element on the primary link, while the author link remains independently clickable above it — all without nesting any <a> elements.
Incorrect: Deeply Nested Anchor
The restriction applies at any depth, not just direct children:
<a href="/page">
<div>
<span>
<a href="/other">Nested link</a>
</span>
</div>
</a>
Fix: Restructure to Avoid Nesting
<div>
<a href="/page">Main page link</a>
<span>
<a href="/other">Other link</a>
</span>
</div>
Always verify that your final markup contains no <a> element inside another <a>, regardless of how many elements sit between them. Running your HTML through the W3C validator after making changes will confirm the issue is resolved.
Validate at scale.
Ship accessible websites, faster.
Automated HTML & accessibility validation for large sites. Check thousands of pages against WCAG guidelines and W3C standards in minutes, not days.
Pro Trial
Full Pro access. Cancel anytime.
Start Pro Trial →Join teams across 40+ countries