HTML Guides for encoding
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 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.
Spaces in the href attribute of a <link> 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. The <link> element points to external resources such as stylesheets, icons, and preloaded files, and its href must be a valid URL. A browser often corrects the space on its own, but the markup is still invalid, so the validator flags it.
To fix the issue, replace each space in the URL with %20, the percent-encoded form of the space character. If the resource genuinely lives at a path that contains spaces, the encoding is still required for the reference to be valid.
This often appears with files whose names contain spaces, like an icon exported as Capture 2026-06-23.webp, or with folder names that include spaces.
HTML examples
Invalid: space in href
<link rel="icon" href="https://example.com/files/Capture 2026-06-23.webp">
Valid: spaces encoded as %20
<link rel="icon" href="https://example.com/files/Capture%202026-06-23.webp">
If you control the file or directory names, renaming them to avoid spaces is a simpler long term fix. Use hyphens or underscores instead:
<link rel="icon" href="https://example.com/files/capture-2026-06-23.webp">
A | (pipe) character in a link element's href attribute is not valid in a URL because it is not a legal character in a query string without being percent-encoded.
This error commonly appears when loading multiple Google Fonts in a single <link> tag using the older Google Fonts API v1 syntax, which used the pipe character to separate font families:
fonts.googleapis.com/css?family=Open+Sans|Roboto
The | character must be encoded as %7C to be valid in a URL. However, even with encoding, the Google Fonts API v1 no longer supports loading multiple families this way. The current Google Fonts API v2 (/css2) uses separate family parameters instead.
Bad example
<link rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Open+Sans|Roboto">
Good example
Use the Google Fonts API v2 with separate family parameters:
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Open+Sans&family=Roboto&display=swap">
If you are not using Google Fonts and cannot change the URL structure, percent-encode the pipe character:
<link rel="stylesheet"
href="https://example.com/css?param1=a%7Cparam2=b">
Square brackets are not valid characters in a URL path segment and must be percent-encoded in the poster attribute of a <video> element.
The poster attribute accepts a valid URL that points to an image displayed while the video is not playing or until the first frame loads. URLs follow RFC 3986, which restricts the characters allowed in path segments. Square brackets ([ and ]) are reserved characters that are only permitted in the host component of a URL (for IPv6 addresses). When they appear in a path, query, or fragment component, they must be percent-encoded as %5B for [ and %5D for ].
This applies to any HTML attribute that expects a URL, not just poster. The same rule covers src, href, action, and similar attributes.
Invalid example
<video poster="/images/thumbnail[1].jpg" controls>
<source src="video.mp4" type="video/mp4">
</video>
Fixed example
Replace [ with %5B and ] with %5D:
<video poster="/images/thumbnail%5B1%5D.jpg" controls>
<source src="video.mp4" type="video/mp4">
</video>
If you control the file names on the server, renaming the file to avoid square brackets entirely is a simpler long term fix:
<video poster="/images/thumbnail-1.jpg" controls>
<source src="video.mp4" type="video/mp4">
</video>
A URL follows a specific structure defined by RFC 3986. 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=valueafter 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. Invalid URLs can also cause issues with assistive technologies, link sharing, and automated tools that parse your HTML.
How to fix it
- Locate all
#characters in the URL and determine which one is the intended fragment delimiter. - Remove any duplicate
#characters that were added by mistake. - Move query parameters before the fragment — the
?queryportion must come before the#fragmentin a well-formed URL. - 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:
<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:
<iframe src="https://example.com/?secret=123#abc"></iframe>
Correct: single fragment, no query
If you only need a fragment identifier, use a single #:
<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:
<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:
<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:
<iframe src="https://app.example.com/#/dashboard/settings"></iframe>
Or, if the application supports it, use standard path-based routing instead:
<iframe src="https://app.example.com/dashboard/settings"></iframe>
A space character in the src attribute of an iframe is not valid in a URL. Spaces must be percent-encoded as %20 to produce a legal URL.
URLs follow the syntax rules defined in RFC 3986, which does not allow literal space characters anywhere in a URI, including the query string portion (the part after ?). When the HTML validator encounters a raw space in a URL attribute like src, it flags the value as malformed.
This commonly happens when query parameter values contain readable text, or when a URL is copied from a browser's address bar where the browser displays decoded spaces for readability. The fix is to replace each space with %20.
Some developers use + instead of %20 for spaces in query strings. The + convention originates from the application/x-www-form-urlencoded format used in HTML form submissions, and many servers accept it. However, %20 is the correct encoding according to RFC 3986 and will pass W3C validation without issues.
HTML examples
Invalid: space in the query string
<iframe
src="https://example.com/embed?title=my page&lang=en"
width="600"
height="400">
</iframe>
Valid: space encoded as %20
<iframe
src="https://example.com/embed?title=my%20page&lang=en"
width="600"
height="400">
</iframe>
When the W3C HTML Validator reports "Bad value X for attribute src on element iframe: Illegal character in query: [ is not allowed", it means your URL contains unencoded square brackets in the query portion (everything after the ?). While most modern browsers will silently handle these characters and load the resource anyway, the URL does not conform to the URI syntax defined in RFC 3986, which the HTML specification requires.
According to RFC 3986, square brackets are reserved characters that have a specific purpose: they are only permitted in the host component of a URI to denote IPv6 addresses (e.g., http://[::1]/). Anywhere else in the URI — including the query string — they must be percent-encoded. The HTML living standard (WHATWG) requires that URLs in attributes like src, href, and action be valid URLs, which means they must follow these encoding rules.
Why this matters
- Standards compliance: Invalid URLs cause W3C validation failures, which can indicate deeper issues in your markup.
- Interoperability: While mainstream browsers are forgiving, some HTTP clients, proxies, CDNs, or web application firewalls may reject or mangle URLs with unencoded square brackets.
- Consistent behavior: Percent-encoding reserved characters guarantees that the URL is interpreted the same way everywhere — in browsers, server logs, link checkers, and automated tools.
- Copy-paste reliability: When users or tools copy a URL from your HTML source, an already-encoded URL is less likely to break during transmission through email clients, messaging apps, or other systems.
How to fix it
Replace every occurrence of [ with %5B and ] with %5D within the query string of the URL. If you're generating URLs server-side or in JavaScript, use the language's built-in URL encoding functions rather than doing manual find-and-replace:
- JavaScript:
encodeURIComponent()or theURL/URLSearchParamsAPIs - PHP:
urlencode()orhttp_build_query() - Python:
urllib.parse.urlencode()orurllib.parse.quote()
These functions will automatically encode square brackets and any other reserved characters.
Examples
❌ Invalid — unencoded square brackets in query string
<iframe src="https://example.com/embed?filter[status]=active&filter[type]=video"></iframe>
The validator flags [ and ] as illegal characters in the query component.
✅ Valid — square brackets percent-encoded
<iframe src="https://example.com/embed?filter%5Bstatus%5D=active&filter%5Btype%5D=video"></iframe>
Replacing [ with %5B and ] with %5D resolves the error. The server receives the exact same parameter values — most server-side frameworks (PHP, Rails, etc.) automatically decode percent-encoded characters before processing them.
❌ Invalid — square brackets in a timestamp parameter
<iframe src="https://example.com/report?time=[2024-01-01]"></iframe>
✅ Valid — timestamp parameter properly encoded
<iframe src="https://example.com/report?time=%5B2024-01-01%5D"></iframe>
Generating encoded URLs in JavaScript
If you're setting the src dynamically, let the browser handle encoding for you:
<iframe id="report-frame"></iframe>
<script>
const url = new URL("https://example.com/embed");
url.searchParams.set("filter[status]", "active");
url.searchParams.set("filter[type]", "video");
document.getElementById("report-frame").src = url.toString();
</script>
The URLSearchParams API automatically percent-encodes the brackets, producing a valid URL in the src attribute.
A note on other elements
This same rule applies to any HTML attribute that accepts a URL — including href on <a> elements, action on <form> elements, and src on <script> or <img> elements. Whenever you place a URL in HTML, ensure all reserved characters in the query string are properly percent-encoded.
URLs follow strict syntax rules defined by RFC 3986. Within a URL's path segment, only a specific set of characters is allowed to appear literally. When a character falls outside this allowed set, it must be percent-encoded — represented as a % sign followed by two hexadecimal digits corresponding to the character's ASCII code. The W3C validator checks that every URL in your HTML conforms to these rules, and it flags any src value that contains raw illegal characters.
Characters that commonly trigger this error include:
| Character | Percent-encoded |
|---|---|
| (space) | %20 |
[ | %5B |
] | %5D |
{ | %7B |
} | %7D |
| | %7C |
^ | %5E |
` | %60 |
Other reserved characters like ?, #, @, !, $, &, ', (, ), *, +, ,, ;, and = also need encoding when used as literal data in a path segment rather than as URL delimiters. The % character itself must be encoded as %25 if it appears literally.
Why This Is a Problem
- Browser inconsistency: While many modern browsers silently fix malformed URLs, not all do. Some browsers, older user agents, or HTTP clients may fail to load the resource or interpret the URL differently, leading to broken images.
- Standards compliance: Invalid URLs violate the HTML specification, which requires that attribute values containing URLs conform to valid URL syntax.
- Interoperability: Servers, CDNs, proxies, and caching layers may handle illegal characters unpredictably, causing intermittent failures that are difficult to debug.
- Accessibility: If a URL is malformed and the image fails to load, users relying on assistive technologies may not receive the intended content, even when appropriate
alttext is provided.
How to Fix It
You have two main approaches:
- Percent-encode the illegal characters in the
srcvalue. Replace each offending character with its%XXequivalent. - Rename the file to use only URL-safe characters. Stick to lowercase letters, digits, hyphens (
-), underscores (_), and dots (.). This is the cleanest long-term solution.
If you're generating URLs programmatically, use your language's built-in URL encoding functions (e.g., encodeURIComponent() in JavaScript, urlencode() in PHP, or urllib.parse.quote() in Python).
Examples
Illegal characters in the filename
The square brackets in the src value are not allowed in a URL path segment:
<!-- ❌ Invalid: raw [ and ] in URL path -->
<img src="image[00].svg" alt="Company logo">
Fix by percent-encoding:
<!-- ✅ Valid: [ and ] are percent-encoded -->
<img src="image%5B00%5D.svg" alt="Company logo">
Fix by renaming the file:
<!-- ✅ Valid: filename uses only safe characters -->
<img src="image-00.svg" alt="Company logo">
Spaces in the filename
Spaces are one of the most common causes of this error:
<!-- ❌ Invalid: space in URL path -->
<img src="my photo.jpg" alt="Vacation photo">
<!-- ✅ Valid: space encoded as %20 -->
<img src="my%20photo.jpg" alt="Vacation photo">
<!-- ✅ Valid: filename uses a hyphen instead of a space -->
<img src="my-photo.jpg" alt="Vacation photo">
Curly braces in a template-like path
Sometimes filenames or paths contain curly braces from templating artifacts or naming conventions:
<!-- ❌ Invalid: raw { and } in URL path -->
<img src="icons/{home}.png" alt="Home icon">
<!-- ✅ Valid: curly braces percent-encoded -->
<img src="icons/%7Bhome%7D.png" alt="Home icon">
Best practice for file naming
The simplest way to avoid this error entirely is to adopt a consistent file naming convention that only uses URL-safe characters:
<!-- ✅ Valid: clean, URL-safe filenames -->
<img src="images/hero-banner-2024.webp" alt="Welcome banner">
<img src="photos/team_photo_01.jpg" alt="Our team">
Spaces in the src attribute of an img element are not valid URL characters and must be encoded as %20 or replaced with hyphens/underscores.
URLs follow the rules defined in RFC 3986, which does not allow literal space characters in any part of a URL. When a browser encounters a space in a src value, it may try to percent-encode it automatically, but this behavior is not guaranteed across all contexts. The W3C validator flags this because the HTML specification requires attribute values containing URLs to hold valid URL strings.
There are two ways to fix this: rename the file to remove spaces, or percent-encode the spaces as %20. Renaming is the better long term approach because it avoids encoding issues entirely and makes URLs easier to read and share.
Examples
Invalid: space in the file name
<img src="my photo.jpg" alt="A photo">
Valid: percent-encoded space
<img src="my%20photo.jpg" alt="A photo">
Valid: renamed file with no spaces
<img src="my-photo.jpg" alt="A photo">
URLs follow strict syntax rules defined by RFC 3986, which does not allow literal space characters. When the W3C validator encounters a space in the src attribute of an <img> element, it reports it as an illegal character in the scheme data. While most modern browsers will attempt to handle spaces in URLs by automatically encoding them, relying on this behavior is not standards-compliant and can lead to broken images in certain contexts, such as when URLs are processed by other tools, APIs, or older browsers.
This issue commonly arises when file names contain spaces (e.g., my photo.jpg) and the path is copied directly into the HTML. It can also happen when a URL is incorrectly constructed by concatenating strings with unencoded values.
Beyond validation, unencoded spaces can cause real problems. A URL with a space may break when shared, copied, or passed through redirects. Email clients and messaging apps may truncate the URL at the space. Search engine crawlers might fail to index the resource correctly.
How to fix it
There are several ways to resolve this issue:
- Percent-encode spaces as
%20— Replace every literal space in the URL with%20. - Rename the file — Remove spaces from file names entirely, using hyphens (
-), underscores (_), or camelCase instead. - Use proper URL encoding functions — If generating URLs dynamically (e.g., in JavaScript or a server-side language), use built-in encoding functions like
encodeURI()orencodeURIComponent().
Renaming files to avoid spaces is generally the best long-term practice, as it prevents encoding issues across your entire workflow.
Examples
❌ Invalid: spaces in the src attribute
<img src="images/my photo.jpg" alt="A vacation photo">
<img src="/assets/blog posts/header image.png" alt="Blog header">
Both of these will trigger the validation error because the src value contains literal space characters.
✅ Fixed: spaces encoded as %20
<img src="images/my%20photo.jpg" alt="A vacation photo">
<img src="/assets/blog%20posts/header%20image.png" alt="Blog header">
✅ Fixed: file renamed to remove spaces
<img src="images/my-photo.jpg" alt="A vacation photo">
<img src="/assets/blog-posts/header-image.png" alt="Blog header">
A note on + vs %20
You may see spaces encoded as + in some contexts (e.g., query strings in form submissions using application/x-www-form-urlencoded). However, + is not a valid substitute for a space in a URL path. Always use %20 for spaces in the src attribute.
<!-- ❌ Incorrect: + does not represent a space in a URL path -->
<img src="images/my+photo.jpg" alt="A vacation photo">
<!-- ✅ Correct -->
<img src="images/my%20photo.jpg" alt="A vacation photo">
The src attribute on a <script> element points to a URL whose path contains a character that is not allowed there unless it is percent-encoded.
A URL path may only use a restricted set of characters. Symbols such as ^, spaces, square brackets, and backticks have to be written in their percent-encoded form, so a caret becomes %5E. The validator names the exact character it rejected, so look at the path segment it points to and either encode that character or change the URL so it no longer appears.
A common source of the ^ character is a version range copied from a package manager. A specifier like @^2 belongs in a package.json file, not in a CDN URL. When you load a script straight from a CDN, pin an explicit version instead.
Invalid example
<script src="https://cdn.example.com/pkg@^2/dist/app.js"></script>
Valid example
<script src="https://cdn.example.com/pkg@2.1.0/dist/app.js"></script>
URLs follow strict syntax rules defined by RFC 3986, which does not permit literal space characters anywhere in the URI — including path segments, query strings, and fragment identifiers. When the W3C HTML Validator encounters a space in the src attribute of a <script> element, it flags it as an illegal character because the attribute value is not a valid URL.
While most modern browsers will silently fix this by encoding the space before making the request, relying on this behavior is problematic for several reasons:
- Standards compliance: The HTML specification requires that the
srcattribute contain a valid URL. A URL with a literal space is technically malformed. - Cross-browser reliability: Not all user agents, proxies, or CDNs handle malformed URLs the same way. What works in one browser may fail in another context.
- Interoperability: Other tools that consume your HTML — such as linters, crawlers, screen readers, and build pipelines — may not be as forgiving as browsers.
- Copy-paste and linking issues: Literal spaces in URLs cause problems when users copy links or when URLs appear in plain-text contexts like emails, where the space may break the URL in two.
How to fix it
You have three options, listed from most recommended to least:
- Rename the file or directory to eliminate spaces entirely (e.g., use hyphens or underscores). This is the cleanest solution.
- Percent-encode the space as
%20in thesrcattribute value. - Use a build tool or bundler that generates references with properly encoded or space-free paths automatically.
Avoid using + as a space replacement in path segments. The + character represents a space only in application/x-www-form-urlencoded query strings, not in URL path segments.
Examples
❌ Invalid: space in the path segment
<script src="https://example.com/media assets/app.js"></script>
The space between media and assets makes this an invalid URL.
✅ Fixed: percent-encode the space
<script src="https://example.com/media%20assets/app.js"></script>
Replacing the space with %20 produces a valid, standards-compliant URL.
✅ Better: rename to avoid spaces entirely
<script src="https://example.com/media-assets/app.js"></script>
Using a hyphen (or underscore) instead of a space is the preferred approach. It keeps URLs clean, readable, and free of encoding issues.
❌ Invalid: space in a local relative path
This issue isn't limited to absolute URLs. Relative paths trigger the same error:
<script src="js/my script.js"></script>
✅ Fixed: encode or rename the local file
<script src="js/my%20script.js"></script>
Or, better yet:
<script src="js/my-script.js"></script>
Multiple spaces and other special characters
If a URL contains multiple spaces or other special characters, each one must be individually encoded. For example, { becomes %7B and } becomes %7D. A quick reference for common characters:
| Character | Encoded form |
|---|---|
| Space | %20 |
[ | %5B |
] | %5D |
{ | %7B |
} | %7D |
<!-- Invalid -->
<script src="libs/my library [v2].js"></script>
<!-- Valid -->
<script src="libs/my%20library%20%5Bv2%5D.js"></script>
<!-- Best: rename the file -->
<script src="libs/my-library-v2.js"></script>
Note that this same rule applies to the src attribute on other elements like <img>, <iframe>, <audio>, and <video>, as well as the href attribute on <a> and <link>. Whenever you reference a URL in HTML, make sure it contains no literal spaces.
When the browser's HTML parser encounters a src attribute, it expects the value between the opening and closing quotes to be a valid URL. URLs have strict rules about which characters are permitted — a literal double quote (") is not one of them. If a " character appears inside the URL, the validator flags it as an illegal character in the query string (or other parts of the URL).
This error often doesn't stem from an intentionally embedded quote in the URL itself. Instead, it's usually a symptom of malformed HTML around the attribute. Common causes include:
- Stray quotes after the attribute value, such as writing
src="https://example.com/script.js"async""instead of properly separating attributes. - Accidentally doubled closing quotes, like
src="https://example.com/script.js"". - Boolean attributes given empty-string values incorrectly, such as
async""instead of justasync, which causes the parser to interpret the extra quotes as part of the precedingsrcvalue. - Copy-paste errors that introduce smart quotes (
"") or extra quotation marks into the URL.
This matters for several reasons. Malformed src attributes can cause the script to fail to load entirely, breaking functionality on your page. It also violates the HTML specification, which can lead to unpredictable behavior across different browsers as each parser tries to recover from the error differently.
To fix the issue, carefully inspect the <script> tag and ensure that:
- The
srcattribute value contains only a valid URL with no unescaped"characters. - The opening and closing quotes around the attribute value are properly balanced.
- Attributes are separated by whitespace — not jammed together.
- Boolean attributes like
asyncanddeferare written as bare keywords without values.
If you genuinely need a double quote character in a query string (which is rare), encode it as %22.
Examples
Incorrect — stray quotes between attributes
In this example, the async attribute is malformed as async"", which causes the parser to interpret the extra quotes as part of the src URL:
<script src="https://example.com/js/app.js?ver=3.1" async""></script>
Incorrect — doubled closing quote
Here, an extra " at the end of the src value creates an illegal character in the URL:
<script src="https://example.com/js/app.js?ver=3.1""></script>
Incorrect — smart quotes from copy-paste
Curly/smart quotes copied from a word processor are not valid HTML attribute delimiters and get treated as part of the URL:
<script src="https://example.com/js/app.js?ver=3.1"></script>
Correct — clean attributes with proper quoting
<script src="https://example.com/js/app.js?ver=3.1" async></script>
Correct — boolean attribute written as a bare keyword
Boolean attributes like async and defer don't need a value. Simply include the attribute name:
<script src="https://example.com/js/app.js?ver=3.1" defer></script>
While async="async" is technically valid per the spec, the cleanest and most common form is the bare attribute. Avoid empty-string patterns like async="" placed without proper spacing, as they can lead to the exact quoting errors described here.
Correct — full valid document
<!DOCTYPE html>
<html lang="en">
<head>
<title>Valid Script Example</title>
<script src="https://example.com/js/app.js?ver=3.1" async></script>
</head>
<body>
<p>Page content here.</p>
</body>
</html>
If you're generating <script> tags dynamically (through a CMS, template engine, or build tool), check the template source rather than just the rendered output. The stray quotes are often introduced by incorrect string concatenation or escaping logic in the server-side code.
Spaces in the src attribute of a <source> element are not valid URL characters and must be encoded or removed.
URLs follow strict syntax rules defined in RFC 3986. A space character is not permitted in any part of a URL path. When a file name or path contains spaces, you must replace each space with %20 (the percent-encoded form) or rename the file to avoid spaces altogether.
This applies to all elements that accept a URL, including <source>, <img>, <a>, <script>, and <link>. Browsers often handle spaces gracefully by encoding them automatically, but the HTML is still technically invalid and can cause issues in some contexts, such as when URLs are copied, shared, or processed by other tools.
The best practice is to avoid spaces in file names entirely. Use hyphens (-) or underscores (_) instead. If you can't rename the files, percent-encode the spaces.
Bad Example
<video controls>
<source src="videos/my cool video.mp4" type="video/mp4">
</video>
Good Example — Percent-Encoded Spaces
<video controls>
<source src="videos/my%20cool%20video.mp4" type="video/mp4">
</video>
Good Example — No Spaces in File Name
<video controls>
<source src="videos/my-cool-video.mp4" type="video/mp4">
</video>
The HTML document's character encoding was not declared before the parser encountered non-ASCII content, forcing the validator to restart parsing with UTF-8 encoding.
When a browser or validator processes an HTML document, it needs to know the character encoding as early as possible. If the encoding isn't declared — or is declared too late in the document — the parser may initially guess the wrong encoding and then have to restart when it detects UTF-8 content. This warning typically appears when:
- The
<meta charset="utf-8">declaration is missing entirely. - The
<meta charset="utf-8">tag is placed after other elements like<title>or<script>that contain non-ASCII characters (e.g., accented letters, emoji, or special symbols). - The server sends a conflicting or missing
Content-TypeHTTP header.
The <meta charset="utf-8"> tag must appear within the first 1024 bytes of the document and should be the first child of the <head> element, before any other elements that contain text content.
Incorrect Example
<!DOCTYPE html>
<html lang="fr">
<head>
<title>Café résumé</title>
<meta charset="utf-8">
</head>
<body>
<p>Bienvenue au café!</p>
</body>
</html>
Here, the <title> contains non-ASCII characters (é) before the charset declaration, triggering the reparsing warning.
Fixed Example
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>Café résumé</title>
</head>
<body>
<p>Bienvenue au café!</p>
</body>
</html>
Moving <meta charset="utf-8"> to the very first position inside <head> ensures the parser knows the encoding before it encounters any non-ASCII characters.
Changing the character encoding declaration too late in the document prevents the browser from processing it correctly. The <meta charset> declaration must appear within the first 1024 bytes of the HTML document, and specifically before any non-ASCII content.
When a browser parses an HTML document, it reads the bytes as a stream. If it encounters a <meta charset> tag after it has already started interpreting content, it would need to go back and re-parse everything from the beginning — this is "non-streamable behavior." To avoid this, the HTML specification requires that the charset declaration appear very early in the document.
The most common causes of this error are:
- Placing
<meta charset>after other large<meta>tags, long<title>content, or<script>blocks in the<head>. - Placing
<meta charset>after content that pushes it beyond the 1024-byte boundary. - Including it in the
<body>instead of the<head>.
The fix is simple: make <meta charset="utf-8"> the very first element inside <head>, before any other elements.
Incorrect Example
<!DOCTYPE html>
<html lang="en">
<head>
<title>A very long title that takes up many bytes and pushes the charset declaration further down in the document stream...</title>
<meta name="description" content="A very long description with lots of text that consumes bytes before the charset is declared...">
<meta charset="utf-8">
</head>
<body>
<p>Hello world</p>
</body>
</html>
Corrected Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>A very long title that takes up many bytes...</title>
<meta name="description" content="A very long description with lots of text...">
</head>
<body>
<p>Hello world</p>
</body>
</html>
Always keep <meta charset="utf-8"> as the first child of <head>. This ensures the browser knows the encoding before it processes any other content.
When the validator parses your HTML (especially in XHTML mode or when serialized as XML), every element name must conform to the XML 1.0 naming rules. These rules require that element names begin with a letter (a–z, A–Z) or an underscore (_), followed by any combination of letters, digits, hyphens (-), underscores, periods (.), or combining characters. Characters like spaces, angle brackets, slashes, or other special symbols within a tag name make it unrepresentable in XML 1.0.
This error most commonly occurs due to:
- Typos in tag names — accidentally inserting a space, extra character, or symbol into a tag name.
- Malformed closing tags — forgetting the slash or placing characters incorrectly in a closing tag.
- Template syntax errors — template engine placeholders leaking into the final HTML output.
- Copy-paste issues — invisible or non-ASCII characters sneaking into tag names from rich-text editors.
This matters because browsers may not parse malformed tags as intended, leading to broken layouts or missing content. Screen readers and assistive technologies rely on well-formed markup to interpret page structure. Additionally, any system that processes your HTML as XML (such as RSS feed generators, EPUB renderers, or XHTML-serving environments) will reject documents with invalid element names entirely.
How to Fix
- Inspect the flagged line — look carefully at the element name the validator is complaining about. Check for stray characters, spaces, or symbols.
- Correct any typos — replace the malformed tag with the correct HTML element name.
- Validate template output — if you use a templating engine, ensure the rendered HTML doesn't contain unprocessed template tokens inside tag names.
- Check for invisible characters — paste the tag name into a plain-text editor or use a hex viewer to spot hidden characters.
Examples
Typo with a space in the tag name
A space inside the tag name creates an invalid element name:
<!-- Wrong: space in the element name -->
<di v class="container">
<p>Hello world</p>
</di v>
Fix by removing the accidental space:
<!-- Correct -->
<div class="container">
<p>Hello world</p>
</div>
Special character in a tag name
An accidental special character makes the name unrepresentable in XML 1.0:
<!-- Wrong: stray hash character in the tag name -->
<s#ection>
<h2>About</h2>
</s#ection>
Fix by using the correct element name:
<!-- Correct -->
<section>
<h2>About</h2>
</section>
Malformed closing tag
A missing or misplaced slash can produce a garbled tag name:
<!-- Wrong: slash is in the wrong place -->
<p>Some text<p/>
Fix with a properly formed closing tag:
<!-- Correct -->
<p>Some text</p>
Template placeholder leaking into output
Unprocessed template syntax can produce invalid element names in the rendered HTML:
<!-- Wrong: unresolved template variable in element name -->
<{{tagName}}>Content</{{tagName}}>
Ensure your template engine resolves the variable before serving the HTML. The rendered output should be:
<!-- Correct: after template processing -->
<article>Content</article>
The HTML specification explicitly forbids certain Unicode code points from appearing anywhere in an HTML document. These include most ASCII control characters (such as U+0000 NULL, U+0008 BACKSPACE, or U+000B VERTICAL TAB), as well as Unicode noncharacters like U+FFFE, U+FFFF, and the range U+FDD0 to U+FDEF. When the W3C validator encounters one of these code points, it reports the error "Forbidden code point" followed by the specific value.
These characters are forbidden because they have no defined meaning in HTML and can cause unpredictable behavior across browsers and platforms. Some may be silently dropped, others may produce rendering glitches, and some could interfere with parsing. Screen readers and other assistive technologies may also behave erratically when encountering these characters, making this an accessibility concern as well.
How forbidden characters get into your code
- Copy-pasting from external sources like word processors, PDFs, or databases that embed invisible control characters.
- Faulty text editors or build tools that introduce stray bytes during file processing.
- Incorrect character encoding where byte sequences are misinterpreted, resulting in forbidden code points.
- Programmatic content generation where strings aren't properly sanitized before being inserted into HTML.
How to fix it
- Identify the character and its location. The validator message includes the code point (e.g.,
U+000B) and the line number. Use a text editor that can show invisible characters (such as VS Code with the "Render Whitespace" or "Render Control Characters" setting enabled, or a hex editor). - Remove or replace the character. In most cases, the forbidden character serves no purpose and can simply be deleted. If it was standing in for a space or line break, replace it with the appropriate standard character.
- Sanitize content at the source. If your HTML is generated dynamically, strip forbidden code points from strings before outputting them. In JavaScript, you can use a regular expression to remove them.
// Remove common forbidden code points
text = text.replace(/[\x00-\x08\x0B\x0E-\x1F\x7F\uFDD0-\uFDEF\uFFFE\uFFFF]/g, '');
Examples
Incorrect — contains a forbidden control character
In this example, a vertical tab character (U+000B) is embedded between "Hello" and "World." It is invisible in most editors but the validator will flag it.
<!-- The ␋ below represents U+000B VERTICAL TAB, an invisible forbidden character -->
<p>Hello␋World</p>
Correct — forbidden character removed
<p>Hello World</p>
Incorrect — NULL character in an attribute value
A U+0000 NULL character may appear inside an attribute, often from programmatic output.
<!-- The attribute value contains a U+0000 NULL byte -->
<div title="Some�Text">Content</div>
Correct — NULL character removed from attribute
<div title="SomeText">Content</div>
Allowed control characters
Not all control characters are forbidden. The following are explicitly permitted in HTML:
U+0009— Horizontal tab (regular tab character)U+000A— Line feed (newline)U+000D— Carriage return
<pre>Line one
Line two with a tab</pre>
This is valid because it uses only standard whitespace characters (U+000A for the newline and U+0009 for the tab).
A < character appearing where an attribute name is expected typically means a closing > is missing on the previous tag, causing the browser to interpret the next tag as an attribute.
This error occurs when you forget to close an HTML element's opening tag with >. The validator sees the < of the next element and thinks it's still parsing attributes of the previous element. It's a common typo that can cascade into multiple confusing errors.
For example, if you write <div without the closing >, the following <p> tag gets parsed as if it were an attribute of the div, triggering this error.
HTML Examples
❌ Incorrect
<div class="container"
<p>Hello, world!</p>
</div>
The <div> tag is missing its closing > after "container", so the validator sees <p> as part of the div's attribute list.
✅ Correct
<div class="container">
<p>Hello, world!</p>
</div>
Make sure every opening tag is properly closed with >. If the error points to a specific line, check the tag immediately before that line for a missing >.
When a browser or validator reads your HTML document, it looks at the <meta charset="..."> declaration to determine how to decode the bytes in the file. Every character encoding maps bytes to characters differently. UTF-8 and Windows-1252 share the same mappings for basic ASCII characters (letters A–Z, digits, common punctuation), but they diverge for bytes in the 0x80–0x9F range. Windows-1252 uses these bytes for characters like €, ", ", —, and ™, while UTF-8 treats them as invalid or interprets them as parts of multi-byte sequences. When the declared encoding doesn't match the actual encoding, the validator raises this error, and browsers may render characters incorrectly.
This is a problem for several reasons:
- Broken text display: Characters like curly quotes (
" "), em dashes (—), and accented letters (é,ñ) can appear as mojibake — sequences likeâ€"oré— confusing your readers. - Standards compliance: The HTML specification requires that the declared encoding match the actual byte encoding of the file. A mismatch is a conformance error.
- Accessibility: Screen readers and other assistive technologies rely on correct character interpretation. Garbled text is unintelligible to these tools.
- Search engines: Encoding mismatches can cause search engines to index corrupted text, hurting your content's discoverability.
How to fix it
The best approach is to re-save your file in UTF-8 encoding. Most modern text editors and IDEs support this:
- VS Code: Click the encoding indicator in the bottom status bar (it may say "Windows 1252"), select "Save with Encoding," and choose "UTF-8."
- Sublime Text: Go to File → Save with Encoding → UTF-8.
- Notepad++: Go to Encoding → Convert to UTF-8, then save the file.
- Vim: Run
:set fileencoding=utf-8then:w.
After re-saving, make sure your <meta charset="utf-8"> declaration remains in the <head>. The <meta charset> tag should appear as early as possible — ideally as the first element inside <head> — because the browser needs to know the encoding before parsing any other content.
If your workflow or legacy system absolutely requires Windows-1252 encoding, you can change the declaration to <meta charset="windows-1252"> instead. However, this is strongly discouraged. UTF-8 is the universal standard for the web, supports virtually all characters from all languages, and is recommended by the WHATWG HTML specification.
Examples
Incorrect — encoding mismatch triggers the error
The file is saved in Windows-1252, but the meta tag declares UTF-8:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Page</title>
</head>
<body>
<!-- The byte 0x93 in Windows-1252 represents " but is invalid in UTF-8 -->
<p>She said, "Hello!"</p>
</body>
</html>
This produces the validator error: Internal encoding declaration "utf-8" disagrees with the actual encoding of the document ("windows-1252").
Correct — file saved as UTF-8 with matching declaration
Re-save the file in UTF-8 encoding. The meta tag and the file's byte encoding now agree:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Page</title>
</head>
<body>
<p>She said, "Hello!"</p>
</body>
</html>
Alternative — declaration changed to match Windows-1252 file
If you cannot change the file encoding, update the charset declaration to match. This eliminates the mismatch error but is not the recommended approach:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="windows-1252">
<title>My Page</title>
</head>
<body>
<p>She said, "Hello!"</p>
</body>
</html>
Tips for preventing this issue
- Configure your editor to default to UTF-8 for all new files.
- If you copy text from Microsoft Word or other desktop applications, be aware that they often use Windows-1252 curly quotes and special characters. Pasting this text into a UTF-8 file is fine as long as your editor properly converts the characters to UTF-8 bytes when saving.
- Use
<meta charset="utf-8">as the very first element inside<head>so the encoding is established before the browser encounters any other content. - If your server sends an HTTP
Content-Typeheader with acharsetparameter, make sure it also matches — for example,Content-Type: text/html; charset=utf-8.
When a browser loads an HTML document, it needs to know which character encoding to use to correctly interpret the bytes in the file. The <meta> tag's charset attribute (or the http-equiv="Content-Type" declaration) tells the browser what encoding to expect. If this declaration says windows-1251 but the file is actually saved as utf-8, the browser faces conflicting signals — the declared encoding disagrees with the actual byte content.
This mismatch matters for several reasons:
- Broken text rendering: Characters outside the basic ASCII range (such as accented letters, Cyrillic, CJK characters, emoji, and special symbols) may display as garbled or replacement characters (often seen as
Ðsequences,�, or other mojibake). - Data integrity: Form submissions and JavaScript string operations may produce corrupted data if the browser interprets the encoding incorrectly.
- Standards compliance: The WHATWG HTML Living Standard requires that the encoding declaration match the actual encoding of the document. Validators flag this mismatch as an error.
- Inconsistent behavior: Different browsers may handle the conflict differently — some may trust the
<meta>tag, others may sniff the actual encoding — leading to unpredictable results across user agents.
How to fix it
Determine the actual encoding of your file. Most modern text editors (VS Code, Sublime Text, Notepad++) display the file encoding in the status bar. If your file is saved as UTF-8 (which is the recommended encoding for all new web content), your
<meta>tag must reflect that.Update the
<meta>tag to declareutf-8instead ofwindows-1251.Prefer the shorter
charsetsyntax introduced in HTML5, which is simpler and equivalent to the olderhttp-equivform.Place the encoding declaration within the first 1024 bytes of the document, ideally as the first element inside
<head>, so the browser encounters it before parsing other content.
Examples
❌ Incorrect: declared encoding doesn't match actual file encoding
The file is saved as UTF-8 but the <meta> tag declares windows-1251:
<head>
<meta charset="windows-1251">
<title>My Page</title>
</head>
Or using the older http-equiv syntax:
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>My Page</title>
</head>
✅ Correct: declared encoding matches the actual UTF-8 file
Using the modern HTML5 charset attribute:
<head>
<meta charset="utf-8">
<title>My Page</title>
</head>
Or using the equivalent http-equiv form:
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>My Page</title>
</head>
✅ Correct: full document example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Page</title>
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
What if you actually need windows-1251?
If your content genuinely requires windows-1251 encoding (for example, a legacy Cyrillic text file), you need to re-save the file in windows-1251 encoding using your text editor. However, UTF-8 is strongly recommended for all web content because it supports every Unicode character and is the default encoding for HTML5. Converting your file to UTF-8 and updating the <meta> tag accordingly is almost always the better path forward.
HTML documents must use UTF-8 as their character encoding. The legacy encoding iso-8859-15 is no longer allowed in modern HTML.
The HTML living standard requires all documents to be encoded in UTF-8. Older encodings like iso-8859-15 (also known as Latin-9) were common in the past, especially for Western European languages, but they are now considered legacy. UTF-8 supports virtually all characters from every writing system, making it the universal standard for the web.
To fix this, you need to do two things. First, update the <meta> charset declaration in your HTML to specify UTF-8. Second — and this is the important part — you must actually save or convert the file itself to UTF-8 encoding. Simply changing the meta tag without re-encoding the file can cause characters like é, ñ, or € to display incorrectly.
Most modern code editors (VS Code, Sublime Text, Notepad++) let you change the file encoding. In VS Code, click the encoding label in the bottom status bar and choose "Save with Encoding" → "UTF-8".
If your server is sending an iso-8859-15 charset in the HTTP Content-Type header, you'll also need to update that. The HTTP header takes precedence over the meta tag.
HTML Examples
❌ Incorrect: legacy encoding
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="iso-8859-15">
<title>Mon site</title>
</head>
<body>
<p>Bienvenue sur mon site € 2025</p>
</body>
</html>
✅ Correct: UTF-8 encoding
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>Mon site</title>
</head>
<body>
<p>Bienvenue sur mon site € 2025</p>
</body>
</html>
If you're using an Apache server, update your .htaccess or server config:
AddDefaultCharset UTF-8
For Nginx, update the server block:
charset utf-8;
All HTML documents must use UTF-8 character encoding. The shift_jis encoding is a legacy Japanese encoding that is no longer valid in modern HTML.
The HTML specification requires UTF-8 as the only permitted character encoding for HTML documents. This rule applies regardless of the document's language or the characters it contains. Legacy encodings like shift_jis, euc-jp, iso-8859-1, and others are not allowed.
To fix this, two things need to happen. First, the <meta> charset declaration must specify UTF-8. Second, the file itself must actually be saved with UTF-8 encoding. Declaring UTF-8 in the markup while the file is saved as Shift_JIS will cause garbled text (mojibake). Most modern text editors and IDEs (VS Code, Sublime Text, Notepad++) can convert a file's encoding through a "Save with Encoding" or "Reopen with Encoding" option.
If a server sends a Content-Type header with charset=shift_jis, that header also needs to be updated to charset=utf-8. The HTTP header takes precedence over the <meta> tag, so fixing only the HTML is not enough if the server overrides it.
HTML examples
Before (invalid)
<!doctype html>
<html lang="ja">
<head>
<meta charset="shift_jis">
<title>Example</title>
</head>
<body>
<p>日本語のテキスト</p>
</body>
</html>
After (valid)
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Example</title>
</head>
<body>
<p>日本語のテキスト</p>
</body>
</html>
Make sure the file is also saved as UTF-8 in your text editor. In VS Code, click the encoding label in the bottom status bar and select "Save with Encoding" then "UTF-8".
When a browser or validator reads your HTML file, it interprets the raw bytes according to a character encoding — most commonly UTF-8. Each encoding has rules about which byte sequences are valid. For example, in UTF-8, bytes above 0x7F must follow specific multi-byte patterns. If the validator encounters a byte or sequence of bytes that violates these rules, it reports a "malformed byte sequence" error because it literally cannot decode the bytes into meaningful characters.
This problem commonly arises in a few scenarios:
- Encoding mismatch: Your file is saved as Windows-1252 (or Latin-1, ISO-8859-1) but the document declares UTF-8, or vice versa. Characters like curly quotes (
""), em dashes (—), or accented letters (é,ñ) are encoded differently across these encodings, producing invalid byte sequences when interpreted under the wrong one. - Copy-pasting from word processors: Content copied from Microsoft Word or similar applications often includes "smart quotes" and special characters encoded in Windows-1252, which can produce malformed bytes in a UTF-8 file.
- File corruption: The file was partially corrupted during transfer (e.g., FTP in the wrong mode) or by a tool that modified it without respecting its encoding.
- Mixed encodings: Parts of the file were written or appended using different encodings, resulting in some sections containing invalid byte sequences.
This is a serious problem because browsers may display garbled text (mojibake), skip characters entirely, or substitute replacement characters (�). It also breaks accessibility tools like screen readers, which may mispronounce or skip corrupted text. Search engines may index garbled content, harming your SEO.
How to Fix It
- Declare UTF-8 encoding in your HTML with
<meta charset="utf-8">as the first element inside<head>. - Save your file as UTF-8 in your text editor. Most editors have an option like "Save with Encoding" or "File Encoding" in the status bar or save dialog. Choose "UTF-8" or "UTF-8 without BOM."
- Re-encode the file if it was originally saved in a different encoding. Tools like
iconvon the command line can convert between encodings:iconv -f WINDOWS-1252 -t UTF-8 input.html -o output.html
- Replace problematic characters by re-typing them or using HTML character references if needed.
- Check your server configuration. If your server sends a
Content-Typeheader with a charset that conflicts with the file's actual encoding (e.g.,Content-Type: text/html; charset=iso-8859-1for a UTF-8 file), the validator will use the HTTP header's encoding, causing mismatches.
Examples
Incorrect — Encoding mismatch
A file saved in Windows-1252 but declaring UTF-8. The byte 0xE9 represents é in Windows-1252 but is an invalid lone byte in UTF-8, triggering the malformed byte sequence error.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Page</title>
</head>
<body>
<!-- If the file is saved as Windows-1252, the é below is byte 0xE9, -->
<!-- which is not a valid UTF-8 sequence -->
<p>Resumé</p>
</body>
</html>
Correct — File properly saved as UTF-8
The same document, but the file is actually saved in UTF-8 encoding. The character é is stored as the two-byte sequence 0xC3 0xA9, which is valid UTF-8.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Page</title>
</head>
<body>
<p>Resumé</p>
</body>
</html>
Alternative — Using character references
If you can't resolve the encoding issue immediately, you can use HTML character references to avoid non-ASCII bytes entirely:
<p>Resumé</p>
Or using the named reference:
<p>Resumé</p>
Both render as "Resumé" regardless of file encoding, though this is a workaround — properly saving the file as UTF-8 is the preferred long-term solution.
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