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

> Canonical HTML version: https://rocketvalidator.com/html-validation/bad-value-x-for-attribute-src-on-element-iframe-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 follows a specific structure defined by [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986). The general format is:

```
scheme://host/path?query#fragment
```

The `#` character serves as the delimiter that introduces the **fragment** portion of the URL. It may only appear once in this role. Once the parser encounters the first `#`, everything after it is treated as the fragment identifier. A second `#` within that fragment is an illegal character because the fragment production in the URL specification does not permit unescaped `#` characters.

This validation error commonly arises from:

- **Duplicate `#` characters** — e.g., accidentally including two hash marks like `/#?param=value#section`.
- **Misplaced query strings** — putting `?key=value` after the `#` instead of before it. While this may work in some single-page application routers, it results in the query being part of the fragment, and adding another `#` after that creates an invalid URL.
- **Copy-paste errors** — assembling URLs from multiple parts and inadvertently introducing an extra `#`.

This matters for several reasons. Browsers may handle malformed URLs inconsistently — some may silently truncate or ignore part of the URL, while others may fail to load the resource entirely. The W3C validator flags this because it violates the HTML specification's requirement that the `src` attribute contain a [valid URL](https://url.spec.whatwg.org/). Invalid URLs can also cause issues with assistive technologies, link sharing, and automated tools that parse your HTML.

### How to fix it

1. **Locate all `#` characters** in the URL and determine which one is the intended fragment delimiter.
2. **Remove any duplicate `#`** characters that were added by mistake.
3. **Move query parameters before the fragment** — the `?query` portion must come before the `#fragment` in a well-formed URL.
4. **Percent-encode if needed** — if a literal `#` must appear as data within the fragment or query value (not as a delimiter), encode it as `%23`.

## Examples

### Incorrect: multiple `#` characters

The second `#` inside the fragment is illegal:

```html
<iframe src="https://example.com/#?secret=123#abc"></iframe>
```

### Correct: query before fragment

Move the query string before the `#` so the URL has a proper structure:

```html
<iframe src="https://example.com/?secret=123#abc"></iframe>
```

### Correct: single fragment, no query

If you only need a fragment identifier, use a single `#`:

```html
<iframe src="https://example.com/#abc"></iframe>
```

### Correct: percent-encoding a literal `#` in a value

If the fragment itself must contain a `#` as data (not as a delimiter), percent-encode it:

```html
<iframe src="https://example.com/?secret=123#color=%23ff0000"></iframe>
```

Here, `%23ff0000` represents the literal string `#ff0000` within the fragment value, which is valid because the `#` is encoded.

### Incorrect: hash-based routing with duplicate `#`

Some single-page app embed URLs use hash-based routing, which can lead to accidental double hashes:

```html
<iframe src="https://app.example.com/#/dashboard#settings"></iframe>
```

### Correct: use a single fragment for the route

Restructure the URL to use a single `#` with a combined path:

```html
<iframe src="https://app.example.com/#/dashboard/settings"></iframe>
```

Or, if the application supports it, use standard path-based routing instead:

```html
<iframe src="https://app.example.com/dashboard/settings"></iframe>
```
