ISO-8859-1 ISO-8859-1 (Latin-1) ISO-8859 Flagship
The classic single-byte Western European encoding — mostly superseded by UTF-8, but still a common source of mojibake when the two get confused.
What it is
ISO-8859-1, commonly called Latin-1, is a fixed 1-byte-per-character encoding covering Western European languages — English plus accented characters used in French, German, Spanish, Portuguese, Italian, Dutch, and a handful of others. It reuses ASCII for bytes 0x00–0x7F and adds Western European letters, punctuation, and symbols in the 0xA0–0xFF range, leaving 0x80–0x9F as unassigned control codes.
It was the default encoding for HTTP and HTML for years (before UTF-8 became the near-universal standard), which is why it still shows up constantly in legacy systems, old databases, and — critically — as one half of the most common mojibake bug on the web: UTF-8 bytes misread as Latin-1.
Byte structure
| Byte range | Content |
|---|---|
| 0x00–0x7F | Identical to ASCII |
| 0x80–0x9F | Unassigned (reserved for C1 control codes, rarely used) |
| 0xA0–0xFF | Western European letters, accented characters, punctuation, symbols (é, ñ, ü, ©, ×, ÷, etc.) |
Because it's fixed-width, every character is exactly 1 byte — there's no multi-byte sequencing to worry about, which is part of why it was so easy to adopt in early web/database systems, and part of why it can't represent anything outside its ~191 assigned characters.
Why your text is garbled (symptom → cause)
| Symptom | Likely cause |
|---|---|
é shows as é, ñ shows as ñ |
UTF-8 bytes were read as Latin-1 — each UTF-8 2-byte character gets split into two separate Latin-1 characters |
Smart quotes/em-dashes show as ’, â€" |
Same double-decode pattern, but happening on Windows-1252 punctuation (curly quotes, em dash) that doesn't exist in true Latin-1 at all — a strong sign the actual source was Windows-1252, not Latin-1 |
| Text with € (euro sign) doesn't display or shows wrong character | True ISO-8859-1 has no euro sign (it predates the euro) — this needs ISO-8859-15 or Windows-1252, both of which added it |
| Correct in old exports, garbled after modern import | Old system exported Latin-1 (once the web default), new system assumes UTF-8 on import without converting first |
Compatibility & gotchas
- Windows-1252 is not the same thing, despite being extremely similar — they're often confused/mislabeled as interchangeable. The difference is entirely in the 0x80–0x9F range: Latin-1 leaves it as unused control codes, Windows-1252 fills it with printable characters (curly quotes, em/en dash, €, ™, and more). Mislabeling one as the other corrupts exactly those characters. Full comparison on the Windows-1252 page.
- No euro sign. ISO-8859-1 predates the euro currency (1999) — if you need € support in a single-byte Western European encoding, you want ISO-8859-15 (which swapped a few rarely-used symbols for €) or Windows-1252.
Latin-1is sometimes used loosely to mean Windows-1252 in casual conversation and even in some software documentation — always verify which one a system actually implements before assuming compatibility.- HTML/HTTP historically defaulted to Latin-1 in the absence of an explicit charset declaration — modern browsers default to UTF-8 instead, so old undeclared-charset pages can render differently today than they did 15+ years ago.
- Every byte value is technically "valid" in Latin-1 (nothing is disallowed), which means Latin-1 decoders never throw errors on malformed input — they'll happily produce garbage characters for genuinely different encodings rather than failing loudly, making Latin-1-related mojibake bugs easy to miss until someone actually reads the output.
Fixing it
- Command-line conversion:
iconv -f ISO-8859-1 -t UTF-8 input.txt -o output.txt— the standard fix once you've confirmed the source really is Latin-1 and not Windows-1252 (test both if unsure; the difference will only show up in text containing curly quotes, em dashes, or €). - Python:
content.encode('latin-1').decode('utf-8')is the classic one-liner to reverse a UTF-8-read-as-Latin-1 mistake, re-encoding back to the original bytes and decoding them correctly as UTF-8. - Databases: check both the column charset (
SHOW CREATE TABLE) and the connection charset — a Latin-1 column read over a UTF-8 connection (or vice versa) will corrupt data even if each side is individually "correct" for its own layer. - Always declare charset explicitly in new code —
<meta charset="utf-8">,Content-Type: text/html; charset=utf-8, explicitencoding=parameters in file I/O — to avoid ever needing this kind of forensic guesswork going forward.
Comparison
| Encoding | Key difference from ISO-8859-1 |
|---|---|
| Windows-1252 | Nearly identical, but fills the 0x80–0x9F range with printable characters (curly quotes, dashes, €) instead of leaving it as unused control codes |
| ISO-8859-15 | Same layout as 8859-1 except a handful of rarely-used symbols are swapped for € and a few French/Finnish letters |
| UTF-8 | Variable-width, covers all of Unicode; ASCII range is identical, but multi-byte UTF-8 sequences look like garbage if misread as Latin-1 |
FAQ
Is Latin-1 the same as Windows-1252?
No, though they're close enough to cause real bugs when confused. They're identical for bytes 0x00–0x7F and 0xA0–0xFF, but differ in the 0x80–0x9F range — Windows-1252 uses that range for printable characters like curly quotes and the euro sign, while true Latin-1 leaves it unassigned.
Why does my text show extra symbols like é instead of é?
This is the standard signature of UTF-8 bytes being decoded as Latin-1 (or Windows-1252). Each accented UTF-8 character is 2 bytes, and Latin-1 decodes each of those bytes as its own separate character, producing two garbled characters where one accented character should be.
Should I use Latin-1 for a new project?
No — use UTF-8. Latin-1 only covers Western European characters and can't represent most of the world's writing systems or emoji. It persists mainly in legacy systems and specific protocol contexts (some parts of HTTP header encoding still reference it historically), not as a recommended choice for new work.
Fun fact
Despite ISO-8859-1 being nominally "obsolete" in favor of UTF-8, the HTTP/1.1 specification (RFC 7230) still references Latin-1 as the default fallback encoding for certain header field interpretations — a small but persistent artifact of it once being the web's default encoding, baked into the protocol spec long after browsers stopped using it as their actual page-rendering default.