In short
HTML character encoding tells the browser how to turn the bytes in your file into letters. Learn what a charset is, why UTF-8 is the right choice, and how to fix broken characters. You met <meta charset="utf-8"> in the boilerplate lesson.
HTML character encoding tells the browser how to turn the bytes in your file into letters. Learn what a charset is, why UTF-8 is the right choice, and how to fix broken characters.
You met <meta charset="utf-8"> in the boilerplate lesson. Now let's understand what it really does, what the older charsets were, and what to do when your page shows strange symbols like café instead of café.
What is a character set (charset)?
Computers only store numbers. To store text, they need a table that says "this number means this letter". That table is a character set, or charset. The way those numbers are written as bytes in a file is called the character encoding. In everyday web work people use both words for the same thing.
Think of it like a secret code between two friends. If you write a message with code A and your friend reads it with code B, the message turns into nonsense. The same happens when your editor saves a file in one encoding and the browser reads it in another.
ASCII, ANSI, ISO-8859-1 and UTF-8 compared
Charsets grew over time as computers spread around the world.
ASCII (1960s) has only 128 characters: the English letters A to Z and a to z, the digits 0 to 9, common punctuation, and some invisible control codes. There is no é, no ñ, no €, and nothing for Arabic, Chinese or Hindi.
ISO-8859-1 (also called Latin-1) extended ASCII to 256 characters. It added letters for Western European languages, like é, ü and ñ. It was the default charset of HTML 4. Other ISO-8859 versions covered other regions, such as ISO-8859-5 for Cyrillic, but each page could use only one of them.
ANSI is the name Windows uses for its own older charsets. On English and Western European Windows it usually means Windows-1252, which is almost the same as ISO-8859-1 but adds characters like € and curly quotes. Modern browsers actually treat a page labelled ISO-8859-1 as Windows-1252.
UTF-8 is part of Unicode, one giant list that gives a number to every character in every language, plus symbols and emoji. That is more than 150,000 characters. UTF-8 stores each character in 1 to 4 bytes. The first 128 characters are exactly the same as ASCII, so plain English text looks identical in both.
| Charset | Characters | Languages | Use today? |
|---|---|---|---|
| ASCII | 128 | English only | Only as part of UTF-8 |
| ISO-8859-1 (Latin-1) | 256 | Western European | No, legacy only |
| Windows-1252 ("ANSI") | 256 | Western European | No, legacy only |
| UTF-8 | All of Unicode | Every language, symbols, emoji | Yes, always |
UTF-8 is used by about 98% of all websites, and the HTML standard says new pages must use UTF-8.
How to declare UTF-8 with meta charset
Put this line as the very first element inside <head>:
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Café Olé: Menú del día</title>
</head>
<body>
<h1>Menú del día</h1>
<p>Paella valenciana: 14 €</p>
<p>Crème brûlée: 6 € 🍮</p>
</body>
</html>
When this file is saved as UTF-8, the browser shows every accent, the euro sign and the emoji correctly.
Why first? The browser reads the file from the top. It must know the charset before it reads any text, including the <title>. The rule is that the <meta charset> must appear in the first 1024 bytes of the file. Putting it first in the head is the simple way to be safe.
A few more details:
utf-8andUTF-8both work. Lowercase matches the rest of your HTML.- You may see the old long form
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">. It still works, but the short form is all you need. - The web server can also send the charset in an HTTP header:
Content-Type: text/html; charset=utf-8. If the header and the meta tag disagree, the header wins. More about that below.
Common mistake: Writing
<meta charset="utf-8">but saving the file in a different encoding. The meta tag is only a label. It does not change the file. The label and the real encoding must match.
Mojibake: why do I see strange characters on my page?
Mojibake (a Japanese word meaning roughly "character transformation") is the jumble of wrong symbols you get when text is read with the wrong charset. Here are the two most common cases:
| What you see | What happened | Fix |
|---|---|---|
café, €, don’t | The file is UTF-8, but the browser read it as Windows-1252 | Add <meta charset="utf-8"> or fix the server header |
caf� (a black diamond with a question mark) | The file is Windows-1252, but the browser read it as UTF-8 | Re-save the file as UTF-8 |
In the first case, é is stored as two bytes in UTF-8. The browser thinks each byte is its own Windows-1252 letter, so you see two characters: é. In the second case, the single Windows-1252 byte for é is not valid UTF-8, so the browser shows the replacement character �.
Follow these steps to fix mojibake:
- Check that
<meta charset="utf-8">is the first thing in<head>. - Check the real encoding of the file in your editor (see the next section).
- Check the server. In the browser, open DevTools (F12), go to the Network tab, click your page and look at the
Content-Typeresponse header. It should saycharset=utf-8. - If text comes from a database (for example MySQL in XAMPP), make sure the database and connection use
utf8mb4, the full UTF-8 that also supports emoji.
If you build pages with PHP, you can send the right header yourself at the very top of the file:
<?php
header('Content-Type: text/html; charset=utf-8');
?>
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>Réservation</title>
</head>
<body>
<p>Merci ! Votre réservation est confirmée.</p>
</body>
</html>
The header() line makes the server tell the browser "this is UTF-8" before any HTML arrives, so the header and the meta tag agree.
How to save a file as UTF-8 in VS Code
VS Code saves new files as UTF-8 by default, but files you copy from older projects or other programs may use another encoding.
- Open the file and look at the status bar at the bottom right. You will see the encoding, for example
UTF-8orWindows 1252. - If the text already looks broken in the editor, click the encoding name and choose Reopen with Encoding, then pick the encoding that makes the text look right (often
Windows 1252). - Now click the encoding name again, choose Save with Encoding, and pick UTF-8.
- Reload the page in the browser.
You may also see UTF-8 with BOM. The BOM (byte order mark) is an invisible marker at the start of the file. It is allowed, but it can cause problems in PHP files (it is sent before your header() calls). Choose plain UTF-8.
Tip: To make sure VS Code never uses anything else, open Settings, search for
files.encoding, and check that it is set toutf8.
Practice exercise
Create a file called greetings.html with <meta charset="utf-8"> and a list of "hello" in five languages, for example: Hola, Bonjour, Grüß Gott, Привет, こんにちは, and one emoji. Save it as UTF-8 and open it in the browser. Then use VS Code's Save with Encoding to save it as Windows 1252 and reload the page. Watch what happens, then save it as UTF-8 again to fix it.
Hint: When you save as Windows 1252, VS Code may warn that some characters can't be saved. That is exactly the point: Windows-1252 has no Russian or Japanese letters.
Key takeaways
- A charset is a table that maps numbers to characters; the encoding is how those numbers are stored as bytes.
- ASCII, ISO-8859-1 and Windows-1252 ("ANSI") are old and limited. UTF-8 covers every language and emoji.
- Put
<meta charset="utf-8">first in<head>, and make sure the file is really saved as UTF-8. cafémeans UTF-8 read as Windows-1252;caf�means Windows-1252 read as UTF-8.- Check the editor's status bar, the server's
Content-Typeheader and the database charset when fixing mojibake.