10/28/2026
PHP loadHTML Garbles UTF-8: Characters Turn Into é
Short answer: when the HTML you give DOMDocument::loadHTML has no encoding declared, PHP behaves as if it were Latin-1, so UTF-8 characters come out as two or three wrong ones, such as café. Prepend an XML encoding declaration, $doc->loadHTML('<?xml encoding="UTF-8">' . $html), or put a <meta charset> in the HTML before the text. Recent PHP versions also have Dom\HTMLDocument::createFromString, which we tested on 8.5.5.
Reproduce it
We ran this on PHP 8.5.5 with a string that has an accent, a euro sign, a dash and a diaeresis:
$html = "<p>café €5 \u{2014} naïve</p>";
$doc = new DOMDocument();
libxml_use_internal_errors(true);
$doc->loadHTML($html);
echo $doc->getElementsByTagName('p')->item(0)->textContent;
Result: café â¬5 â naïve. Each multi-byte character was read one byte at a time.
Four fixes, tested
| Approach | Result on PHP 8.5.5 |
|---|---|
loadHTML($html) with nothing |
café â¬5 â naïve (garbled) |
loadHTML('<?xml encoding="UTF-8">' . $html) |
café €5 — naïve |
loadHTML('<meta charset="utf-8">' . $html) |
café €5 — naïve |
loadHTML(mb_encode_numericentity($html, [0x80, 0x10FFFF, 0, 0x1FFFFF], 'UTF-8')) |
café €5 — naïve |
Dom\HTMLDocument::createFromString($html) |
café €5 — naïve |
The manual's page for the method is DOMDocument::loadHTML. Its own description does not discuss character encoding. Comments under it say that older versions needed the HTML 4 form of the meta tag, <meta http-equiv="Content-Type" content="text/html; charset=utf-8">. We tested the short HTML5 form, and it worked on 8.5.5, but check your own version if you support older ones.
Which to use
- The XML declaration is the smallest change when you control the string. It is the one most often copied.
<meta charset>works if you are already building or can add a head to the HTML.- Numeric entities make the string plain ASCII, so it does not depend on any encoding rule, at the cost of an extra function call and a string that is bigger.
Dom\HTMLDocumentreads HTML the way a browser does. We found the class on 8.5.5. Runclass_exists('Dom\\HTMLDocument')before relying on it, since older PHP does not have it.
Check yours
Is the text garbled?
Look for Ã, â or ¬ where an accent or symbol should be.
Does the HTML declare an encoding?
If not, give loadHTML a hint, as above.
Do you save it back out?
Check saveHTML() too. The encoding in has to match what you expect out.
Which PHP versions run it?
Test on the oldest one you support.
Where this bites
Anything that parses someone else's page: scrapers, link and image checkers, sitemap builders. The PHP image checker on this site reads only attributes, so the bug does not affect it, but a script that reads page text will.
FAQ
Why does loadHTML turn é into é?
With no encoding declared, it treats the bytes as a single-byte encoding such as Latin-1, so each byte of a UTF-8 character becomes its own character.
How do I make loadHTML read UTF-8?
Prepend an XML encoding declaration, add a meta charset before the text, or convert non-ASCII characters to numeric entities first.
Is there a newer way to parse HTML in PHP?
Recent PHP versions include Dom\HTMLDocument, which parses HTML the way a browser does. We saw it on PHP 8.5.5; check yours with class_exists.
Researched and drafted with AI, then reviewed and edited by the site's editors.