Skip to main content
HTML Validation

Bad value “X” for attribute href on element a: Illegal character in fragment: “#” is not allowed.

About This HTML Issue

A URL can have only one fragment identifier — the portion that comes after the single # symbol. When the browser encounters a # in a URL, it treats everything after it as the fragment. If a second # appears, it becomes an illegal character within that fragment because the fragment portion of a URL does not allow unencoded # characters. This violates the URL specification (WHATWG) and the HTML standard’s requirements for valid URLs in the href attribute.

This matters for several reasons:

  • Standards compliance: Browsers may handle malformed URLs inconsistently, leading to unpredictable navigation behavior across different environments.
  • Accessibility: Screen readers and assistive technologies rely on well-formed URLs to correctly announce link destinations and allow users to navigate.
  • SEO and crawling: Search engine crawlers may fail to follow or index pages with invalid URLs.

The # character is perfectly valid when used exactly once to introduce a fragment. For example, #pricing or /faqs#pricing are fine. The issue arises when a second # appears, such as /faqs#guarantee#pricing, or when # is used in a part of the URL where it isn’t expected.

If you genuinely need a literal # character as part of a path or query string (not as a fragment delimiter), you must percent-encode it as %23.

Examples

❌ Invalid: Multiple # characters in the URL

This triggers the validator error because the fragment (guarantee#pricing) contains an illegal #:

<a href="/faqs#guarantee#pricing">Guarantee and Pricing</a>

✅ Fixed: Use a single fragment identifier

Link to one section at a time with a single #:

<a href="/faqs#guarantee">Guarantee</a>
<a href="/faqs#pricing">Pricing</a>

✅ Fixed: Encode the # if it’s meant as a literal character

If the # is part of the actual path or data (not a fragment delimiter), encode it as %23:

<a href="/faqs%23guarantee#pricing">Pricing</a>

In this case, /faqs%23guarantee is the path (containing a literal #), and pricing is the fragment.

Using fragments correctly in a table of contents

Fragments work well for in-page navigation. Each target element needs a matching id:

<nav>
  <ul>
    <li><a href="#pricing">Pricing</a></li>
    <li><a href="#terms">Terms</a></li>
    <li><a href="#guarantee">Guarantee</a></li>
  </ul>
</nav>

<h2 id="pricing">Pricing</h2>
<p>All about pricing...</p>

<h2 id="terms">Terms</h2>
<p>You can find our terms at...</p>

<h2 id="guarantee">Guarantee</h2>
<p>We offer a guarantee...</p>

❌ Invalid: # in a query string or path without encoding

Sometimes this error appears when a URL accidentally includes an unencoded # in the wrong place:

<a href="/search?color=#ff0000">Red items</a>

✅ Fixed: Encode the # in the query value

<a href="/search?color=%23ff0000">Red items</a>

Here, %23ff0000 correctly represents the literal string #ff0000 as a query parameter value, rather than being misinterpreted as the start of a fragment.

Common causes

  • Copy-pasting URLs from other sources that contain multiple # characters.
  • Dynamically generated links where fragment values aren’t properly sanitized.
  • Color codes in URLs like #ff0000 that need to be percent-encoded as %23ff0000.
  • Concatenation errors in templates where a # is added both in the base URL and the appended fragment.

The fix is always the same: make sure your href contains at most one # used as the fragment delimiter, and percent-encode (%23) any # that is meant as a literal character.

Find issues like this automatically

Rocket Validator scans thousands of pages in seconds, detecting HTML issues across your entire site.

Help us improve our guides

Was this guide helpful?

Ready to validate your sites?
Start your trial today.