Contents

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\HTMLDocument reads HTML the way a browser does. We found the class on 8.5.5. Run class_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.