Кешування WordPress, яке не ламає кошики й форми

Було корисно?

Все добре, поки не починаєш помічати помилки: CEO намагається купити худі, загальна сума в кошику неправильна, а ваш «високопродуктивний кеш» радо віддає учорашню сесію сьогоднішньому клієнту. Або ваша лід-ген форма «працює на моїй машині», а в продакшені зникає у циклі кешованої сторінки подяки.

Кешування не є лиходієм. Неправильне охоплення кешу — ось проблема. Мета — швидкість без функціональних багів: кошики залишаються персональними, оформлення замовлення — реальним, форми — унікальними, а сесії авторизованих користувачів не стають публічним ефіром.

Практична модель: що можна кешувати безпечно

Кешування WordPress — це стек, а не одна функція. Ви можете мати (і часто маєте) кілька кешів одночасно: кеш браузера, кеш на краю CDN, кеш реверс-проксі (Varnish або NGINX), OPcache PHP, «page cache» від плагіна, object cache (Redis/Memcached) і кеші бази даних. Кожен вирішує різну проблему і кожен може по-своєму «очарувати» ваш сайт помилками.

Шари кешу і для чого вони підходять

  • Кеш браузера (Cache-Control, ETag): відмінно підходить для статичних ресурсів (CSS/JS/зображення). Не для HTML з персоналізацією.
  • Кеш CDN: відмінно для статичних ресурсів і інколи для анонімного HTML, якщо ви правильно обходите сесії і потоки оформлення замовлення.
  • Реверс-проксі / full-page cache: відмінно для анонімного трафіку. Небезпечне для всього, що залежить від сесії, якщо ви не налаштували логіку cookie правильно.
  • Плагін кешу сторінок WordPress: часто записує статичний HTML на диск. Легко розгорнути, легко неправильно налаштувати, може конфліктувати з проксі/CDN.
  • Object cache (Redis): кешує результати запитів і обчислені об’єкти. Зазвичай безпечний для авторизованих користувачів і WooCommerce, якщо плагін поводиться коректно.
  • PHP OPcache: прискорює виконання PHP. Рідко ламає функціональність; частіше викликає проблеми під час деплоїв, коли ви забуваєте інвалідизувати.

Правило, яке врятує вас від проблем

Кешуйте анонімні GET-запити для сторінок, які не залежать від користувача, місця, валюти, кошика чи стану автентифікації. Усе інше обходьте (або кешуйте з явними ключами варіації, які ви зможете відстоювати при інциденті).

От і все. Звучить очевидно. На практиці це ламається, бо «анонімний» — хитка категорія: користувач може бути технічно неавторизованим, але мати кошик, перемикач валюти чи форму з nonce, який має бути унікальним. Також WordPress полюбляє cookie. WooCommerce обожнює cookie. Ваш маркетинговий набір обожнює cookie. Cookie — це спосіб, яким кеш вчиться переставати бути кешем.

Сухий факт: якщо ви не можете пояснити, чому відповідь кешується, вказавши на заголовки, ключі та правила обходу, у вас не кеш — у вас рулетка.

Короткий жарт №1: Якщо сторінка оформлення замовлення кешується — вітаю, ви винайшли спільний шопінг.

Що ніколи не повинно кешуватися (HTML)

Це незмінні правила для більшості WordPress-сайтів:

  • /wp-admin/ та /wp-login.php
  • Будь-яка сторінка для авторизованих користувачів (якщо тільки ви не маєте навмисного «приватного кешу» з ключами на користувача — це рідко і складно)
  • WooCommerce: /cart/, /checkout/, /my-account/, endpoints додавання в кошик, endpoints фрагментів
  • Ендпоїнти форм і сторінки з пропадаючими nonce, якщо ви покладаєтесь на стандартну поведінку WordPress
  • Все, що містить CSRF-токени, персоналізовані ціни, лічильники доступності, які мають бути актуальними, або повідомлення, залежні від сесії

Що зазвичай можна кешувати (HTML)

  • Публічні маркетингові сторінки, пости блогу, сторінки документації
  • Архіви категорій/тегів для анонімних відвідувачів
  • Сторінки списків продуктів (PLP) для анонімних відвідувачів, якщо ціни/валюта не змінюються
  • Сторінки товарів (PDP) для анонімних відвідувачів, з увагою до повідомлень «в наявності»

Визначте «var y» серйозно

Якщо HTML змінюється на основі будь-чого з цього, ваш кеш має або обходити, або варіювати ключ кешу:

  • Cookie (кошик, сесія, згода, A/B-тести)
  • Гео/країна (податки, права доставки, правила контенту)
  • Валюта й мова
  • Тип пристрою (рідко варто варіювати зараз; зазвичай перемагає адаптивний дизайн)
  • Заголовки авторизації

Цікаві факти й історичний контекст (бо минуле все ще платить рахунки)

  1. HTTP-кешування існувало задовго до WordPress. RFC 2616 (HTTP/1.1) формалізував семантику Cache-Control у 1999 році, і ми досі сперечаємося про «must-revalidate».
  2. WordPress популяризував «плагіни кешу сторінок» раніше, бо PHP був повільний, а shared-хостинг дорогий. Записування статичного HTML на диск було тактикою виживання, а не естетичним вибором.
  3. Кошик WooCommerce керується cookie для гостей. Ось чому «неавторизований» не означає «безпечний для кешування».
  4. ESI (Edge Side Includes) був ранньою спробою кешувати змішані динамічні сторінки. Працює, але це операційно важко; більшість WordPress-стеків уникають цього, поки не буде серйозної платформи.
  5. Varnish здобув популярність, бо зробив HTTP-кешування першокласним реверс-проксі. Його мова VCL потужна, і також хороший спосіб створити важкодіагностовані правила обходу.
  6. Проблеми «каскаду» кешу (cache stampedes) визнали як проблему надійності задовго до WordPress. Техніки як request coalescing і «stale-while-revalidate» існують, бо трафік-шпиці карають origin-сервери.
  7. Браузери ставали суворішими щодо cookie з часом. За замовчуванням зміни SameSite змінили поведінку, що може вплинути на взаємодію сесійних cookie з шарами кешу.
  8. Багато CDN за замовчуванням кешують «все» лише якщо ви цього явно попросите. Коли люди вмикають це для HTML, вони часто забувають про логіку обходу для кошика й checkout, а потім звинувачують WooCommerce.

Як кешування ламає кошики й форми: реальні сценарії відмов

1) Кешований HTML протікає персональні дані сесії

Класика: віджет кошика показує товари іншого користувача, або заголовок вітає «Привіт, Олександре» чужій людині. Це відбувається, коли ключі full-page кешу не варіюються за потрібними cookie і ви кешуєте відповідь, яка містила персоналізовані фрагменти.

Навіть якщо основна сторінка кошика виключена, endpoint фрагмента кошика або міні-кошик у хедері можуть бути некоректно закешовані плагіном, CDN або надто неточним правилом реверс-проксі.

2) Суми на Checkout застарівають

Податки й доставка можуть змінюватися залежно від адреси, країни або навіть поштового індексу. Якщо ви кешуєте HTML checkout або XHR-відповіді, які використовуються для обчислення сум, ви можете віддати неправильні підсумки. Краще: платіжний шлюз відмовляє. Гірше: ви недоплатили і проводите вихідні, вивчаючи бухгалтерію складним шляхом.

3) Форми не працюють через прострочені або спільні nonce

Nonce WordPress — це токени з обмеженим часом життя. Багато плагінів форм вставляють їх у HTML. Якщо кешувати HTML занадто довго, nonce прострочуються і дають помилки, що виглядають як випадкові «Security check failed». Якщо кешувати неправильно між користувачами — можна створити дивну поведінку повтору, коли сабміти йдуть не у ту логіку станів.

4) Сторінки авторизованих кешуються як анонімні (або навпаки)

Якщо ваш шар кешу ігнорує cookie типу wordpress_logged_in_* або власні auth-cookie, ви можете публічно закешувати сторінки авторизованих. Або ж ви можете обходити кеш для всіх, бо налаштували занадто широкий cookie як сигнал обходу, наприклад «consent=true». Обидва варіанти погані. Один — інцидент безпеки; інший — інцидент бюджету.

5) «Оптимізації» воюють між собою: плагін кешу vs проксі vs CDN

Декілька кешів можуть працювати разом, але тільки якщо ви вирішили, хто авторитетний для HTML. Коли плагін сторінкового кешу встановлює заголовки одним способом, NGINX їх перевизначає, а CDN ігнорує — відладка перетворюється на інтерпретативний танець.

6) Шторми при очищенні та «гуркіт стада»

Очищення всього кешу при кожному оновленні продукту — поширений за замовчуванням підхід. На завантажених магазинах це стає самонанесеним відмовою в обслуговуванні: кеш «холодний», origin смикається, черга PHP-FPM зростає, потім timeouts, потім повтори, потім ще навантаження.

Є корисна ідея від Werner Vogels (CTO Amazon), варта запису на стікері: будуйте системи, припускаючи, що щось зламається, і проектуйте для відновлення замість досконалості.

Швидка діагностика: знайдіть вузьке місце й баг швидко

Це план дій, коли хтось каже «кошик неправильний» або «форми не відправляються» і вам потрібно швидко отримати сигнал.

Спочатку: підтвердьте, чи віддається кешований HTML

  1. Перевірте заголовки відповіді на наявність попадань/промахів кешу і Cache-Control.
  2. Порівняйте анонімний запит і з cookie кошика (або cookie для авторизації), щоб побачити, чи варіює кеш правильно.
  3. Перевірте CDN проти origin: чи віддає edge кешовану відповідь, коли origin каже no-store?

Друге: ізолюйте шар, який кешує

  1. Обійдіть CDN (підключіться до origin-хоста або внутрішньої IP) і протестуйте знову.
  2. Обійдіть реверс-проксі (потрапте безпосередньо до PHP upstream, якщо можливо) і протестуйте знову.
  3. Тимчасово вимкніть плагін кешу сторінок (або увімкніть «development mode», якщо плагін це підтримує) і протестуйте знову.

Третє: перевірте специфічні ендпоїнти WooCommerce і форм

  1. Переконайтеся, що /cart/, /checkout/ та endpoints фрагментів WooCommerce не кешуються.
  2. Переконайтеся, що POST-запити ніколи не кешуються (вони не повинні, але не довіряйте налаштуванням за замовчуванням).
  3. Для форм перевірте свіжість nonce і що сторінка з nonce не кешується понад його TTL.

Четверте: перевірте навантаження бекенда

  1. Якщо ви бачите багато пропусків кешу, переконайтеся, що origin витримає навантаження пропусків: черга PHP-FPM, латентність БД, стан Redis.
  2. Якщо ви бачите попадання в кеш, але неправильний контент — зосередьтеся на варіаціях ключа кешу й правилах обходу, а не на «більше заліза».

Практичні завдання: команди, очікуваний результат і рішення

Ці завдання розраховані на типовий Linux-хост з NGINX + PHP-FPM, за потреби Varnish і Redis, спереду CDN. Налаштуйте шляхи та імена сервісів під свій стек. Кожне завдання містить (a) команду, (b) що означає вивід, і (c) рішення, яке потрібно прийняти.

Завдання 1: Перевірити заголовки кешу для публічної сторінки

cr0x@server:~$ curl -sI https://store.example.com/ | egrep -i 'cache-control|age|expires|etag|x-cache|via|cf-cache-status|server'
server: nginx
cache-control: public, max-age=600
etag: "a1b2c3"
x-cache: HIT
age: 87
via: 1.1 varnish

Що це означає: Ви бачите явне кешування (public, max-age=600) і потрапляння в кеш реверс-проксі (x-cache: HIT) з наростанням Age. Добре для головної сторінки, якщо вона безпечна для анонімів.

Рішення: Якщо це маркетингова сторінка — залиште як є. Якщо на головній є персональні підсумки кошика або «недавно переглянуті» для гостей — потрібно винести ці віджети на клієнт або обходити кеш для користувачів з відповідними cookie.

Завдання 2: Перевірити заголовки кешу для сторінок кошика й чекауту

cr0x@server:~$ curl -sI https://store.example.com/cart/ | egrep -i 'cache-control|age|x-cache|cf-cache-status|set-cookie'
cache-control: no-store, no-cache, must-revalidate, max-age=0
x-cache: MISS
set-cookie: woocommerce_items_in_cart=1; path=/; secure; HttpOnly

Що це означає: Кошик правильно позначений як некешований і має пропуск кешу (що і потрібно). Cookie вказує на стан сесії/кошика.

Рішення: Якщо ви бачите public або HIT тут — вважайте це бойовою помилкою: додайте правила обходу для цих шляхів на всіх шарах кешу.

Завдання 3: Перевірити варіацію при наявності cookie

cr0x@server:~$ curl -sI https://store.example.com/ -H 'Cookie: woocommerce_items_in_cart=1; wp_woocommerce_session_123=abc' | egrep -i 'cache-control|x-cache|age|vary'
cache-control: private, no-store, max-age=0
x-cache: BYPASS
vary: Accept-Encoding

Що це означає: При наявності cookie кошика кеш обходять і відповідь приватна/no-store. Це розумна базова поведінка.

Рішення: Якщо досі x-cache: HIT, ваш ключ кешу ігнорує cookie або логіка обходу не спрацьовує. Виправте це перш ніж «оптимізувати» щось інше.

Завдання 4: Підтвердити поведінку CDN проти origin (підключитися безпосередньо до origin)

cr0x@server:~$ curl -sI https://origin.store.example.com/checkout/ | egrep -i 'cache-control|x-cache|age|server'
server: nginx
cache-control: no-store, no-cache, must-revalidate, max-age=0
x-cache: MISS

Що це означає: Origin не кешує checkout. Якщо публічний хост все ще повертає кешований checkout, винен CDN.

Рішення: Додайте правила обходу CDN для шляхів checkout/cart/account і відповідних cookie. Не покладайтеся на «поважати заголовки origin», поки не перевірили це на практиці.

Завдання 5: Знайти активний плагін кешу (WordPress CLI)

cr0x@server:~$ cd /var/www/store
cr0x@server:~$ sudo -u www-data wp plugin list --status=active
+--------------------------+--------+-----------+---------+
| name                     | status | update    | version |
+--------------------------+--------+-----------+---------+
| woocommerce              | active | available | 8.6.1   |
| redis-cache              | active | none      | 2.5.3   |
| wp-super-cache           | active | none      | 1.9.4   |
+--------------------------+--------+-----------+---------+

Що це означає: У вас активний плагін кешу сторінок (wp-super-cache) і плагін object cache (redis-cache).

Рішення: Визначте, хто відповідає за full-page кеш: плагін чи реверс-проксі. Можливо одночасне використання обох працює, але зазвичай створює питання «чому це кешується?». Виберіть одного і налаштуйте іншого так, щоб він працював у своїй зоні відповідальності.

Завдання 6: Перевірити, чи дійсно використовується Redis object cache

cr0x@server:~$ sudo -u www-data wp redis status
Status: Connected
Client: PhpRedis (v5.3.7)
Redis: 7.0.15
Drop-in: Valid

Що це означає: Object cache працює. Це знижує навантаження на базу даних і може дозволити вам менше агресивно підходити до HTML-кешування для авторизованих потоків.

Рішення: Тримайте object cache ввімкненим. Якщо object cache відключений, виправте це перед тим, як зменшувати TTL для HTML; інакше ви повернете навантаження на MySQL.

Завдання 7: Перевірити присутність cookie сесії WooCommerce під час перегляду

cr0x@server:~$ curl -sI https://store.example.com/product/hoodie/ | egrep -i 'set-cookie|cache-control|x-cache'
cache-control: public, max-age=600
x-cache: HIT

Що це означає: Сторінка товару кешована і не встановлює cookie кошика при перегляді. Це ідеально.

Рішення: Якщо сторінки товарів встановлюють wp_woocommerce_session_* для всіх — ваш рейтинг попадань кешу впаде. Дослідіть плагіни, які ініціюють сесію на кожному перегляді.

Завдання 8: Перевірити конфігурацію NGINX fastcgi_cache і логіку обходу

cr0x@server:~$ sudo nginx -T 2>/dev/null | egrep -n 'fastcgi_cache|fastcgi_cache_bypass|fastcgi_no_cache|cache_key|set \$skip_cache' | head -n 40
123:    fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=WORDPRESS:100m inactive=60m max_size=5g;
181:    set $skip_cache 0;
186:    if ($request_method = POST) { set $skip_cache 1; }
190:    if ($request_uri ~* "/(cart|checkout|my-account)/") { set $skip_cache 1; }
194:    if ($http_cookie ~* "woocommerce_items_in_cart|wp_woocommerce_session_|wordpress_logged_in_") { set $skip_cache 1; }
221:    fastcgi_cache_bypass $skip_cache;
222:    fastcgi_no_cache $skip_cache;
223:    fastcgi_cache WORDPRESS;
224:    fastcgi_cache_key "$scheme$request_method$host$request_uri";

Що це означає: На цьому хості використовується NGINX fastcgi_cache. Є явні правила обходу для POST, ключових шляхів WooCommerce і важливих cookie.

Рішення: Переконайтеся, що ключ кешу включає query string, коли це має значення (далі буде). Переконайтеся, що ви обходите кеш за потрібними cookie для вашого сайту і уникайте обходу через нешкідливі cookie, як аналітика чи згода, якщо це непотрібно.

Завдання 9: Перевірити, чи query string неправильно звужує кеш-записи

cr0x@server:~$ curl -sI "https://store.example.com/?utm_source=test1" | egrep -i 'x-cache|age'
x-cache: HIT
age: 140

Що це означає: Закешована сторінка не варіювалася за query string, що зазвичай добре для параметрів маркетингу. Але це може бути погано, якщо ваш сайт використовує функціональні query-параметри (фільтри, валюта, мова).

Рішення: Видаляйте відомі маркетингові параметри на краю, але варіюйте кеш для функціональних параметрів (наприклад, ?currency=, ?lang=, фільтри). Не вгадуйте — інвентаризуйте, які параметри використовує ваша тема й плагіни.

Завдання 10: Перевірити поведінку Varnish і які cookie викликають pass

cr0x@server:~$ curl -sI https://store.example.com/ -H 'Cookie: wordpress_logged_in_abc=1' | egrep -i 'x-cache|via|set-cookie|cache-control'
via: 1.1 varnish
x-cache: MISS
cache-control: private, no-store, max-age=0

Що це означає: Cookie для авторизації викликає промах/обхід і приватну відповідь.

Рішення: Підтвердьте, що Varnish не кешує сторінки авторизованих. Якщо ви бачите HIT тут — зупиніться і виправте VCL; ви можете просочувати приватний контент.

Завдання 11: Перевірити навантаження PHP-FPM під час промахів кешу

cr0x@server:~$ sudo ss -lntp | egrep 'php-fpm|:9000'
LISTEN 0      4096         127.0.0.1:9000       0.0.0.0:*    users:(("php-fpm8.2",pid=1211,fd=9))

Що це означає: PHP-FPM слухає локально. Це нормально.

Рішення: Далі перевірте статус пулу й беклог. Якщо backlog зростає під час трафіку — ваші правила обходу кешу можуть бути занадто широкі або TTL занадто короткий.

Завдання 12: Подивитися статус пулу PHP-FPM (якщо ввімкнено)

cr0x@server:~$ curl -s http://127.0.0.1/php-fpm-status | egrep -i 'listen queue|idle processes|active processes|max children reached'
listen queue:         0
idle processes:       12
active processes:     3
max children reached: 0

Що це означає: Немає черги, багато вільних воркерів. Origin здоровий.

Рішення: Якщо listen queue росте і max children reached інкрементується — у вас проблема з потужністю origin. Збільшіть ресурси FPM, зменште частоту промахів або і те, й інше.

Завдання 13: Швидка перевірка латентності MySQL

cr0x@server:~$ mysqladmin -uroot -p ping; mysqladmin -uroot -p status
mysqld is alive
Uptime: 183204  Threads: 42  Questions: 23801984  Slow queries: 17  Opens: 231  Flush tables: 1  Open tables: 1024  Queries per second avg: 129.9

Що це означає: MySQL доступний; повільні запити є, але не очевидно, що вони різко зростають на основі цього знімка.

Рішення: Якщо повільні запити підскакують під час очищень кешу — потрібно зменшити область очищення, додати object cache або оптимізувати індекси/запити БД. Не «виправляйте» це кешуванням checkout.

Завдання 14: Перевірити, що POST не кешується посередником

cr0x@server:~$ curl -s -o /dev/null -D - -X POST https://store.example.com/wp-admin/admin-ajax.php | egrep -i 'cache-control|x-cache|status|via'
HTTP/2 400
cache-control: no-store, no-cache, must-revalidate, max-age=0
via: 1.1 varnish
x-cache: MISS

Що це означає: POST-відповідь не кешується (і ви отримали 400 через відсутність payload). Це ок для цього тесту.

Рішення: Якщо ви бачите HIT на POST — у вас серйозна помилка проксі/CDN. Виправляйте негайно; кешування POST ламає не лише кошики — воно ламає реальність.

Завдання 15: Перевірити WordPress cron і фоновые задачі (навантаження від очищень кешу)

cr0x@server:~$ sudo -u www-data wp cron event list --fields=hook,next_run,recurrence | head
+------------------------------+---------------------+------------+
| hook                         | next_run            | recurrence |
+------------------------------+---------------------+------------+
| wp_version_check             | 2025-12-27 03:12:00 | twice_daily|
| woocommerce_cleanup_sessions | 2025-12-27 02:45:00 | daily      |
| wp_scheduled_delete          | 2025-12-27 02:10:00 | daily      |
+------------------------------+---------------------+------------+

Що це означає: Очистка сесій WooCommerce і інші cron-задачі запускаються регулярно. Деякі плагіни кешу підключаються до cron для purge/preload.

Рішення: Якщо ви бачите надто часті завдання purge/preload, обмежте їх. Прелоад може перетворитися на самонаведний краулер, що краде ресурси у реальних користувачів.

Шаблони конфігурації, які працюють: плагін, сервер, CDN і cookie

Визначте, хто кешує HTML

Виберіть один основний HTML-кеш. Мій упереджений дефолт для продакшену:

  • Реверс-проксі кеш (NGINX fastcgi_cache або Varnish) для анонімного HTML.
  • CDN для статичних ресурсів, опційно анонімного HTML, якщо у вас дисципліновані правила обходу.
  • Object cache (Redis) завжди, для анонімних і авторизованих користувачів.
  • Плагін кешу сторінок WordPress лише якщо ви не контролюєте серверний шар. Якщо контролюєте — тримайте плагіни мінімальними.

Чим більше місць, де випадково можна закешувати сторінку checkout, тим більше шансів, що ви рано чи пізно це зробите.

Обхід на основі cookie: серце безпеки WooCommerce

WooCommerce ставить cookie, що сигналізують про стан кошика/сесії. Імена різняться, але зазвичай ви побачите:

  • woocommerce_items_in_cart
  • woocommerce_cart_hash
  • wp_woocommerce_session_*
  • wordpress_logged_in_* (для автентифікації WordPress)

На рівні реверс-проксі або NGINX правило обходу має зазвичай спрацьовувати, коли присутній будь-який із цих cookie. Це дозволяє обмежити HTML-кеш тільки для справді анонімного перегляду.

Виключення за шляхом: все ще необхідні

Навіть із обходом за cookie явно виключайте чутливі шляхи. Бо люди тестуватимуть checkout без кошика, без cookie, і скажуть, що це «безпечно». Потім реальний покупець з’явиться зі станом, і кеш зробить щось креативне.

Поширені виключення:

  • /cart/, /checkout/, /my-account/
  • /?wc-ajax=* і AJAX-ендпоїнти WooCommerce
  • /wp-admin/, /wp-login.php
  • Ендпоїнти форм, особливо якщо використовується admin-ajax або REST-маршрути для сабмітів

Тримайте «vary» навмисним (і компактним)

Якщо ви варіюєте надто багато, ви руйнуєте показник попадань у кеш і потім дивуєтеся, чому продуктивність не покращується. Якщо варіюєте замало — пророкуєте витоки даних і проблеми у потоках.

Добрі причини для варіації:

  • Accept-Encoding (обробляється автоматично)
  • Країна, якщо сайт змінює податки/доставку/юридичний контент
  • Валюта, якщо ціни змінюються
  • Мова, якщо контент змінюється

Погані причини для варіації:

  • Випадкові маректингові cookie
  • Стан згоди (якщо він не змінює HTML суттєво)
  • User agent (якщо ви не віддаєте зовсім різний HTML)

Використовуйте стратегії «stale», щоб уникнути stampedes

Якщо ваш реверс-проксі це підтримує, дозволяйте віддавати застарілий контент для анонімних сторінок під час повторної валідації у фоновому режимі. Користувач отримує швидку відповідь; origin не страждає від напливу запитів при закінченні TTL популярної сторінки.

Але не застосовуйте стале для транзакційних endpoint-ів. Ніхто не хоче застарілий checkout.

Форми: ставтеся до nonce як до молока, а не меду

Коли форма містить nonce або CSRF-токен у HTML, у вас є три життєздатні варіанти:

  1. Не кешувати сторінку з формою (просто, безпечно, може коштувати продуктивності).
  2. Кешувати сторінку, але рендерити nonce динамічно через AJAX або edge-side includes (складніше, масштабованіше).
  3. Короткий TTL для сторінки форми і приймати, що деякі довго відкриті вкладки зламляться (бізнес-рішення; логувати і моніторити помилки).

Короткий жарт №2: Закешований nonce — як фотокопія картки-ключа в готелі: технічно ключ, фактично скарга.

CDN: кешуйте HTML лише коли можете обходити точно

CDN відмінні для статичних ресурсів. Для HTML дійте лише якщо:

  • Ви можете обходити за cookie і шляхами.
  • Ви можете чистити вибірково (за URL або тегом), а не «очищати все».
  • Ви бачите відлагоджувальні заголовки (статус кешу, ключ кешу або варіації) під час інцидентів.

Якщо правило CDN — «cache everything» з TTL 1 година і примарним обходом — рано чи пізно ви закешуєте те, що не мали на увазі. Це не цинізм. Це час.

Object cache — не заміна page cache (і це добре)

Redis object cache прискорює виконання WordPress без видачі HTML одного користувача іншому. Це зазвичай найменш ризиковий приріст продуктивності на WooCommerce-сайтах, бо не скорочує логіку запит/відповідь на HTTP-шарі.

Використовуйте його, щоб зменшити спокусу кешувати динамічний HTML.

Три корпоративні історії з кешувальних фронтів

Міні-історія 1: Інцидент через хибне припущення

Компанія: рітейлер середнього ринку з «модернізованим» стеком WordPress. Новий реверс-проксі кеш попереду PHP-FPM. На демонстрації спринту головна сторінка вантажилась менше ніж за 100 мс. Усі аплодували. Хтось сказав «практично вирішили проблему продуктивності».

Хибне припущення було простим: «Якщо користувач не залогінений, відповідь безпечна для кешування». Це речення звучить як інженерне рішення, поки ви не згадаєте про WooCommerce.

В понеділок служба підтримки повідомила, що покупці бачать «товари вже в вашому кошику» при першому візиті. Через декілька скріншерів стало гірше: міні-кошик показував продукти з чужих сесій. Не платіжні дані і не адреси — але все одно витік приватності і втрата довіри.

Дебаг показав, що реверс-проксі ігнорував cookie WooCommerce. Гості були «анонімними», але їхній стан кошика відстежувався через wp_woocommerce_session_*. Ключ кешу був лише host + uri. Перший, хто додавав товар, фактично «отруював» кешований HTML для всіх наступних відвідувачів.

Фікс був банальний: обходити кеш, якщо присутні cookie сесії/кошика WooCommerce, і явно виключити шляхи cart/checkout. Важливіше було наступне: вони написали односторінковий чек-лист «що робить відповідь специфічною для користувача» і зробили його частиною огляду змін, що стосуються кешування.

Міні-історія 2: Оптимізація, що вистрілила собі в ногу

Компанія: сервіс за підпискою з великим контент-маркетингом і WooCommerce checkout для додаткових товарів. Вони хотіли підвищити показник попадань кешу на CDN, тому вирішили «нормалізувати» cookie: обрізати більшість cookie на краю, щоб більше запитів ставали кешованими.

Ідея звучала розумно. Але була неповною. Одна з обрізаних cookie відповідала за вибір валюти, встановлену плагіном. Інша — за вибір країни для відображення податків. Вони не варіювали кеш по країні, бо «CDN-geo заголовок має це вирішувати», але не підключили його до ключа кешу. І CDN, як машина, зробив саме те, що йому наказали.

Результат: канадські відвідувачі бачили ціни в доларах США на сторінках продуктів. Дехто доходив до checkout і бачив, як суми змінюються. Інші бачили повідомлення «податок включено», що не відповідало дійсності. Конверсія впала, тикети в підтримку зросли. Інцидент класифікували як «регресія продуктивності», корпоративною мовою — «ми поламали гроші, ганяючись за мілісекундами».

План відновлення був дисциплінований: вони відкотили обрізку cookie, потім поетапно повернули її з allowlist-ами. Задокументували, які query-параметри і cookie є функціональними і мають залишатися у варіації кешу. Додали автоматичні перевірки: скрипт curl, що порівнює заголовки та кілька репрезентативних сторінок з і без індикаторів валюти/країни.

Зрештою їм вдалося налаштувати CDN HTML-кешування для анонімних маркетингових сторінок лише, з жорстким обходом для всього commerce-орієнтованого. Показник попадань був нижчий, ніж первісна мрія. Дохід перестав поводитися дивно. Справедливий обмін.

Міні-історія 3: Нудна, але правильна практика, що врятувала день

Компанія: B2B SaaS, що використовує WordPress для маркетингу та документації, з формами, прив’язаними до CRM. У них був реверс-проксі кеш і CDN, і вони планували кампанію зі значним піком трафіку.

Замість «підвищити кешування» вони зробили нудну річ: записали всі URL-шаблони та ендпоїнти, які ніколи не повинні кешуватися, потім перевірили це в staging з production-подібними заголовками. Також вони створили стандарт debug-заголовків кешу: origin додавав X-Cache-Bypass-Reason, коли обходив кеш, а реверс-проксі — X-Cache: HIT/MISS/BYPASS.

Під час кампанії продуктивність була в нормі — аж поки форми не почали падати для частини користувачів. Швидка реакція могла б звинуватити плагін форми або CRM. Натомість вони слідували власному плейбуку: перевірили заголовки на сторінці форми. Вона кешувалась 30 хвилин на CDN через невідповідний набір правил, що було розгорнуто напередодні.

Оскільки у них були послідовні debug-заголовки, за кілька хвилин було очевидно, який шар винен. Вони поправили правило CDN, щоб обходити кеш для сторінок форм і будь-яких відповідей, що встановлюють певні cookie. Помилки припинилися. Кампанія продовжилася. Нікому не довелося придумувати історії для керівництва про «періодичні проблеми з третіми сторонами».

Вони не отримали оплесків за стандарт заголовків. Вони отримали краще: тишу в каналі інцидентів.

Поширені помилки: симптом → корінна причина → виправлення

1) Кошик показує товари іншого користувача

Симптом: Міні-кошик або сторінка кошика показує несподівані товари; користувачі повідомляють про «привидів у кошику».

Корінна причина: Full-page кеш не обходить за cookie сесії/кошика WooCommerce або кешує endpoints фрагментів.

Виправлення: Обходьте кеш за woocommerce_items_in_cart, woocommerce_cart_hash, wp_woocommerce_session_*. Виключайте /cart/, /checkout/, /?wc-ajax=*. Переконайтеся, що endpoints фрагментів не кешуються на CDN чи проксі.

2) Суми на checkout несподівано змінюються або неправильні

Симптом: Доставка/податки змінюються між кроками, або суми не співпадають з платіжним шлюзом.

Корінна причина: Кешований checkout HTML або кешовані AJAX-відповіді, що використовуються для обчислення сум; відсутня варіація по країні/валюті.

Виправлення: Ніколи не кешуйте checkout HTML. Обходьте кеш для вибору країни/валюти. Для CDN вимкніть кешування WooCommerce AJAX і checkout endpoint-ів повністю.

3) «Security check failed» при відправці форм

Симптом: Помилки валідації nonce, періодичні збої форм, особливо після того, як сторінка довго відкрита.

Корінна причина: Закешована сторінка містить прострочений nonce; TTL занадто довгий; кеш спільний між користувачами.

Виправлення: Виключити сторінки форм з кешу або генерувати nonce динамічно. Скоротити TTL лише якщо ви приймаєте збої у довго відкритих вкладках; моніторити кількість помилок.

4) Авторизовані користувачі бачать кешовані анонімні сторінки (або навпаки)

Симптом: Панель адміністратора відсутня, сторінка акаунта виглядає як для незалогіненого, або анонімні бачать приватний контент.

Корінна причина: Кеш ігнорує wordpress_logged_in_* cookie; заголовок Authorization ігнорується; ключ кешу непослідовний.

Виправлення: Обходьте кеш за cookie автентифікації WordPress і заголовком Authorization. Перевіряйте через curl з cookie. Якщо вам потрібно кешувати сторінки для залогінених — реалізуйте варіацію на користувача явно (рідко; ретельно тестуйте).

5) Падіння показника попадань кешу після додавання аналітики/інструментів згоди

Симптом: Раптом усе стає MISS у кеші; навантаження на origin зростає.

Корінна причина: Обхід кешу спрацьовує на «будь-який cookie» або на широкий regex, що ловить cookie згоди/аналітики.

Виправлення: Перейдіть від «обхід, якщо є будь-який cookie» до моделі allowlist/denylist: обходьте тільки на функціональні cookie (кошик/сесія/автентифікація). Видаляйте неважливі cookie з розгляду кешу, коли це безпечно.

6) Очищення кешу спричиняє відмови або таймаути

Симптом: Сайт сповільнюється після оновлень контенту; зростають 5xx; CPU бази даних піднімається.

Корінна причина: Стратегія purge-all; занадто агресивний preload-краулер; stampede при завершенні TTL.

Виправлення: Очищуйте вибірково; впровадьте stale-while-revalidate де можливо; обмежте частоту прелоаду; переконайтеся, що origin має потужність для пропусків.

7) Сторінки продуктів показують неправильну мову або валюту

Симптом: Відвідувачі бачать мову/валюту, що не відповідає їхньому вибору.

Корінна причина: CDN/проксі кешують HTML без варіації за cookie або заголовком валюти/мови; query-параметри неправильно відкидаються.

Виправлення: Варіюйте ключ кешу за функціональним селектором (cookie/заголовок/query-param). Або обходьте кеш для сторінок, де цього не можна зробити чисто.

8) «Працює, коли я обходжу кеш»

Симптом: Баг зникає при вимкненому кеші; повертається при включеному.

Корінна причина: Кеш маскує станну залежність (nonce/сесія), або кешує відповідь з помилкою.

Виправлення: Запобігайте кешуванню статусів помилок; обходьте кеш за станними cookie; посильте Cache-Control для чутливих сторінок; перевіряйте шари послідовно за заголовками.

Чек-листи / покроковий план для безпечного кешування

Крок 1: Інвентаризуйте, що має бути динамічним

  • Перерахуйте транзакційні URL: cart, checkout, account, login, admin.
  • Перерахуйте сторінки форм і ендпоїнти сабмітів (admin-ajax, REST-маршрути).
  • Перерахуйте персоналізаційні елементи на анонімних сторінках (недавно переглянуті, гео-ціни, перемикачі валюти/мови).

Крок 2: Оберіть авторитет кешування

  • Якщо ви контролюєте NGINX/Varnish: використовуйте їх для HTML-кешу і вимкніть плагіни кешу сторінок (або налаштуйте їх лише для browser cache/оптимізації статичних ресурсів).
  • Якщо у вас обмежений хостинг: використовуйте надійний плагін кешу і тримайте CDN HTML-кешування консервативним.

Крок 3: Впровадьте жорсткі виключення

  • Виключіть /wp-admin/, /wp-login.php.
  • Виключіть /cart/, /checkout/, /my-account/.
  • Виключіть AJAX-ендпоїнти WooCommerce (wc-ajax) і фрагменти.
  • Виключіть сторінки форм і ендпоїнти з nonce, якщо не можете рендерити їх динамічно.

Крок 4: Впровадьте обходи за cookie

  • Обходьте кеш за wordpress_logged_in_* та будь-якими вашими auth/session cookie.
  • Обходьте кеш за cookie WooCommerce: woocommerce_items_in_cart, woocommerce_cart_hash, wp_woocommerce_session_*.
  • Не обходьте на «будь-який cookie». Використовуйте цілеспрямоване зіставлення.

Крок 5: Визначте ключі варіації для мови/валюти/гео

  • Якщо валюта/мова змінюють HTML — варіюйте ключ кешу або обходьте.
  • Видаляйте маркетингові query-параметри з ключа кешу; зберігайте функціональні.

Крок 6: Встановіть TTL як доросла людина

  • Маркетингові/блог-сторінки: 5–30 хвилин — типовий старт.
  • Сторінки продуктів: коротший TTL, якщо часто змінюються запаси/ціни; інакше помірний TTL з очищенням при оновленні.
  • Транзакційні сторінки: no-store/no-cache.

Крок 7: Впровадьте селективне очищення

  • Очищуйте тільки змінені URL при оновленні поста/продукту.
  • Уникайте purge-all, окрім деплоїв чи аварій.
  • Обмежуйте частоту запитів на очищення, щоб захистити кеші й origin.

Крок 8: Додайте спостережуваність кешування

  • Додавайте X-Cache і, бажано, X-Cache-Bypass-Reason на рівні проксі.
  • Відстежуйте співвідношення попадань у кеш, час відповіді origin і частоту 5xx.
  • Створіть канарковий синтетичний тест для cart/checkout, що працює кожні кілька хвилин і сповіщає, якщо ті сторінки стають кешованими.

Крок 9: Тестуйте з реальними cookie і потоками

  • Анонімний перегляд без cookie.
  • Анонімний з cookie кошика присутнім.
  • Залогінений користувач.
  • Варіації країни/валюти, якщо підтримуються.
  • Відправка форми зі сторінки, що стояла відкритою 30+ хвилин (сценарій прострочення nonce).

Питання й відповіді

1) Чи можна кешувати сторінки WooCommerce взагалі?

Ви можете кешувати деякі сторінки WooCommerce для анонімних користувачів: списки продуктів і сторінки товарів зазвичай безпечні. Не кешуйте cart, checkout, account або AJAX-ендпоїнти WooCommerce. Межа безпеки — стан сесії, а не просто «це сторінка магазину».

2) Чому мій показник попадань кешу падає майже до нуля після ввімкнення WooCommerce?

Зазвичай тому, що щось рано встановлює сесію/cookie кошика (на перегляді товару, головній або навіть на кожній сторінці). Як тільки cookie з’являється, логіка обходу може перестати кешувати для цього користувача. Знайдіть плагін/тему, що ініціює сесію, і припиніть це робити.

3) Чи треба обходити кеш, якщо є будь-який cookie?

Ні. Це швидкий шлях до перетворення кешу в декоративний конфігураційний файл. Обходьте тільки для функціональних cookie (автентифікація, кошик, сесія, валюта/мова, якщо не можна варіювати безпечно).

4) Мій CDN каже, що «поважає заголовки origin». Чому він кешує checkout?

Бо «поважає» має зноски. Може бути правило сторінки, що це перевизначає; або CDN обробляє деякі статуси по-іншому; або ви кешуєте за замовчуванням і забули шляхи. Перевіряйте порівняння заголовків origin vs edge і тестуйте з cookie.

5) Яка найнадійніша конфігурація кешу для WordPress + WooCommerce?

Консервативне full-page кешування лише для анонімних сторінок (реверс-проксі або плагін), жорсткий обхід для cart/checkout/account/auth і cookie WooCommerce, плюс Redis object cache. Статичні ресурси кешуйте агресивно на CDN/браузері.

6) Чи потрібен Varnish, якщо вже є плагін кешу WordPress?

Не обов’язково. Varnish може бути відмінним, але це ще один компонент. Якщо ви контролюєте сервер і хочете передбачуваної поведінки на великій нагрузці, Varnish або NGINX fastcgi_cache часто чистіші рішення, ніж плагін. Якщо сервер контролює хостинг, плагін може бути практичним варіантом.

7) Чому форми ламаються лише іноді?

Бо помилки кешування залежать від часу: вікно прострочення nonce, TTL кешу і те, чи потрапляє користувач на кешовану копію або на свіжу. Періодичні збої форм — сильний сигнал, що HTML з nonce кешується занадто довго.

8) Чи ризикований object caching (Redis) для WooCommerce?

Загалом, це менш ризиковано, ніж full-page кеш, бо не віддає HTML одного користувача іншому. Основні ризики — це операційні: доступність Redis, розмір пам’яті і політика вилучення. Моніторьте його як будь-який зовнішній сервіс.

9) Як визначити, який шар віддав кешовану відповідь?

За заголовками. Додайте їх, якщо їх нема. Заголовки статусу кешу CDN, X-Cache реверс-проксі і заголовки origin як Cache-Control підкажуть, куди дивитися. Якщо не можете це з’ясувати — не зможете швидко дебажити під тиском.

10) Чи можна безпечно кешувати сторінки для залогінених користувачів?

Можливо, але рідко варто для WordPress, якщо не реалізувати ключі кешу на користувача і не прийняти складність. Для більшості сайтів краще інвестувати в object cache, оптимальні запити і налаштування PHP-FPM. Залишайте HTML для залогінених динамічним.

Наступні кроки, які можна зробити цього тижня

Якщо ваші кошики або форми ламаються, не починайте з зміни TTL. Почніть з доведення, який шар віддає кешований контент і чи варіює він за правильним станом.

  1. Додайте debug-заголовки кешу на реверс-проксі (HIT/MISS/BYPASS і причина обходу). Ви подякуєте собі пізніше.
  2. Впровадьте жорсткі виключення для cart/checkout/account/login/admin і AJAX-ендпоїнтів WooCommerce на кожному шарі кешу, яким ви керуєте.
  3. Впровадьте обходи за cookie для cookie сесії WooCommerce і cookie авторизації WordPress.
  4. Аудит cookie і query-параметрів для валюти/мови/гео і вирішіть: варіювати, обходити або перепроєктувати.
  5. Розгорніть Redis object cache (якщо ще ні) і підтвердіть, що він дійсно активний.
  6. Створіть синтетичний тест, який запитує /cart/ і /checkout/ і перевіряє, що відповіді мають Cache-Control: no-store і немає заголовків HIT кешу.
  7. Припиніть поведінку purge-all, якщо тільки це не надзвичайна ситуація. Очищуйте хірургічно і використовуйте стратегії stale для анонімних сторінок, якщо проксі це підтримує.

Кешування — це потужний інструмент. Ставтеся до нього відповідально. Носіть захисні окуляри: заголовки, правила обходу і тести, що працюють, коли ви не дивитеся.

← Попередня
Debian 13: APT ламається («незадоволені залежності») — виправте це без перевстановлення
Наступна →
Розріджені томи ZFS: пастка перевиділення та як її моніторити

Залишити коментар