Skip to content

field note

Google changed JSON-LD escaping: what to check on your site

Google updates

Shahid AliAugust 23, 2026all posts

Google changed JSON-LD escaping: what to check on your site

Google changed how Googlebot reads JSON-LD, and the change is the kind that fails quietly. Nothing errors. Your structured data still validates. The values just come out wrong, and they stay wrong until somebody looks at the rendered output instead of the error count.

Search Central announced it on LinkedIn on 21 August 2026, in one paragraph, with no documentation change to go with it.

What did Google change about JSON-LD?

Here is the announcement in full, because the wording matters:

To bring our parser up to JSON and other standards, we changed our JSON-LD extraction and are now only applying a single pass of HTML unescaping. Practically speaking, this means that double-escaped entities (like & or ✔) will no longer be unrolled. If you’re using JSON-LD for structured data, be sure to update your code to standard JSON escapes or Unicode hexadecimal escapes (like \u0026).

Gary Illyes added a pointer to the standard: “If you’re wondering what proper escaping is in JSON, I have good news for you! It’s very, very well defined in RFC 8259, specifically section 7.”

Two things are worth pinning down before anyone panics. The verb is past tense, “we changed”, so this is live, not a heads up about a future rollout. And Google gave no date, no grace period, and no deprecation window. As of today the Search Central documentation updates page still has nothing about it; the most recent entry there is the preferred sources button from 20 August. The LinkedIn post is the whole primary source.

What does double-escaped mean here?

JSON has its own escaping rules and they have nothing to do with HTML. RFC 8259 section 7 is short and blunt about it: all Unicode characters may sit inside the quotation marks except the ones that must be escaped, which are the quotation mark, the reverse solidus, and the control characters U+0000 through U+001F. Any character may also be written as a six character sequence, a backslash, a lowercase u, and four hex digits. That is the entire escaping vocabulary. &amp; is not in it. &#10004; is not in it. Those are HTML, and inside a <script type="application/ld+json"> block they were never valid JSON, only tolerated.

The breakage comes from templates that HTML-escape their output on the way into the script tag. A CMS field already holds Tom &amp; Jerry because somebody escaped it once on input. The template escapes again on output, and what ships is Tom &amp;amp; Jerry. The old parser unescaped twice and got back Tom & Jerry. The new parser unescapes once, so the product name Google stores is the literal string Tom &amp; Jerry, ampersand, a, m, p, semicolon and all.

One pass instead of two What ships in the script tag "name": "Tom &amp;amp; Jerry"

Before: two passes Tom & Jerry the name you meant

Now: one pass Tom &amp; Jerry the name Google now stores

Source: Google Search Central, LinkedIn, 21 August 2026. Example values are illustrative of the double-escaping pattern Google described.

The same thing happens to numeric entities. A check mark written as &amp;#10004; used to arrive as a tick. Now it arrives as the seven characters &#10004;. If your product titles, review authors, or FAQ answers contain ampersands, quotes, apostrophes, or symbols, and something in your stack escapes on the way out, this is your problem.

Why won’t the validators catch this?

Because there is nothing to catch. "Tom &amp;amp; Jerry" is perfectly valid JSON. It parses. The Rich Results Test will report zero errors, because its job is finding syntax problems like a bad escape sequence, not judging whether a name looks like it went through an escaper twice. Same for the Schema Markup Validator. Same for the rich result reports in Search Console, which count errors and warnings rather than reading your product names back to you.

That is what makes this worth an hour of your time even if your Enhancements reports are green. Green means well formed. It does not mean correct.

How do I check my own site?

Read the values, not the error count. Three checks, in the order I would do them:

  1. Open the Rich Results Test on a page you know has an ampersand or a symbol in a title, and look at the detected item’s values in the results panel rather than the pass or fail badge. If you see &amp; sitting inside a name, you have found it.
  2. Do the same in the URL Inspection tool on the live version of a page, since that is Googlebot’s own rendering rather than a test fetch.
  3. Grep the shipped HTML. This is the fast one, and it scales past the handful of pages you would ever test by hand.

That last one is what I did here. This site emits JSON-LD from Astro templates on every page, so I wrote a check that walks the built output, pulls every application/ld+json block, parses it, and fails the build if any block is invalid JSON or contains an HTML entity anywhere inside it. It runs against dist after the build, alongside the copy check that already guards this site’s brand rules.

The result on shahidali.co, run today: 444 JSON-LD blocks across 120 HTML pages, zero entities, zero parse failures. Clean, which I expected, because Astro’s JSON.stringify into set:html never HTML-escapes the payload in the first place. That is the real lesson from the scan. Frameworks that serialise objects to JSON are safe by construction. The sites at risk are the ones building JSON-LD as a string in a template language that escapes by default: WordPress themes and plugins with hand written schema, older Shopify Liquid snippets, anything where somebody wrote {{ product.title | escape }} inside a script tag because escaping felt like the responsible thing to do.

I have a soft spot for that particular failure, because it is the same class of problem as a CMS silently stripping backslashes out of anything you paste into a code field. The escaping layer that protects your HTML corrupts your JSON, and nothing tells you. It also belongs in the same mental bucket as the reporting quirks I keep writing up: it is invisible in every Search Console indexing status, because it is not an indexing problem at all.

The check itself is twenty lines and has no dependencies. If you run a static site, make it part of the build. If you run WordPress, the equivalent is a one off crawl: fetch a sample of templates, one product, one article, one category, one page with a symbol in the title, and read the extracted values.

Does this change my rankings?

No, and I want to be careful here because I have not tested it and neither has anybody else who published in the last two days. What is confirmed is a parsing change. What follows from it is mechanical: if a value that feeds a rich result now contains visible entity junk, that rich result is either uglier or ineligible, depending on the type. A brand name reading Acme &amp; Sons in a product snippet is not a penalty, it is a bad snippet.

I am also not claiming a scale for this. One independent developer reported checking a sample of top domains and finding a small number affected, but that is a single unverified sample and I am not going to launder it into a statistic. The honest summary is: the change is real and live, the blast radius is unknown, and the check is cheap.

What I am doing about it: the JSON-LD check now runs in this site’s verify chain, and it goes into the audit pass on every client property I touch this month, alongside the crawl and index checks I already run. If you want the pattern for that kind of sweep, the bulk URL inspection tool writeup covers how I batch this sort of per-URL verification without burning the quota.

Two things to watch. Whether Google backfills this into the structured data documentation, which it should, because a LinkedIn post is not a spec. And whether the rich result reports start showing anything, which I doubt, since valid JSON is valid JSON.

One more thing worth knowing about where this class of problem lives: Googlebot does not parse JSON at all, which is why a broken block never shows up as a crawl error.

Sources