HTML entity encode/decode · Guide
Convert HTML Character Entities to UTF-8
Converting entities to UTF-8 is decoding plus an encoding decision. Legacy content is full of `é` and `é` where a plain `é` belongs, and cleaning it up is worth doing once rather than decoding on every render.
The conversion
// PHP — name the charset explicitly
html_entity_decode($s, ENT_QUOTES | ENT_HTML5, 'UTF-8');
# Python
import html
html.unescape('café') # 'café'
// JavaScript — let the parser do it
new DOMParser().parseFromString(s, 'text/html').documentElement.textContent;All three handle named, decimal and hexadecimal references. Hand-written replacement chains cover four entities and miss two thousand.
Why the charset argument matters
The decoder produces characters and must then encode them as bytes. Ask PHP for ISO-8859-1 output and → (→) has no representation, so it is dropped or replaced.
Specify UTF-8 for both the input interpretation and the output, and make sure the destination — file, database column, HTTP header — is also UTF-8. A correct decode written into a Latin-1 column corrupts on write.
Cleaning up stored content
Entity-encoded text in a database is a liability: JSON APIs, CSV exports and emails all show the raw é. Decode once in a migration rather than at every render.
Watch for double-encoded values — é decodes to é and needs a second pass. Handle those deliberately as a one-off data fix with a manual review, never with a loop in production code.
Confirm the column and connection are UTF-8 first (utf8mb4 on MySQL, not utf8), or the migration will replace one corruption with another.
Frequently asked questions
Why do some characters disappear after decoding?
The output charset cannot represent them. Use UTF-8 throughout.
Should I store decoded or encoded text?
Decoded. Store raw UTF-8 and escape on output.
What about double-encoded values?
Fix them once in a reviewed migration. Never decode repeatedly at runtime.
Ready to try it?
Open the free browser-based HTML entity encode/decode and apply what you just read — no sign-up, runs locally.
Open the HTML entity encode/decode tool