GridPane daje dobry punkt startu: szybki serwer, cache stron, Redis. Brakuje ostatnich 20%, które decydują, czy sklep jest naprawdę szybki dla ruchu, za który płacisz, i poprawny dla klientów, którzy kupują. O tych 20% jest ten tekst.
Tak wygląda setup, na którym stoją teraz nasze sklepy na GridPane:
| Co | Gdzie jest cache | Dlaczego |
|---|---|---|
| zwykłe strony | Redis na serwerze (nie FastCGI) | czyszczenie cache jednej strony nie rusza pozostałych |
wejścia z reklam (gclid, utm_*, fbclid) | ten sam wpis co czysty adres | klient z reklamy dostaje szybką stronę, analityka dalej działa |
| koszyk, zamówienie, konto (pod dowolną nazwą) | nigdzie | koszyk z cache potrafi pokazać klientowi cudzy koszyk |
| zmiana ceny albo stanu | strona produktu odświeżana od razu | inaczej sklep pokazuje starą cenę |
| CSS, JS, fonty, obrazki | Cloudflare | od tego jest CDN |
| kombajn do szybkości (FlyingPress, WP Rocket…) | brak | motyw i wtyczki optymalizujemy u źródła |
Całość jest open source: SHIFT64/gridpane-nginx-marketing-params. Instalator, testy i dziesięć wydań w szesnaście dni. Działa na naszych produkcyjnych sklepach.
Spis treści
Rozwiń spis treści
- Dlaczego nie FlyingPress ani WP Rocket
- Redis zamiast FastCGI
- Ruch z reklam: z cache i bez psucia analityki
- Czego nigdy nie trzymamy w cache
- Odświeżanie stron po zmianie ceny i stanu
- Cloudflare: tylko pliki statyczne
- Plus: odpowiedzi prosto z logów
- Sprawdzone na produkcji
- Co oddajesz w zamian
- Podsumowanie
Dlaczego nie FlyingPress ani WP Rocket
Bo wychodzimy z prostego założenia: motyw i wtyczki mają być szybkie u źródła, a nie wyglądać na szybkie dzięki kolejnej wtyczce.
Kombajny łączą dwie rzeczy: cache stron i przepisywanie frontu (opóźnianie JavaScriptu, wycinanie CSS, łączenie plików). Ta druga część częściej chowa problem, niż go rozwiązuje. Motyw, który ładuje 1,2 MB JavaScriptu, dalej go ładuje, tylko później, kiedy Lighthouse już nie patrzy. A kiedy wtyczka źle oceni skrypt bramki płatności, szukasz błędu w dwóch warstwach zamiast w jednej.
Cache stron GridPane już ma, na serwerze, zanim w ogóle ruszy WordPress. Dlatego żadnych kombajnów. To nie znaczy zero wtyczek: WordPress musi dać znać, że coś się zmieniło, i od tego są małe, jednozadaniowe helpery. Nginx Helper od GridPane czyści cache, a nasza mała wtyczka (o niej niżej) łata to, czego Nginx Helper nie widzi w WooCommerce. Żadna z nich nie przepisuje frontu.
Widać to prosto w logach. Ta sama strona kategorii na produkcyjnym sklepie, otwarta trzy razy z rzędu (adres i IP podmienione, czasy dokładnie takie, jak w logu):
[23/Oct/2026:16:02:31 +0200] 203.0.113.10 0.277 - sklep.example "GET /kategoria/lampy/ HTTP/2.0" 200 36377 0.319
[23/Oct/2026:16:02:31 +0200] 203.0.113.10 - - sklep.example "GET /kategoria/lampy/ HTTP/2.0" 200 36377 0.002
[23/Oct/2026:16:02:32 +0200] 203.0.113.10 - - sklep.example "GET /kategoria/lampy/ HTTP/2.0" 200 36377 0.001
- Trzecia kolumna to czas pracy PHP. Za pierwszym razem 0,277 s: WordPress zbudował stronę, a serwer zapisał ją w Redis. Za drugim i trzecim razem jest kreska: PHP w ogóle nie został zawołany.
- Ostatnia liczba to cały czas odpowiedzi: 0,319 s, potem 0,002 s i 0,001 s. Od 160 do 300 razy szybciej.
- Ta sama liczba bajtów (36377) we wszystkich trzech, więc to naprawdę ta sama strona, a nie błąd czy przekierowanie.
I to jest sedno: strony z cache w ogóle nie zajmują PHP. Procesy PHP zostają wolne dla tego, czego żaden cache nie obsłuży, czyli koszyka, zamówienia i panelu. Fala ruchu z kampanii czy od botów nie ma ich czym zapchać, więc sklep zostaje dostępny dla tych, którzy kupują. A boty potrafią przyjść naprawdę dużą falą: wczoraj jeden z naszych sklepów dostał ponad 70 tysięcy zapytań od bota Meta w 3 godziny.
Redis zamiast FastCGI
GridPane daje dwa rodzaje cache stron: FastCGI i Redis. Dla jednej strony są podobnie szybkie. Na serwerze z kilkoma stronami jest jedna duża różnica, i dotyczy czyszczenia. Przy FastCGI każda strona ma swoje wpisy w cache i nie widzi cudzych, a odświeżenie pojedynczego adresu (np. po edycji wpisu) dotyczy tylko jej. Ale wszystkie strony leżą w jednej wspólnej przestrzeni cache na serwerze. Kiedy któraś zrobi „wyczyść wszystko” (po zmianie motywu, aktualizacji wtyczek, wdrożeniu), opróżnia całą tę przestrzeń, czyli cache wszystkich stron. Tak jest w domyślnym ustawieniu GridPane. Da się to obejść, ale wymaga to więcej kombinowania i późniejszego pilnowania.
Złapaliśmy to na żywo. Na jednym serwerze staging czyścił cały cache po każdym wdrożeniu, od 1 do 11 razy dziennie. Za każdym razem produkcyjny sklep obok tracił cały swój cache i musiał budować strony od nowa. Przy dziesięciu stronach na jednym serwerze cache znika wiele razy dziennie, wszystkim naraz, a serwer dostaje skok obciążenia dokładnie wtedy, kiedy wszystkie strony budują się od zera.
Przy Redis każda strona ma własną bazę i czyszczenie zostaje w jej obrębie. Od razu, w domyślnym ustawieniu, bez żadnych przeróbek i bez dodatkowego utrzymania. Sprawdziliśmy to na dwóch serwerach, w tym jednym z 27 stronami: wyczyszczenie jednej strony nie ruszyło cache żadnej innej. Szczegóły i pomiary są w issue #1.
Jedno zastrzeżenie: strony dzielą pamięć Redis, więc bardzo duża strona może wypychać wpisy mniejszych. To i tak nieporównanie lepiej niż „każde wdrożenie na stagingu czyści produkcję”. Dlatego na każdym serwerze z więcej niż jedną stroną: Redis.
Ruch z reklam: z cache i bez psucia analityki
Reklama wysyła klienta pod adres w rodzaju:
/produkt/kubek/?gclid=EAIaIQobChMI...&utm_source=google
Dla klienta to ten sam kubek. Dla cache to inny adres. Zdecydowana większość konfiguracji cache, nie tylko na GridPane, w ogóle nie zapisuje adresów, które mają choćby jeden parametr po znaku zapytania. Taki klient omija cache i czeka, aż WordPress zbuduje stronę, która już jest gotowa pod /produkt/kubek/. Na żywej stronie zmierzyliśmy:
| Adres | Żądań na sekundę |
|---|---|
strona z gclid i utm, bez naszej poprawki | ~64 |
| ta sama strona z poprawką | ~6 300 |
| czysty adres, dla porównania | ~7 000 |
Mniej więcej sto razy więcej dla ruchu, za który płacisz najwięcej.
Ignorowanie parametrów marketingowych wisi na roadmapie GridPane od dłuższego czasu. W końcu postanowiliśmy nie czekać i zrobić to po swojemu.
Najprostszy sposób to przekierować klienta na czysty adres. Tyle że wtedy parametry znikają z paska adresu, a właśnie stamtąd czytają je GA4, Google Ads i Meta Pixel. Szybkość w zamian za zepsutą analitykę. Nasze rozwiązanie działa inaczej:
- Bez przekierowania. Parametry zostają w pasku adresu, analityka działa jak wcześniej.
- Jeden wpis w cache. Klient z reklamy dostaje tę samą gotową stronę co klient bez parametrów.
- WordPress widzi czysty adres. Niektóre strony wklejają parametry z adresu do treści (robi to choćby formularz komentarzy w WordPressie). Gdyby WordPress zobaczył
gclidpierwszego klienta, ta wartość trafiłaby do cache i dostawaliby ją wszyscy kolejni. - Przekierowania nie gubią parametrów. Gdy sklep przekieruje klienta (np. na adres z ukośnikiem na końcu), parametry jadą razem z nim.
- Reszta działa jak wcześniej. Wyszukiwarka czy dodawanie do koszyka z linku nadal trafiają prosto do WordPressa.
Na liście są tylko parametry czytane przez analitykę i piksele w przeglądarce. Nigdy te, z których korzysta sam sklep, jak ref, wyszukiwanie czy add-to-cart.
Jeśli czytałeś mój wcześniejszy tekst o parametrach UTM i cache w Cloudflare: Worker w Cloudflare nadal ma sens, kiedy nie masz wpływu na serwer. Na GridPane mamy, więc robi to serwer i żadna z naszych stron na GridPane już Workera nie używa.
Czego nigdy nie trzymamy w cache
Standardowe reguły rozpoznają koszyk, zamówienie i konto po angielskich adresach: /cart, /checkout, /my-account. Na polskim sklepie z koszykiem pod /koszyk/ znaleźliśmy stronę koszyka w cache. Gość bez ciasteczek mógł dostać zapisany koszyk innego gościa. Podobnie z REST API, z którego korzysta m.in. nowy koszyk WooCommerce.
Poprawka: o tym, czy strona to koszyk, decyduje teraz sam WooCommerce, niezależnie od jej nazwy i języka. Taka strona nigdy nie trafia do cache. REST API też nie.
Odświeżanie stron po zmianie ceny i stanu
Ta sama mała wtyczka rozwiązuje odwrotny problem: strony, które powinny się odświeżyć, a się nie odświeżają.
WooCommerce zapisuje zmiany cen i stanów w sposób, którego Nginx Helper nie zauważa: spadek stanu po zamówieniu, synchronizacja z ERP, import, edycja masowa, zaplanowana promocja. Na żywym sklepie w pięć dni zmieniło się 53 produkty i 22 warianty, a cache nie odświeżył się ani razu. Karta produktu pokazywała starą cenę albo „dostępny” aż do wygaśnięcia cache.
W tym samym czasie każda zmiana statusu zamówienia czyściła stronę główną, około 190 razy dziennie. Akurat stronę, na którą trafia większość reklam.
Wtyczka to odwraca:
- zmiana ceny albo dostępności odświeża produkt, jego kategorie, stronę sklepu i stronę główną;
- zmiana samej liczby sztuk przy tej samej dostępności domyślnie niczego nie odświeża, o ile sklep nie pokazuje stanów na stronie produktu. Jeśli pokazuje („zostało 12” → „zostało 11”), odświeżanie włącza się jednym filtrem także dla liczby sztuk;
- zamówienia, zwroty i notatki do zamówień niczego nie czyszczą;
- czyszczenie odbywa się już po wysłaniu odpowiedzi, więc klient przy zamówieniu na nie nie czeka.
Cloudflare: tylko pliki statyczne
Jeśli przed serwerem stoi Cloudflare, trzyma tylko CSS, JS, fonty i obrazki. Strony HTML zostają w Redis na serwerze, gdzie odświeżają się w chwili zmiany treści albo ceny. Żadnych nieaktualnych kopii w Cloudflare i żadnej wtyczki, która by nimi zarządzała.
Plus: odpowiedzi prosto z logów
Kiedy cache i jego czyszczenie dzieją się na serwerze, każde wejście i każde czyszczenie zostawia ślad w logach nginx. Nie trzeba zgadywać ani instalować kolejnej wtyczki do statystyk. Wystarczy zajrzeć do logów i policzyć, tak jak w wycinku wyżej.
Wszystkie liczby w tym tekście stamtąd pochodzą: staging czyszczący cache produkcji od 1 do 11 razy dziennie, około 190 czyszczeń strony głównej dziennie, 53 zmienione produkty bez ani jednego odświeżenia. Przy kombajnie decyzje o cache zapadają w środku wtyczki i dużo trudniej je prześledzić.
Sprawdzone na produkcji
Pierwsza wersja wyszła 7 października 2026, aktualna 23 października. Prawie każde z dziesięciu wydań wzięło się z czegoś, co zobaczyliśmy na produkcji: cache czyszczony przez sąsiednią stronę, /koszyk/ w cache, ceny, które się nie odświeżały.
Na żywym serwerze da się tego używać, bo:
- testy odpalają się na jednorazowej kopii z prawdziwymi szablonami GridPane z Twojego serwera i nie dotykają działających stron;
- instalator sprawdza konfigurację przed każdą zmianą i cofa ją, jeśli coś jest nie tak;
- test na żywo sprawdza Twoją stronę po instalacji;
- przypadki, których nie umie obsłużyć bezpiecznie, po prostu pomija i mówi dlaczego.
Instalacja, na serwerze GridPane jako root:
git clone https://github.com/SHIFT64/gridpane-nginx-marketing-params.git /root/gridpane-nginx-marketing-params
cd /root/gridpane-nginx-marketing-params && ./test.sh && ./install.sh --full twojsklep.pl
./test.sh live twojsklep.pl /jakas-strona/ --fresh
Więcej szczegółów technicznych, po angielsku: The Best GridPane Setup for WooCommerce na Shift64.
Co oddajesz w zamian
- Parametry z listy nie docierają do WordPressa na stronach z cache. Jeśli landing zmienia treść na podstawie
utm_campaignpo stronie serwera, przestanie to robić. (Ze stroną w cache i tak nie robił tego niezawodnie.) - Utrzymujesz to sam. Po aktualizacjach GridPane warto odpalić testy, które to sprawdzą.
Podsumowanie
Redis zamiast FastCGI, ruch z reklam obsługiwany z cache, koszyk nigdy w cache, ceny odświeżane od razu, Cloudflare tylko dla plików statycznych i zero kombajnów. Na tym stoją dziś nasze sklepy na GridPane.
To nie jest jednorazowa konfiguracja. Ciągle to poprawiamy i monitorujemy, a każde kolejne wydanie bierze się z tego, co widzimy w logach.
Jeśli masz sklep na WooCommerce, który trzeba dopicować, odezwij się. Zrobimy to.