Google przestał naprawiać Twoje dane strukturalne. Ekstrakcja JSON-LD stosuje teraz jeden przebieg odescapowania HTML, więc blok, który wcześniej był po cichu prostowany, dziś po prostu nie parsuje się i znika. Nie ma komunikatu o błędzie w Search Console, nie ma ostrzeżenia, jest tylko rich result, który przestaje się pojawiać. Ten tekst pokazuje, jak w kilka minut zmierzyć własny korpus zamiast zgadywać, gdzie w WordPressie ten zapis powstaje najczęściej i dlaczego jednorazowy audyt nie wystarczy. Nasz własny pomiar na 68 055 blokach jest w środku, razem ze skryptem.
Co dokładnie zmienił Google
Komunikat jest krótki i wart zacytowania w całości, bo cała reszta to konsekwencje jednego zdania:
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
(Glosa: żeby dociągnąć parser do standardu JSON i innych standardów, Google zmienił ekstrakcję JSON-LD i stosuje teraz tylko jeden przebieg odescapowania HTML.)
Gary Illyes dorzucił wskazówkę, gdzie szukać definicji poprawności: RFC 8259, czyli specyfikacja formatu JSON. To nie jest wytyczna SEO, tylko odesłanie do standardu, który obowiązuje każdy parser JSON na świecie.
Praktyczny skutek opisano równie zwięźle: podwójnie zaescapowane encje, takie jak & czy ✔, nie będą już rozwijane. Wcześniej parser Google robił dodatkowy przebieg i taki zapis prostował. Teraz robi jeden przebieg i zostaje mu tekst, który nie jest poprawnym JSON-em.
Warto od razu nazwać to, czego w tej informacji nie ma. Nie ma daty wdrożenia. Nie ma linku do zaktualizowanej dokumentacji Google. Źródłem jest wpis na LinkedIn, relacjonowany przez Search Engine Roundtable 21 sierpnia 2026. Traktuj to więc jako stan zgłoszony przez Google, a nie jako zapis w dokumentacji, na który można się powołać przed klientem.
Czym jest podwójne escapowanie i skąd się bierze
Weź prosty przypadek: nazwa firmy „Kowalski & Syn” w polu name w JSON-LD.
| zapis | co widzi parser JSON | status |
|---|---|---|
"Kowalski & Syn" | Kowalski & Syn | poprawny, ampersand nie wymaga ucieczki w JSON |
"Kowalski & Syn" | Kowalski & Syn | poprawny, ucieczka uniwersalna |
"Kowalski & Syn" | Kowalski & Syn | parsuje się, ale wartość jest brzydka |
"Kowalski & Syn" | Kowalski & Syn | to jest podwójne escapowanie |
Sam ampersand nie wysadzi bloku, bo JSON go nie wymaga uciekać. Prawdziwy problem zaczyna się przy cudzysłowie. Jeśli szablon zapisze " tam, gdzie powinien być \", po jednym przebiegu odescapowania zostaje ", a nie znak cudzysłowu. Blok nie domyka stringu i przestaje być JSON-em.
Skąd to się bierze w WordPressie? Prawie zawsze z podwójnego przetwarzania tej samej wartości. Treść przechodzi przez esc_html() przy zapisie, potem przez filtr motywu przy wyświetlaniu, a na końcu ląduje w polu JSON-LD, które i tak zrobiłoby własną ucieczkę. Każdy z tych kroków z osobna jest poprawny. Złożone razem produkują zapis, który przez lata działał tylko dlatego, że Google go za nas prostował.
Jak sprawdzić własny korpus w pięć minut
Najważniejsza zasada brzmi: sprawdzasz zbudowany HTML, nie źródło szablonu. W źródle wszystko wygląda dobrze, bo escapowanie dokłada się dopiero na wyjściu. Jeśli masz statyczny build, skanujesz katalog wynikowy. Jeśli masz klasycznego WordPressa, pobierasz próbkę adresów przez wget albo curl i skanujesz to, co odpowiedział serwer.
Sama kontrola to kilkanaście linii. Wyciągnij każdy blok <script type="application/ld+json">, spróbuj go sparsować i osobno poszukaj wzorca podwójnej encji:
const RE = /<script[^>]*type=["']application\/ld\+json["'][^>]*>([\s\S]*?)<\/script>/gi;
let m;
while ((m = RE.exec(html))) {
const body = m[1];
if (/&(quot|amp|lt|gt|#\d+);/.test(body)) report("podwójna encja", file);
try { JSON.parse(body); } catch (e) { report("nie parsuje się: " + e.message, file); }
}
Dwa osobne testy, bo łapią różne rzeczy. JSON.parse wywróci się na uszkodzonym stringu, ale przepuści &amp; w wartości, która nadal jest poprawnym JSON-em, tylko zawiera śmieć. Test na wzorzec encji łapie właśnie ten drugi przypadek: blok się parsuje, a w rich result widać & zamiast znaku.
Trzeci test, który warto dopisać, to pojedyncze encje HTML w wartościach. One nie są błędem, ale po zmianie zostaną rozwinięte dokładnie raz, więc wynik może różnić się od tego, co widziałeś wcześniej. Lepiej wiedzieć, że je masz.
Nasz pomiar: 68 055 bloków, zero błędów
Puściliśmy ten skan na własnym korpusie 30 sierpnia 2026, na świeżo zbudowanej wersji produkcyjnej.
| metryka | wynik |
|---|---|
| strony z co najmniej jednym blokiem JSON-LD | 15 742 |
| bloków JSON-LD razem | 68 055 |
| bloków, które nie parsują się jako JSON | 0 |
| stron z podwójnie zaescapowaną encją | 0 |
| stron z pojedynczą encją HTML w JSON-LD | 0 |
Zero we wszystkich trzech kategoriach. Nie piszemy tego jako chwały, tylko jako informacji o tym, co ten wynik znaczy i czego nie znaczy. Nasz stos generuje dane strukturalne z frontmattera przez komponenty Astro i wstawia je wzorcem set:html={JSON.stringify(...)}. JSON.stringify z definicji produkuje poprawny JSON, a set:html nie dokłada własnej ucieczki HTML. Innymi słowy: nie mieliśmy zera dlatego, że byliśmy ostrożni, tylko dlatego, że ten konkretny wzorzec nie ma jak wyprodukować podwójnego escapowania.
To jest ważniejsze niż sama liczba. Jeśli Twój stos składa JSON-LD przez konkatenację stringów w szablonie albo przez wtyczkę, która wkleja pole treści do gotowego kawałka JSON-a, ryzyko jest realne i wynik będzie inny. Zmierz swój, nie przepisuj naszego.
Gdzie to pęka najczęściej w WordPressie
Z audytów, które robimy u klientów, wynikają trzy powtarzalne źródła.
Pierwsze to wtyczka SEO wypełniająca description z pola, które przeszło już przez wp_kses albo esc_attr. Najczęściej wysypuje się na apostrofie i cudzysłowie w polskich tekstach, bo tam takich znaków jest po prostu więcej niż w angielskich.
Drugie to ręczny blok JSON-LD wklejony do header.php albo do ustawień motywu, gdzie wartości wstawia się przez echo bez wp_json_encode. To najczęstszy wariant w motywach robionych na zamówienie kilka lat temu i najtrudniejszy do znalezienia, bo nie widać go w żadnym panelu wtyczki.
Trzecie to page builder, który zapisuje treść z encjami HTML już w bazie. Wtedy nawet poprawnie napisany generator JSON-LD dostaje na wejściu tekst z & i uczciwie go zakoduje jeszcze raz.
Wspólny mianownik jest zawsze ten sam: wartość jest escapowana dwa razy, raz do HTML i raz do JSON, przez dwie różne warstwy, które o sobie nie wiedzą.
Jak zapisać to poprawnie
Reguła jest jednoznaczna, bo mamy do niej standard. W wartości JSON uciekaj po jsonowemu, nie po htmlowemu.
W PHP oznacza to wp_json_encode() na całej strukturze, nigdy ręczne sklejanie stringów. W JavaScripcie JSON.stringify(). W szablonie Astro wzorzec, którego używamy u siebie:
<script type="application/ld+json" set:html={JSON.stringify(schema)} />
Jeśli naprawdę potrzebujesz znaku, który mógłby przedwcześnie zamknąć blok skryptu, użyj ucieczki uniwersalnej. & dla ampersanda i < dla znaku mniejszości są bezpieczne w każdym parserze JSON i nie wymagają żadnej wiedzy o HTML-u po stronie odbiorcy.
Czego nie robić: nie wstawiaj encji HTML do wartości JSON w nadziei, że ktoś je rozwinie. Przez lata rozwijał je Google. Od teraz rozwinie je dokładnie raz, a każdy inny konsument Twoich danych strukturalnych, od Binga po asystenta AI czytającego stronę, nie miał obowiązku robić tego nigdy.
Jednorazowy audyt to za mało, zrób z tego bramkę
To jest ta część, którą najczęściej się pomija. Dane strukturalne nie są tekstem, który raz się napisze. Generuje je szablon, wtyczka albo integracja, i każda ich aktualizacja może wprowadzić escapowanie z powrotem. Audyt zrobiony dziś mówi tylko o dzisiejszym buildzie.
U siebie zamieniliśmy ten skan w bramkę uruchamianą po buildzie. Czyta katalog wynikowy, liczy bloki, próbuje sparsować każdy z nich i kończy się błędem tylko przy realnym problemie, czyli przy bloku, który się nie parsuje, albo przy podwójnej encji. Przy braku katalogu wynikowego kończy się zerem, żeby nie produkować fałszywego czerwonego w środowisku, w którym nikt jeszcze nie budował.
Trzy szczegóły, które decydują o tym, czy taka bramka jest coś warta. Po pierwsze musi czytać artefakt, nie źródło, bo źródło nie dowodzi niczego o escapowaniu. Po drugie musi liczyć bloki i pokazywać tę liczbę, żeby dało się zauważyć, że nagle spadła z 68 tysięcy do dwustu, bo integracja przestała je generować. Po trzecie musi rozróżniać błąd od informacji: pojedyncze encje HTML raportujemy, ale nie wywracamy na nich builda, bo to nie jest awaria, tylko coś, o czym trzeba wiedzieć.
Co zrobić, gdy skan coś znajdzie
Skan daje listę plików, nie diagnozę. Zanim cokolwiek poprawisz, ustal, która warstwa produkuje ten zapis, bo naprawa w złym miejscu wróci przy najbliższej aktualizacji.
Weź jeden adres z listy i wyświetl surową odpowiedź serwera, na przykład curl -s ADRES | grep -A5 "application/ld+json". Popatrz, czy uszkodzona wartość pochodzi z tytułu wpisu, z opisu SEO, czy z pola własnego. To wskazuje warstwę szybciej niż czytanie kodu.
Dalej rozstrzygasz według źródła:
- Jeśli wartość pochodzi z wtyczki SEO, sprawdź, czy nie masz dwóch wtyczek generujących ten sam typ schema. Podwójny generator to częstsza przyczyna dziwnych wartości niż błąd w którymkolwiek z nich z osobna.
- Jeśli blok jest wklejony w motywie, przepisz go na
wp_json_encode()na całej tablicy. Ręczne sklejanie stringów jest tu jedynym prawdziwym błędem i nie da się go załatać częściowo. - Jeśli encje siedzą już w bazie, bo wstawił je page builder, nie naprawiaj ich w generatorze. Odkoduj wartość raz przed przekazaniem do enkodera JSON, na przykład przez
html_entity_decode()z flagą dla cudzysłowów i jawnym UTF-8.
Klasyczny błąd wygląda tak:
echo '{"name":"' . esc_html( $title ) . '"}';
Poprawnie tak:
echo wp_json_encode( array( 'name' => $title ) );
Różnica nie polega na liczbie znaków, tylko na tym, kto odpowiada za ucieczkę. W pierwszym wariancie robi to funkcja HTML-owa w kontekście, który nie jest HTML-em. W drugim robi to enkoder JSON w kontekście JSON-a.
Po poprawce powtórz skan na nowym buildzie, a nie na starym artefakcie. To brzmi banalnie, ale połowa zgłoszeń „naprawiłem, a dalej jest błąd” to skan po starym katalogu wynikowym.
Co realnie tracisz, jeśli to zignorujesz
Utrata schema nie boli od razu i to jest w tym najgorsze. Nie ma spadku pozycji z dnia na dzień, jest stopniowe znikanie rzeczy, które wyróżniały wynik: gwiazdek z ocen, danych o produkcie, listy FAQ, okruszków nawigacyjnych. Efekt zobaczysz w CTR, nie w pozycji, a CTR spada powoli i łatwo przypisać go czemu innemu.
Drugi odbiorca tych danych jest nowszy i mniej wybaczający. Silniki odpowiedzi czytają dane strukturalne, bo to najtańszy sposób, żeby ustalić, czym jest strona, bez interpretowania całej treści. U nas ruch agentów jest już mierzalny i nie jest marginalny: rzędu stu wizyt na dobę, z czego dwie trzecie przez nasz własny endpoint MCP. Te systemy nie mają żadnego powodu, żeby prostować cudze błędy escapowania. Google prostował je latami z uprzejmości wobec zastanego internetu. Nowy konsument Twoich danych nigdy takiego zwyczaju nie miał i nie nabierze.
Trzecia warstwa to Search Console, i tu trzeba uczciwie powiedzieć, czego się nie dowiesz. Raporty wyników z elementami rozszerzonymi pokażą spadek liczby prawidłowych elementów, ale nie powiedzą „blok nie sparsował się z powodu podwójnej encji”. Zobaczysz, że encji ubyło, i będziesz musiał sam dojść dlaczego. Dlatego skan po własnej stronie jest wart tych kilku minut: daje przyczynę, a nie tylko objaw.
Czego o tej zmianie nie wiemy i co robić mimo to
Nie znamy daty wdrożenia. Nie wiemy, czy zmiana dotyczy w równym stopniu wszystkich typów schema, ani czy Search Console w ogóle zaraportuje utraconą encję, czy po prostu przestanie pokazywać rich result. Nie ma zaktualizowanej dokumentacji, do której można odesłać klienta. To są realne luki i lepiej powiedzieć to wprost, niż udawać, że komunikat na LinkedIn ma rangę specyfikacji.
Mimo tych luk decyzja operacyjna jest prosta i nie zależy od żadnej z brakujących informacji. Poprawny JSON był poprawny również wtedy, gdy Google prostował błędy za nas. Sprawdź swój zbudowany HTML, napraw to, co się nie parsuje, zamień encje HTML w wartościach na ucieczki JSON i wpnij ten test w proces, żeby nie wracał. Jeśli wynik wyjdzie zerowy, jak u nas, to też jest wynik: wiesz, że Twój generator nie ma jak wyprodukować tego błędu, i możesz przestać się nad tym zastanawiać przy każdej aktualizacji wtyczki.
Największe ryzyko w tej historii nie leży w samej zmianie, tylko w tym, że jest cicha. Żaden alert nie przyjdzie. Rich result po prostu przestanie się pojawiać, a w raporcie za trzy miesiące zobaczysz spadek widoczności bez oczywistej przyczyny. Pięć minut skanu dzisiaj jest tańsze niż to dochodzenie.





