WooCommerce najczęściej działa wolno mimo WP Rocket, bo sklep ustawia ciasteczko każdemu gościowi, a strony z nagłówkiem Set-Cookie nie trafiają do cache. Zwykle winna jest sesja PHP uruchamiana przez motyw albo wtyczkę. W tym tekście opisujemy pięć przyczyn wolnego sklepu, które znaleźliśmy u klientów: ciasteczka, OPcache, przeciążony serwer, ciężkie zapytania i hosting. Przy każdej podajemy liczby przed i po naprawie.
Jak sprawdzić, czy cache w WooCommerce w ogóle działa?
Objaw w jednym ze sklepów: WP Rocket aktywny od miesięcy, a czas odpowiedzi serwera (TTFB) na każdej stronie 1300–1600 ms. Katalog cache był pusty. Wtyczka nie zgłaszała żadnego błędu, po prostu niczego nie zapisywała.
Sprawdzisz to w minutę. Wyślij zapytanie jako niezalogowany gość i obejrzyj nagłówki:
curl -sI https://example.com/ | grep -iE 'set-cookie|x-cache|cf-cache-status'- Jeśli widzisz
Set-Cookie: PHPSESSID=..., sklep startuje sesję PHP dla każdego gościa. To blokuje cache. - Każde inne ciasteczko ustawiane gościowi przy pierwszym wejściu też jest podejrzane.
- Zajrzyj do
wp-content/cache/wp-rocket/. Pusty katalog po kilku godzinach ruchu oznacza, że cache nie działa.
Przyczyna 1: dlaczego sesja PHP wyłącza cache WP Rocket?
Motyw wywoływał session_start() bezwarunkowo, na hooku init z priorytetem 1, więc każda strona dostawała ciasteczko PHPSESSID, a WP Rocket nie zapisywał żadnej z nich. Ani jednej. Drugi bloker znaleźliśmy we wtyczce płatności, która ustawiała własne ciasteczko każdemu nowemu gościowi.
Tu jest pułapka. Wcześniej ktoś próbował obejść problem w nginx dyrektywą fastcgi_ignore_headers Set-Cookie. Efekt: serwer cache'ował odpowiedź razem z ciasteczkiem i różni użytkownicy dostawali ten sam PHPSESSID, czyli współdzielili sesję. Dlatego kolejność naprawy jest ważna: najpierw usuwasz sesję, dopiero potem zdejmujesz to obejście.
Masz ten sam problem? Widzisz Set-Cookie dla niezalogowanego gościa? Sprawdzimy, która wtyczka je ustawia i jak się jej pozbyć bez psucia sklepu. Opisz sytuację →
Co zmieniliśmy:
- Sesja startuje dopiero wtedy, gdy kod faktycznie coś do niej zapisuje (lazy session).
- Moduł „ostatnio oglądane” przeszedł z sesji PHP na ciasteczko i AJAX.
- Mały mu-plugin neutralizuje ciasteczka, których gość nie potrzebuje.
- W PixelYourSite wyłączyliśmy sesję.
- Wygenerowaliśmy od nowa przeterminowany config WP Rocket. Stary miał zapisany permalink bez końcowego ukośnika, więc pomijał cache dla każdego adresu.
| Pomiar | Przed | Po |
|---|---|---|
| TTFB realnego użytkownika przez Cloudflare | 1300–1600 ms | 41–107 ms |
| Origin, trafienie w cache | brak cache | 7 ms |
Po nagrzaniu cache strona główna odpowiada w 31 ms, produkty w 32–55 ms. Zimne strony, jeszcze nie zapisane w cache, nadal ładują się 1,5–2,1 s. Ten sam wzorzec znaleźliśmy w drugim sklepie: session_start() na init i wtyczka do pobierania danych z GUS skutecznie wyłączały page cache.
Przyczyna 2: czy OPcache może spowolnić albo położyć sklep?
OPcache przyspiesza PHP, ale źle ustawiony daje dwa rodzaje kłopotów.
Stary kod po wdrożeniu. Przy opcache.validate_timestamps=0 PHP nie sprawdza, czy pliki się zmieniły. Po wdrożeniu trzeba przeładować PHP-FPM (sygnał USR2 albo restart). Polecenie wp eval 'opcache_reset();' czyści OPcache procesu CLI, a nie FPM. Z kolei revalidate_freq=240 kosztowało nas 2–3 fałszywe diagnozy typu „poprawka nie działa”, bo PHP sprawdzał zmiany w plikach najwyżej co 4 minuty.
Uszkodzona pamięć OPcache. Cały sklep zwracał błąd 500, a w logu był taki wpis z pliku motywu:
Allowed memory size of ... bytes exhausted (tried to allocate 4295229440 bytes) in .../theme.phpNikt nie alokuje 4 GB w szablonie. Przyczyną był za mały OPcache (bufor interned strings zapełniony w 100%) w połączeniu z JIT w trybie tracing na PHP 8.4. Na PHP 8.3 ten sam tryb JIT powodował SIGSEGV procesów php-fpm. Ustawienia, które to rozwiązały:
opcache.memory_consumption=512
opcache.interned_strings_buffer=64
opcache.jit=offPrzyczyna 3: co mają procesy zombie do wolnego sklepu?
Czasem winny jest serwer, nie WordPress. Na wspólnym VPS działało około 15 projektów, w tym sklepy WooCommerce, a jedna z aplikacji używała Puppeteera, czyli przeglądarki Chrome sterowanej z Node.js w kontenerze Docker. Po godzinie na serwerze było 2971 procesów zombie, około 500 na kontener. W kontenerze PID 1 to był node, który nie zbiera zakończonych procesów potomnych Chrome.
Naprawa to jedna linia w docker-compose: init: true. Docker uruchamia wtedy tini jako PID 1 i liczba zombie spadła do zera. Osobny problem stanowiła nielimitowana równoległość tej samej aplikacji: 2,5 GB zajętej pamięci, OOM, swap i load 40–60. A na tym samym serwerze stały sklepy klientów. Jeśli load rośnie bez widocznego powodu, sprawdź też, czy to nie koparka kryptowalut w kontenerze.
Przyczyna 4: ciężkie zapytania i „zombie cache”
W kolejnym sklepie WP Rocket był wyłączony, ale w wp-content został jego plik advanced-cache.php. Ten dropin dalej serwował HTML sprzed 9 dni.
Ten sam sklep miał zapytanie z meta_query łączącym OR i NOT EXISTS na tabeli postmeta z 1,36 mln wierszy. Jedno wykonanie trwało 10–16 s. Po naprawie listing produktów spadł z 2–8 s do 1,2–2,8 s, a strony dla gości z 85–120 ms do 65–85 ms.
Takie zapytania znajdziesz wtyczką Query Monitor (lista zapytań z czasem i miejscem wywołania) albo w slow query logu MySQL. Jeśli nie używasz żadnej wtyczki cache, a plik advanced-cache.php istnieje, to też jest sygnał do sprawdzenia.
Przyczyna 5: czy zmiana hostingu przyspieszy WooCommerce?
Częściowo. Po migracji jednego sklepu z hostingu współdzielonego na nowy serwer zmierzyliśmy:
| Strona | Hosting współdzielony | Nowy serwer |
|---|---|---|
| Koszyk | 2,09–2,64 s | 1,06–1,42 s |
| Strona główna (zimna) | 4,6–6,8 s | 2,1–2,3 s |
| Produkt (zimny) | 2,6–8,2 s | 1,30–1,32 s |
| Strona z cache (ciepła) | ok. 0,07 s | ok. 0,07 s |
Strony z cache były szybkie już wcześniej. Lepszy hosting pomaga tam, gdzie cache nie sięga, czyli w koszyku i na checkoucie, a jeśli cache w ogóle nie działa przez ciasteczko, sama migracja tego nie naprawi i każde wejście dalej będzie liczone przez PHP od zera.
W jednym z audytów pomogło zajrzenie w statystyki bazy. Redis jako object cache miał 98,3% trafień, bufor InnoDB 99,98%, ale 27% tabel tymczasowych lądowało na dysku. Podnieśliśmy tmp_table_size z 16 do 64 MB.
Od czego zacząć diagnozę wolnego sklepu?
- Sprawdź nagłówki
curl -Ijako gość: Set-Cookie, x-cache, cf-cache-status. - Zajrzyj do katalogu cache.
- Upewnij się, że skrypt wdrożenia przeładowuje PHP-FPM, bo reset OPcache z poziomu WP-CLI nie dotyka procesów, które obsługują ruch.
- Przejrzyj log błędów PHP: dziwne alokacje pamięci, SIGSEGV.
- Sprawdź load i liczbę procesów, zwłaszcza na serwerze współdzielonym z innymi projektami.
- Włącz Query Monitor i slow query log na listingach produktów.
Kolejna wtyczka do cache nie pomogłaby w żadnym z tych przypadków. Dlatego w ASAPDevs zaczynamy optymalizację WooCommerce od pomiaru, a nie od instalowania kolejnej wtyczki.
