What is the JSON-LD single-pass unescape change? Schema New Update

The change, live from August 21, 2026, is that Googlebot’s JSON-LD extractor now performs exactly one round of HTML unescaping on string values inside your structured data. Values that were double-encoded, such as & resolving to &, no longer decode cleanly. Google is aligning its parser with RFC 8259, the JSON specification, which never required HTML entity decoding in the first place. The result is that broken schema keeps ranking normally but stops earning rich features.

The change is not a ranking penalty. It is a parser-strictness change. Google removed a courtesy it had been extending for years. If you also run measurement setup work on your site, the Google Tag and Tag Manager unification update from the day before hit the same week, which is why so many teams are chasing two different fires at once.

Why double-escaping happens (the mechanism)

JSON has its own escape system for four things only: double quotes, backslashes, control characters, and Unicode. HTML entities like & or " are an HTML concept that does not belong inside JSON strings. Double-escaping happens when one layer of your stack writes JSON while treating it as HTML, and a second layer runs an HTML escape on top of that output.

Three common places this pattern gets baked in:

  • A WordPress theme uses esc_html() around a schema block that a plugin already emitted as clean JSON.
  • A Shopify or Liquid template pastes product names into JSON with | escape instead of | json.
  • A React or Vue component builds JSON-LD by string concatenation with dangerouslySetInnerHTML and passes it through an HTML sanitizer.

Any of these will produce & where you wanted &. Before August 21, Google would decode both layers and land on &. Now it decodes one and leaves & sitting inside your value.

Concrete examples of what breaks

The break shows up as a literal HTML entity string inside the structured data value, which fails Google’s schema validation and disqualifies the page from the rich feature that field was feeding. This matters most on pages where a rich result is the reason your title tag and meta description get any click-through help at all.

Product name with an ampersand

"name": "Barnes & Noble Signature Journal"

Before: Barnes & Noble Signature Journal. Product snippet rendered. After: Barnes & Noble Signature Journal. Rich Results Test throws a validation warning, review star eligibility drops.

Recipe ingredient with a fraction

"recipeIngredient": "1½ cups all-purpose flour"

Before: 1½ cups all-purpose flour. Recipe card eligible. After: 1½ cups all-purpose flour. Recipe rich result loses the ingredient field.

Review with a checkmark symbol

"reviewBody": "Fast shipping ✔ would buy again"

Before: Fast shipping ✓ would buy again. Review snippet displayed. After: the literal ✔ string sits inside the review body. Broken values also feed AI surfaces directly, so how AI Overviews pick their citations is another place the mess shows up.

FAQ answer with quoted plan name

"text": "The plan is called "Pro" and costs $19"

Before: The plan is called "Pro" and costs $19. FAQ accordion eligible. After: The plan is called "Pro" and costs $19. FAQ snippet fails validation.

The three correct ways to write these values

JSON-LD is JSON. The right approach is to stop treating it as HTML and use one of three spec-compliant patterns for any character that gave you trouble.

  1. Plain literal characters. JSON strings can hold &'½, and  as themselves. Nothing escapes them. Example: "name": "Barnes & Noble".
  2. JSON string escapes where JSON actually needs them: \" for a quote inside the string, \\ for a backslash. Example: "text": "The plan is called \"Pro\"".
  3. Unicode hex escapes for anything you want on the wire as ASCII. Example: "name": "Barnes & Noble" or "recipeIngredient": "1½ cups sugar".

Worked fixes by platform

The fix lives in the layer that writes the JSON, not in Google’s parser. Below are worked examples for the three platforms where this issue is currently most common.

WordPress with Rank Math or Yoast plus a theme escape filter

Bad pattern in a theme file:

<script type="application/ld+json">
{
  "@type": "Product",
  "name": "<?= esc_html($product->name) ?>"
}
</script>

esc_html is for HTML contexts, not JSON. It escapes & to &amp;. If the plugin already produced JSON, the escape runs a second time and you end up with &amp;amp;.

Correct pattern:

<script type="application/ld+json">
<?= wp_json_encode([
  '@type' => 'Product',
  'name'  => $product->name,
]) ?>
</script>

Let PHP encode the entire object. wp_json_encode handles every escape correctly and never runs the HTML pass. If you are still auditing which plugins own which schema blocks on your site, work through the Search Console setup and use URL Inspection to see the rendered HTML Google actually reads.

Shopify Liquid product template

Bad pattern:

{
  "name": "{{ product.title | escape }}"
}

| escape is the HTML filter. It converts & to &amp; inside a JSON string, which is exactly the wrong direction.

Correct pattern:

{
  "name": {{ product.title | json }}
}

Note the removed quotes around the value. The | json filter serializes the value as a complete JSON string including its surrounding quotes and correct escapes.

React or Next.js component

Bad pattern:

<script
  type="application/ld+json"
  dangerouslySetInnerHTML={{
    __html: `{"name": "${product.name}"}`
  }}
/>

String interpolation into JSON allows any special character in product.name to break the block. Sanitizers layered on top add the double-encoding.

Correct pattern:

<script
  type="application/ld+json"
  dangerouslySetInnerHTML={{
    __html: JSON.stringify({
      "@context": "https://schema.org",
      "@type": "Product",
      "name": product.name
    }).replace(/</g, "\\u003c")
  }}
/>

Serialize with JSON.stringify. The .replace only escapes < as the safe Unicode form, which prevents any string value that contains </script> from closing the block early. That is the one HTML-adjacent concern JSON-LD has, and it has a JSON-native fix.

The ten-minute audit

Any site can check itself for this class of break in under ten minutes using three tools: Google’s Rich Results Test, view-source, and a short console script that greps for residual HTML entities inside parsed JSON-LD values.

  1. Run the Rich Results Test on your home, one category, one product, and one article URL. Broken entities either throw warnings or show up in the preview as literal &amp; or &#XXX; strings.
  2. View-source on the same pages. Search for application/ld+json. Inside each block, look for &amp;&quot;&#. Any hit is a suspect.
  3. Run this in the browser console on any suspect page:
[...document.querySelectorAll('script[type="application/ld+json"]')].forEach((s,i)=>{
  try {
    const data = JSON.parse(s.textContent);
    const walk = (v) => {
      if (typeof v === 'string' && /&(amp|quot|apos|lt|gt|nbsp|#\d+|#x[0-9a-f]+);/i.test(v)) {
        console.warn('Block', i, 'has entity in value:', v);
      } else if (v && typeof v === 'object') Object.values(v).forEach(walk);
    };
    walk(data);
  } catch(e) { console.error('Block', i, 'is not valid JSON:', e.message); }
});

If it logs nothing, the page is clean. If it logs anything, that is the field to fix. For a broader view of how structured data ties into entity SEO and the knowledge graph, treat this audit as part of a wider entity signal check, not a one-off.

Prioritization by page type

Fix the pages where the rich feature is worth the most first. Product and Recipe pages are the highest-value targets. Article and Breadcrumb schema come next. Organization schema on the home page is the lowest urgency because it rarely feeds a visible SERP feature.

Schema typeWhat the rich feature isImpact if brokenPriority
Product with Offer and AggregateRatingPrice, stock, star ratings in SERPHighCritical
RecipeRecipe card, ingredients, cook time, ratingsHighCritical
Review or AggregateRating on servicesStar snippetHighHigh
FAQ (where still eligible for your site type)FAQ accordion in SERPMediumHigh
Article, NewsArticleTop Stories, Discover, publisher cardMediumHigh
BreadcrumbListBreadcrumb display in SERPLowMedium
Organization on home pageKnowledge panel signalsLowMedium

After the fix

Publishing the fix is not the end of the job. Google caches the old broken version, and depending on your CDN and WordPress cache stack, the fixed schema may take days to propagate unless you push it through explicitly.

The steps that finish the job:

  1. Purge your page cache: LiteSpeed, WP Rocket, Nginx FastCGI, or Varnish.
  2. Purge your CDN cache: Cloudflare, Fastly, or your host’s CDN. The wp-json layer usually bypasses CDN, the front-end HTML does not.
  3. Open Google Search Console, use URL Inspection on one fixed page, and click “Request Indexing”. Google will crawl within hours in most cases.
  4. Re-run the Rich Results Test on the live URL, not on pasted code. The paste tool cannot detect a caching problem downstream of your CMS.
  5. Watch the Rich Results reports in GSC for two to four weeks. Valid counts should climb, error counts should drop.

What this is not

This change is not a ranking penalty, not retroactive punishment, and not limited to any one CMS. It is Google enforcing the JSON specification it always pointed to. Pages with broken schema still rank at the same position they did before. What they lose is the visual real estate that rich results provide.

The value of a fix is not in ranking gain. It is in click-through rate. A product listing with star ratings and price outperforms a plain blue link by 20 to 40 percent in most verticals. That is the traffic you were leaving on the table before, and the traffic you are now leaving on the table if the fix is delayed.

Common questions

Will my rankings drop because of this?

No. Rankings are unaffected. Only rich result eligibility is affected.

Do I need to touch every page?

No. The problem is in the template layer, not per-page. Fix the template that emits the JSON and every page it produces is fixed at once.

Is validator.schema.org still enough?

It is enough for JSON validity. Use Google’s Rich Results Test in addition, because it applies Google-specific checks that the general validator does not.

What if I use a schema plugin, not my own template?

Reproduce the issue on a live page, then check whether a theme filter or security plugin is re-escaping the plugin’s output after it is generated. Disable them one at a time on a staging URL to isolate the layer.

Does this affect JSON-LD embedded in RSS or in emails?

Only in whatever context Google parses. RSS is separate. Emails are separate. This is a Googlebot JSON-LD change, focused on Search and the systems that draw from Search.

Sources

Author

  • Portrait of Kavinder Singh, digital marketing and SEO practitioner

    Kavi (Kavinder Singh) is an SEO specialist and digital marketing consultant with hands-on experience in technical SEO, local SEO, content strategy, Google Analytics, Google Ads, Meta Ads, and AI-driven search. He also writes travel guides drawn from first-hand experience across Uttarakhand and the wider Indian Himalaya, including his home region around Munsiyari. Through DigiABC Compass he shares practical, tested strategies and honest travel notes to help readers improve their online visibility and plan better trips.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *