Character Encoding
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
<meta charset> tag—and put it as early as possible inside <head>, ideally as the very first element.charset-declaration.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><meta charset> as the first thing inside <head>.Mojibake: Symptoms of an Encoding Mismatch
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 |
<meta charset> declares.Encoding Consistency End to End
Content-Type header, and the HTML's <meta charset> tag should all agree on UTF-8.response-header.http
Content-Type: text/html; charset=UTF-8
A Note on the Byte-Order Mark (BOM)
<!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).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
Check the actual file encoding your editor saved (most editors show this in the status bar or a "Save with Encoding" menu).
Check the <meta charset> tag is present and is the first element inside <head>.
Check the server’s Content-Type response header — inspect it via browser devtools’ Network tab.
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.
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
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.