← Powrót do bloga
EngineeringBuild in PublicSEO

Nasze strony Next.js byly za duze dla Binga. Payload RSC to 58 % HTML-a

Trzy strony Next.js utkniete na wykryta, ale nieprzeszukana w Bingu. App Router wysyla kazdy bajt dwa razy. Zmniejszylismy jedna o 48 KB i Bing ja zaindeksowal.

8 października 2026

Nasze strony Next.js byly za duze dla Binga. Payload RSC to 58 % HTML-a

Katto to narzędzie do cięcia wideo z użyciem AI. Dajesz mu długie nagranie, a ono znajduje najlepsze momenty i tnie je na pionowe klipy z napisami. Buduję je sam, publicznie, na Next.js z App Routerem. To jest pomiar, który zmienił mój sposób pisania stron, liczba, którą podałem jako pewną przy wszystkich i która okazała się błędna, oraz moja własna hipoteza warta 108 bajtów.

Objaw

Bing Webmaster Tools pokazywał kilka naszych stron jako wykryte, ale nigdy nieprzeszukane. Nie blokował ich robots.txt, nie miały noindex, nie były wolne, nie przekierowywały. Wykryte, a potem porzucone. Google miało je w indeksie. Nasza mapa witryny je wymieniała. Każdemu odpowiadały kodem 200.

Odrzucone strony miały jedną wspólną cechę i nie była nią treść.

Granica i jak słabo ją znamy

Microsoft nie publikuje rozmiaru, powyżej którego jego robot się poddaje. To, co mamy, to nasze własne strony, każda ze statusem odczytanym w Bing Webmaster Tools i z rozmiarem serwowanym zmierzonym tego samego dnia, bez kompresji, z user agentem bingbota:

Jedna strona po drugiej stronie linii to nie prawo. Ale na tej liście jest jedna obserwacja warta więcej niż cała reszta, bo to dwa razy ten sam adres. /compare/ai-video-clippers serwował 172 295 bajtów i tygodniami tkwił na wykryta, ale nieprzeszukana. Zmniejszyliśmy go do około 124 KB. Dziś jest zaindeksowany, przy 127 951 bajtach. Ten sam adres, ten sam szablon, ten sam rodzaj treści: za duży, a potem dość mały.

Tydzień temu podałbym inną liczbę i byłaby błędna. Miałem trzy odczyty statusu i wyraźną lukę w rozkładzie naszych rozmiarów, i wyczytałem z tego granicę w okolicach 125 KB. Dwie z tych stron zostały od tamtej pory zaindeksowane przy niecałych 128 KB. To, co nasza własna witryna może naprawdę powiedzieć, to że linia leży między 128 a 140 KB, a jej druga strona wciąż ma szerokość dokładnie jednej strony.

Jeśli przyszedłeś po liczbę, uczciwa odpowiedź brzmi: zmierz własną. Twierdzenie, którego mogę bronić, jest i tak węższe i bardziej użyteczne: stronę, która utknęła na wykryta, warto zważyć, zanim warto ją przepisać, a strona, która zaczyna być przeszukiwana po odchudzeniu, to jedyny dowód, który naprawdę się liczy.

Ważenie pliku źródłowego nic nie mówi

Oto część specyficzna dla React Server Components. App Router serializuje wyrenderowane drzewo do tego samego dokumentu HTML, jako ciąg znaczników self.__next_f.push(...), żeby nawigacja po stronie klienta mogła ruszyć dalej bez kolejnego obiegu. Ten ładunek nie jest osobnym żądaniem, które robot może pominąć. Jest w dokumencie.

Zmierzone na jednej z naszych stron: 125 981 serwowanych bajtów, z czego 73 571 to ten ładunek. Pięćdziesiąt osiem procent dokumentu to zserializowana kopia tego, co pozostałe czterdzieści dwa procent już mówią.

Więc klasa narzędziowa napisana raz w pętli po 24 wierszach nie wyjeżdża 24 razy. Wyjeżdża 48 razy.

To, co zajęło mi najwięcej czasu

To nie podwaja strony. To podwaja każdą reprezentację, którą strona już niesie.

Nasza roadmapa celowo renderowała każdy wpis dwa razy: raz jako widoczny tekst, raz w bloku ItemList w JSON-LD, żeby czytniki maszynowe dostały nazwę, datę i status jako dane, a nie jako prozę do interpretacji. Rozsądne. Potem policzyłem wystąpienia opisu jednego wpisu w serwowanym dokumencie i wyszły cztery.

Dwie reprezentacje, każda podwojona. Cztery kopie tego samego zdania, a nigdy nie przyszło mi do głowy, żeby policzyć.

Poprawką nie była kompresja. JSON-LD niósł description, która słowo w słowo kopiowała tekst widoczny dla czytelnika trzydzieści wierszy niżej, w tym samym języku. Nic nie wnosiła, a kosztowała dwie z czterech kopii. Po usunięciu blok zachowuje nazwę, datę i status, czyli dokładnie to, czego proza nie mówi maszynie wprost. Ta strona zeszła ze 121 196 do 115 350 bajtów i nie ubyło z niej żadnej informacji. Obie liczby pochodzą z tego samego builda, przed tą jedną zmianą i po niej, bo tylko tak porównanie przed i po cokolwiek znaczy.

Jeśli masz wynieść stąd jeden nawyk: zanim zaczniesz cokolwiek odchudzać, policz, ile razy jedno zdanie twojej treści pojawia się w serwowanym dokumencie. Odpowiada na pytanie, na które ważenie sekcji nie odpowiada.

Dwie większe dźwignie

Powtarzane atrybuty klas przeniesione do reguł arkusza stylów. Narzędzia Tailwinda pisze się cudownie tanio i jadą przy każdym elemencie, dwa razy. Przeniesienie tych powtarzanych do reguł @apply zbiło nasz indeks cen ze 172 295 bajtów do około 142 000. Stanowiły mniej więcej 51 procent znaczników.

Bloki nieinteraktywne poza zserializowanym drzewem. Żeby było precyzyjnie, bo to ma znaczenie: to są Server Components, więc nigdy nie są hydratowane po stronie klienta. Kosztem nie jest hydratacja, tylko serializacja. Każdy element, każdy prop i każdy klucz zapisuje się do ładunku, żeby nawigacja po stronie klienta mogła odbudować drzewo bez obiegu. Statyczna tabela bez żadnego zachowania klienckiego i tak za to płaci, a kiedy ograniczeniem jest rozmiar dokumentu, nie ma powodu, żeby w ogóle pozostawała elementami Reacta. Budujemy teraz takie bloki jako łańcuch HTML z tych samych danych i wstawiamy przez dangerouslySetInnerHTML, escapując każdą interpolowaną wartość w momencie budowania, żeby escapowanie nie zależało od tego, skąd dane przyszły. Kolejne 18 KB na indeksie cen. Lista 54 wierszy na roadmapie zeszła tą samą drogą ze 157 308 do 119 031.

To, co nie zadziałało

Przed tym wszystkim byłem pewien, że problemem są granice komponentów. Pięćdziesiąt cztery komponenty next/link na liście, każdy dokładający do ładunku swoją zserializowaną nazwę, propsy i klucze. Zamieniłem wszystkie 54 na zwykłe kotwice.

Zaoszczędziło to 108 bajtów ze 157 200.

Link jest tani. Treść, którą opakowuje, nie jest. Spędziłbym dzień na przepisywaniu komponentów według tej teorii, gdybym nie zmierzył przed i po, a ten negatywny wynik był najbardziej użyteczną liczbą tego dnia.

Jest podłoga i nie jest niska

Nasza najlżejsza prawdziwa strona, /contact, serwuje 46 111 bajtów, niosąc tylko nagłówek, krótki formularz i kilka pytań. Reszta to nawigacja, stopka, analityka i fonty. Poniżej tej liczby nie ma już czego zdobywać strona po stronie: to byłby projekt powłoki, nie projekt strony. Znajomość podłogi mówi ci, kiedy przestać.

Tłumaczenie kosztuje bajty, nie słowa

Przełożyliśmy tę roadmapę na dziesięć języków. Te same znaczniki, ta sama struktura, ta sama informacja. Strona angielska serwuje 116 239 bajtów, francuska 119 838, japońska 122 155, a hindi 135 379.

Dewanagari zajmuje w UTF-8 trzy bajty na znak tam, gdzie tekst łaciński zajmuje jeden, a ładunek płaci za to drugi raz. Żadna technika na poziomie strony tego nie rusza: te same znaczniki, ta sama informacja, dziewiętnaście kilobajtów różnicy.

Strona hindi jest najciekawsza, bo przy 135 379 bajtach wpada dokładnie w pasmo, którego nie umiemy rozstrzygnąć. To, czy Bing ją przetworzy, jest najtańszym eksperymentem, jaki mamy, i kosztuje jedną inspekcję adresu.

Napisałem tu kiedyś akapit o tym, że ta nadwyżka nie ma wielkiego znaczenia, bo wypada na końcu dokumentu, a końcem tej strony jest lista, której tytuły i tak zostają po angielsku. Wyciąłem go, bo zakłada, że robot czyta, aż wyczerpie budżet, i wtedy przestaje. Wszystko, co naprawdę zaobserwowałem, mówi tylko tyle, że strony powyżej pewnego rozmiaru nie są przetwarzane. Jeśli dokument jest odrzucany w całości, jego kolejność niczego nie kupuje, a dowodu nie mam w żadną stronę. To wygodna teoria o mojej własnej stronie, czyli dokładnie ten rodzaj, który najbardziej warto skasować.

To budżet, nie naprawa

Indeks cen, który zbiłem ze 172 295 do 124 202 bajtów, jest dziś na 127 951, bo wciąż coś do niego dokładaliśmy: więcej narzędzi, więcej kolumn, dziesięć tłumaczeń. Nadal jest zaindeksowany, i o to właśnie chodzi, a zarazem wrócił na kilka kilobajtów od linii. Nic nie poszło źle. Po prostu znów wydaliśmy budżet, nie pilnując go.

To jest prawdziwy wniosek. We frameworku, który serializuje własne wyjście, waga strony nie jest sprzątaniem, które robi się raz. To liczba, która rośnie za każdym razem, gdy ktoś wykona dobrą robotę, a jedyną obroną jest mierzenie serwowanego dokumentu zamiast źródła, po jednej zmianie naraz.

Jak zmierzyć swoje

Related articles

Gotowy, aby zamienić swoje filmy w wiralowe klipy?

Katto automatycznie wycina klipy, dodaje napisy i przekadrowuje Twoje długie filmy w treści krótkie.

Wypróbuj Katto za darmo →
Nasze strony Next.js byly za duze dla Binga. Payload RSC to 58 % HTML-a | Katto