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

> Canonical HTML version: https://rocketvalidator.com/html-validation/bad-value-x-for-attribute-href-on-element-a-illegal-character-in-fragment-hash-is-not-allowed
> Attribution: Rocket Validator (https://rocketvalidator.com)
> License: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)

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)](https://url.spec.whatwg.org/) 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 `#`:

```html
<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 `#`:

```html
<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`:

```html
<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`:

```html
<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:

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

### ✅ Fixed: Encode the `#` in the query value

```html
<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.
