Modelowane listy odbiorców nie zawierają konkretnych osób, tylko prawdopodobieństwa. Wyjaśniam, jak powstają, gdzie ich szukać w panelu i jak czytać ich raporty.
Rozmowy o szybkości strony zwykle zaczynają się od narzędzia pomiarowego i kończą na liście zaleceń, których nikt nie wdraża, bo połowa dotyczy rzeczy niezależnych od zespołu. Chcę tu podejść inaczej i pokazać warstwę, o której w takich rozmowach mówi się najrzadziej: sam transport danych i miejsce, w którym te dane leżą.
Trafiam na serwisy, w których obrazki są skompresowane, skrypty odchudzone, a strona wciąż odpowiada wolno — bo żądanie leci przez pół Europy do jednego serwera, tam czeka na zapytanie do bazy, a całość odbywa się na starym protokole i po drodze trzy razy negocjuje połączenie.
Nie jest to dziedzina, w której specjalista od kampanii samodzielnie coś przestawi. Jest to natomiast wiedza, która pozwala rozmawiać z zespołem technicznym o właściwych rzeczach i nie żądać optymalizacji tam, gdzie nie ma czego optymalizować.
Zacznę od rozbioru na części, bo bez tego dyskusja o protokołach jest wróżeniem.
Czas, po którym przeglądarka dostaje pierwszy bajt odpowiedzi, składa się z kilku niezależnych składników: rozwiązania nazwy domeny, zestawienia połączenia sieciowego, uzgodnienia szyfrowania, przekazania żądania do serwera, wygenerowania odpowiedzi i drogi powrotnej. W każdym z nich można stracić czas i w każdym optymalizuje się co innego.
Kluczowa obserwacja jest taka, że składniki sieciowe zależą przede wszystkim od odległości fizycznej. Sygnał w kablu ma swoją prędkość i żadne przyspieszanie kodu tego nie obejdzie. Jeśli serwer stoi w Niemczech, a użytkownik jest w Polsce, koszt jednego obiegu jest niewielki. Jeśli serwer stoi za oceanem, każdy dodatkowy obieg to realne dziesiątki milisekund.
Dlatego przy diagnozie zawsze pytam najpierw o dwie rzeczy: gdzie fizycznie stoi aplikacja i ile obiegów wymaga nawiązanie połączenia. Reszta jest ważna, ale wtórna.
Warto też rozdzielić czas generowania odpowiedzi od czasu jej dostarczenia. Zdarza się, że zespół inwestuje w sieć dostarczania treści, a problemem jest zapytanie do bazy wykonywane przy każdym wejściu. Wtedy zmiana warstwy transportowej niczego nie naprawi i pojawia się rozczarowanie.
Wersje protokołu HTTP różnią się w sposób, który ma bezpośrednie przełożenie na czas ładowania.
Do tego dochodzi warstwa szyfrowania. Nowsza wersja protokołu TLS potrzebuje mniej obiegów na uzgodnienie połączenia niż starsza, a przy powtórnym połączeniu potrafi obejść się bez pełnej negocjacji. To jeden z tych zysków, których nie widać w audycie kodu, a które użytkownik odczuwa przy każdym wejściu.
Praktyczna rada: nie traktuj włączenia nowszego protokołu jako projektu optymalizacyjnego. To zwykle jedno ustawienie u dostawcy albo w konfiguracji serwera brzegowego, a nie przebudowa serwisu. Sprawdź, co masz włączone, zanim zaczniesz szukać oszczędności w kilobajtach.
Skoro jesteśmy przy transporcie — druga rzecz, która działa od razu, a bywa nieustawiona.
Pliki tekstowe, czyli kod strony, arkusze stylów i skrypty, powinny być kompresowane w locie. Nowszy algorytm kompresji daje przy tych treściach lepszy wynik niż wysłużony standard używany od lat, a obsługują go wszystkie liczące się przeglądarki. Sprawdzenie zajmuje chwilę: wystarczy zajrzeć w nagłówki odpowiedzi w narzędziach dla programistów.
Osobna sprawa to obrazy. Tu kompresja na poziomie transportu nic nie daje, bo pliki są już skompresowane w swoim formacie. Zysk siedzi w wyborze formatu i w tym, żeby nie wysyłać grafiki większej niż miejsce, w którym się wyświetli. To najczęstszy problem, jaki widzę na stronach sklepów: zdjęcie produktu w rozmiarze oryginalnym, przeskalowane w przeglądarce.
Trzecia rzecz, wciąż pomijana: nagłówki określające czas życia zasobów w pamięci podręcznej. Zasoby statyczne z odciskiem treści w nazwie pliku mogą być cache’owane bardzo długo. Bez tego każde kolejne wejście użytkownika kosztuje tyle samo co pierwsze, mimo że nic się nie zmieniło.
Klasyczna sieć dostarczania treści rozstawia po świecie kopie plików statycznych. To rozwiązuje część problemu — obrazki i skrypty przychodzą z pobliskiego węzła. Ale strona generowana dynamicznie nadal powstaje w jednym miejscu.
Nowsze podejście przenosi na brzeg samo wykonanie kodu. Funkcja odpowiadająca za złożenie strony uruchamia się w węźle najbliższym użytkownikowi, a nie w jednym centrum danych. Przy serwisach o zasięgu międzynarodowym różnica jest zauważalna, bo znika najdroższy element — daleka droga do aplikacji.
Problem pojawia się natychmiast po tym, jak przenieśliśmy kod: dane zostały tam, gdzie były. Funkcja działająca w węźle w Warszawie, która musi zapytać bazę stojącą za oceanem, jest wolniejsza niż całość uruchomiona w jednym miejscu obok tej bazy. To najczęstszy błąd przy pierwszym podejściu do architektury brzegowej i widziałem go w projektach prowadzonych przez zdolne zespoły.
Stąd rosnące zainteresowanie bazami danych zaprojektowanymi pod ten model: rozproszonymi geograficznie, z repliką do czytania blisko każdego węzła, czasem z całą bazą trzymaną lokalnie w węźle. Zapisy nadal trafiają do jednego miejsca, ale odczyty — których w typowym serwisie jest znacznie więcej — obsługiwane są lokalnie.
Nie każdy serwis tego potrzebuje. Jeśli cały ruch przychodzi z Polski, dobrze skonfigurowany serwer w Europie z sensowną pamięcią podręczną załatwia sprawę taniej i prościej. Architekturę rozproszoną rozważałbym przy rzeczywiście międzynarodowym zasięgu albo przy treściach mocno personalizowanych, których nie da się cache’ować.
Przy tej okazji warto uporządkować pojęcia, bo słowo „cache” znaczy w tych rozmowach cztery różne rzeczy naraz.
Najbliżej użytkownika działa pamięć przeglądarki — sterowana nagłówkami odpowiedzi, najtańsza w utrzymaniu i najczęściej ustawiona byle jak. Dalej jest pamięć węzła brzegowego, w której leży kopia odpowiedzi dla wielu użytkowników. Jeszcze dalej pamięć po stronie aplikacji: gotowe fragmenty stron, wyniki zapytań, sesje.
Największy zysk daje przesunięcie treści o jeden poziom bliżej użytkownika. Strona kategorii w sklepie, która nie zmienia się co minutę, może być składana raz i podawana z brzegu wielu osobom. To zdejmuje obciążenie z aplikacji i skraca czas odpowiedzi jednocześnie.
Trudna część nie jest techniczna, a organizacyjna: trzeba zdecydować, co i na jak długo można cache’ować oraz co unieważnia kopię. Przy sklepie oznacza to rozmowę o cenach i stanach magazynowych, a nie o protokołach. Bez tej rozmowy zespół ustawia najkrótszy możliwy czas życia kopii i cała warstwa przestaje mieć sens.
Ostatnia rzecz i dla mnie najważniejsza, bo tu popełnia się najwięcej błędów interpretacyjnych.
Test laboratoryjny uruchamiany z jednej lokalizacji, na sztucznie ustalonym łączu, pokazuje wynik powtarzalny i wygodny do porównań między wdrożeniami. Nie pokazuje natomiast, co dzieje się u realnych użytkowników — a przy zmianach w warstwie sieciowej to właśnie tam siedzi cały zysk, bo zależy on od odległości i jakości połączenia.
Dlatego przy takich projektach patrzę przede wszystkim na dane z pola: pomiary zbierane od faktycznych odwiedzających, rozbite na kraj, typ urządzenia i typ podstrony. Dopiero wtedy widać, czy przeniesienie treści na brzeg pomogło komukolwiek poza osobą uruchamiającą test.
Zwracam też uwagę na to, żeby nie oceniać efektu po jednym wskaźniku. Skrócenie czasu odpowiedzi serwera poprawia najczęściej moment pojawienia się głównej treści, ale nie naprawi responsywności strony, jeśli po załadowaniu skrypty blokują wątek przeglądarki. Interakcyjność mierzona od wprowadzenia nowego wskaźnika w Core Web Vitals zależy od kodu wykonywanego u użytkownika, nie od tego, jak szybko dotarł.
I trzymam się zasady, którą powtarzam każdemu zespołowi: jedna zmiana, potem tydzień obserwacji danych z pola, potem następna. Wdrożenie nowego protokołu, przeniesienia kodu na brzeg i przebudowy cache w jednym tygodniu daje efekt, którego nie da się przypisać do przyczyny. A bez przypisania nie ma z tego żadnej wiedzy na przyszłość.
Używamy plików cookies, aby ułatwić Ci nawigację oraz wykonywanie określonych funkcji. Szczegółowe informacje o wszystkich plikach cookies znajdziesz w każdej kategorii zgody poniżej.
Pliki cookies oznaczone jako "Niezbędne" są przechowywane w Twojej przeglądarce, ponieważ są one kluczowe dla zapewnienia podstawowych funkcji strony.
Używamy również plików cookies firm trzecich, które pomagają nam analizować, w jaki sposób korzystasz z tej strony, zapamiętują Twoje preferencje oraz dostarczają treści i reklamy odpowiednie dla Ciebie. Te pliki cookies będą przechowywane w Twojej przeglądarce tylko za Twoją uprzednią zgodą.
Możesz zdecydować, czy chcesz włączyć lub wyłączyć niektóre bądź wszystkie te pliki cookies, jednak wyłączenie niektórych z nich może wpłynąć na Twoje doświadczenia podczas przeglądania strony.
| Cookie | Czas przechowywania | Opis |
|---|---|---|
| __cf_bm | 13 minutes | Cloudflare bot management — distinguishes humans from bots. |
| rc::* | Persistent | Google reCAPTCHA — localStorage holding anti-bot challenge state. |
| Cookie | Czas przechowywania | Opis |
|---|---|---|
| wpEmojiSettingsSupports | Session | WordPress — sessionStorage flag caching whether the browser can render emoji (feature detection). |
| ytidb* | Persistent | YouTube — IndexedDB storing playback/search state for embedded videos. |
| Cookie | Czas przechowywania | Opis |
|---|---|---|
| _ga_* | 400 days | Google Analytics 4 — persists session state per property. |
| _ga | 400 days | Google Analytics — distinguishes unique users via a client id. |
| Cookie | Czas przechowywania | Opis |
|---|---|---|
| NID | 183 days | Google — stores preferences for personalized ads. |
| fr | 90 days | Meta — encrypted Facebook id and browser id for ads. |
| _gcl_au | 90 days | Google AdSense/Ads — experiments with advertising efficiency (conversion linker). |
| Cookie | Czas przechowywania | Opis |
|---|---|---|
| __Secure-YNID | 180 days | |
| YSC | Session | |
| __Secure-ROLLOUT_TOKEN | 180 days | |
| VISITOR_INFO1_LIVE | 180 days | |
| VISITOR_PRIVACY_METADATA | 180 days | |
| datr | 400 days | |
| sb | 400 days | |
| wd | 7 days |