HTMLCharacter Encoding (UTF-8)

Character Encoding

Every text file, including HTML, is stored on disk as raw bytes. A character encoding is the agreed-upon rule for translating those bytes back into readable characters. Get the encoding wrong—or leave it unspecified—and the browser has to guess, which is exactly how you end up with garbled, unreadable text.
UTF-8: the Universal Modern Standard

UTF-8 encodes the entire Unicode character set—every language, emoji, and symbol in common use—as a variable-length sequence of bytes, while staying byte-compatible with plain ASCII for English text. It is, by a wide margin, the dominant encoding on the web today, and there's essentially no reason to use anything else for a new project.

Encoding

Status today

UTF-8

The modern default—use this for everything

ISO-8859-1 (Latin-1)

Legacy Western-European encoding, largely obsolete

Windows-1252

Legacy Windows default, still occasionally seen in old exported files

UTF-16

Common inside programs (e.g. JavaScript strings) but rare as a file encoding on the web

Declaring the Charset
Tell the browser which encoding to use with a <meta charset> tag—and put it as early as possible inside <head>, ideally as the very first element.

charset-declaration.html

HTML
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>My Page</title>
    <!-- everything else in <head> comes after -->
  </head>
  <body>
    <p>Café, naïve, 你好, emoji: 🎉</p>
  </body>
</html>
The charset tag must appear within the first 1024 bytes
Browsers scan only the beginning of the document looking for a charset declaration before they start parsing text—if it comes too late (after a long inline script or a big comment block), the browser may have already guessed a different encoding and started rendering incorrectly. Keep <meta charset> as the first thing inside <head>.
Mojibake: Symptoms of an Encoding Mismatch
Mojibake is the classic garbled text that appears when a file is saved in one encoding but interpreted as another. It's a strong diagnostic signal you can learn to recognize instantly.

You typed / saved

You see instead

Likely cause

café

café

UTF-8 bytes read as Latin-1/Windows-1252

“smart quotes”

“smart quotesâ€

UTF-8 punctuation read as Latin-1

â€"

UTF-8 em-dash read as Latin-1

你好

garbage symbols or boxes

Encoding without proper Unicode/CJK support

If your page shows a burst of odd accented-looking characters where a simple apostrophe, quote, or accented letter should be, mismatched encoding is almost always the cause—check both how the file was saved and what <meta charset> declares.
Encoding Consistency End to End
Mojibake happens when any link in the chain disagrees with the others: the file's actual bytes on disk, the HTTP response's Content-Type header, and the HTML's <meta charset> tag should all agree on UTF-8.

response-header.http

Text
Content-Type: text/html; charset=UTF-8
Save every source file as UTF-8 without exception
Set your editor's default encoding to UTF-8 for all new files. Most modern editors default to this already, but it's worth verifying explicitly, especially in older projects or files exported from legacy tools (spreadsheets, old CMS exports).
A Note on the Byte-Order Mark (BOM)
Some tools save UTF-8 files with a Byte-Order Mark: a few invisible bytes at the very start of the file that some systems use to detect encoding. For HTML, the BOM is unnecessary and can occasionally cause problems—for example, a stray BOM before <!DOCTYPE html> can push the page into quirks mode, or appear as a visible artifact if bytes end up embedded mid- page (such as after a server-side include).
Prefer “UTF-8 without BOM” when your editor offers the choice
Most modern web tooling handles a BOM gracefully, but saving as plain UTF-8 (no BOM) avoids the edge cases entirely and is the convention followed by virtually all web projects.
A Quick Technical Picture

UTF-8 represents each character using one to four bytes: plain ASCII characters (English letters, digits, basic punctuation) take a single byte and are identical to old ASCII encodings, while accented letters, symbols, CJK characters, and emoji take two to four bytes. This is why English-heavy UTF-8 files look nearly identical to their ASCII equivalents, while an encoding mismatch mostly shows up on non-ASCII characters.

Character

UTF-8 byte length

A (basic Latin letter)

1 byte

é (accented Latin letter)

2 bytes

€ (Euro sign)

3 bytes

你 (CJK character)

3 bytes

🎉 (emoji)

4 bytes

Debugging an Encoding Problem
  1. Check the actual file encoding your editor saved (most editors show this in the status bar or a "Save with Encoding" menu).

  2. Check the <meta charset> tag is present and is the first element inside <head>.

  3. Check the server’s Content-Type response header — inspect it via browser devtools’ Network tab.

  4. If all three say UTF-8 and you still see mojibake, check whether the data passed through a database or API that uses a different encoding somewhere in the pipeline.

A quick sanity test string
Include a short line like café — 你好 — 🎉 somewhere during development. If it renders correctly, encoding end-to-end is almost certainly fine; if any part turns to garbled symbols, you've found exactly where to start debugging.
HTML5's Fallback Behavior
If no charset is declared at all, browsers fall back to a best-guess heuristic (historically based on the user's locale or content sniffing), which is unreliable and varies between browsers. Never depend on this fallback—always declare UTF-8 explicitly.
  • Character encoding is the rule for converting stored bytes back into readable text.

  • UTF-8 is the modern universal standard—use it for every HTML file, with no real exceions today.

  • Declare <meta charset="UTF-8" /> as the very first element inside <head>.

  • Mojibake (garbled accented-looking characters) is the telltale symptom of an encoding mismatch.

  • Keep the file’s actual encoding, the HTTP Content-Type header, and <meta charset> all consistent.

  • Save files as UTF-8 without a BOM to avoid quirks-mode and stray-artifact edge cases.