WordPress повільний на мобільних: що оптимізувати насамперед

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

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

Коли WordPress повільний на мобільних, люди роблять хаотичні дії: ставлять три плагіни кешування, двічі стискають усе, звинувачують хостинг, тему або «Google». Так не треба. Ставтеся до цього як до виробничого усунення несправностей: вимірюйте, ізолюйте вузьке місце, виправляйте перше обмеження, повторюйте.

Швидкий план діагностики (як швидко знайти вузьке місце)

Якщо робити більше нічого, зробіть це. Повільність на мобільних зазвичай зумовлена трьома речами: повільною відповіддю сервера (TTFB), великим об’ємом даних (зображення/шрифти/JS/CSS) або хаосом від сторонніх сервісів (реклама, трекери, чат-віджети). Трюк — зрозуміти, яка з причин, за хвилини, а не тижні.

Крок 1: Визначте, це сервер чи браузер

  • Перевірте TTFB з холодного запиту. Якщо TTFB послідовно високий (> 600–800ms) у мобільних мережах, спочатку виправляйте серверну сторону: кешування, PHP-FPM, базу даних, виклики бекенду, затримку опорного сервера.
  • Якщо TTFB нормальний, але LCP поганий, ви в зоні фронтенду: зображення, CSS, що блокує рендер, зайвий JS, шрифти, зсуви макету.
  • Якщо TTFB іноді нормальний, а іноді жахливий, підозрюйте промахи кеша, конкуренцію на бекенді, cron або шумного сусіда (навантаження CPU/I/O).

Крок 2: Підтвердіть, що кешування справді працює

  • Якщо вам здається, що кеш є, але кожен запит потрапляє в PHP, то кешу немає. Є надія — і все.
  • Шукайте заголовки кешу, статус кешу на мережевому рівні і серверні логи, що показують швидкі/статичні відповіді.

Крок 3: Знайдіть найбільші байти на критичному шляху

  • На мобільних зображення й JS зазвичай головні підозрювані.
  • Перевірте елемент LCP: якщо це герой-зображення, виправляйте його перед тим, як сперечатися про індекси бази даних.
  • Вимикайте сторонні скрипти, що блокують головний потік або додають тривалі завдання. Більшість «маркетингових обов’язкових» скриптів є опційними, щойно ви покажете їхню вартість у продуктивності.

Крок 4: Підтвердіть одним метриком, потім рухайтеся далі

Виберіть один метрик успіху для ітерації: TTFB, LCP, INP (затримка взаємодії) або загальний обсяг переданих байтів. Не намагайтеся виправити все одразу; ви внесете купу змін і нічому не навчитеся.

Чому «повільно» на мобільних відрізняється (і чому це важливо)

Мобіль — це не просто «менший десктоп». Це інший двигун фізики.

  • CPU слабший, а браузери агресивно пригнічують роботу, щоб економити батарею.
  • Мережна затримка домінує. Навіть коли пропускна здатність виглядає пристойною, кругові поїздки коштують дорого. Кожен DNS-запит, TLS-рукостискання і перенаправлення погіршують ситуацію.
  • Тиск пам’яті реальний. Великі JavaScript-бандли й важкі теми можуть спричиняти GC браузера у найневдаліший момент.
  • Взаємодія — це дотик. На десктопі ви можете «пробачити» повільний рендер; на мобільному ви не пробачите затримки при торканні.

Також: ви не оптимізуєте для лабораторії. Ви оптимізуєте для заплутаного середнього користувача з 12 відкритими вкладками, нестабільним радіосигналом і телефоном, який бачив кращі часи.

Цитата, яку варто приклеїти на стікер: парафраз Джона Оллспоу: «Надійність приходить від того, як системи поводяться в реальних умовах, а не в ідеальних тестах.»

Жарт №1: Робота над продуктивністю нагадує дієту — ніхто не хоче рахувати калорії, але всі хочуть фото з пляжу.

Цікаві факти та історичний контекст

Трохи контексту допоможе припинити повторювати індустріальні хіти.

  1. WordPress з’явився в 2003 році як форк b2/cafelog, задовго до ери «mobile-first». Багато стандартних підходів народилися у десктопну епоху вебу.
  2. HTTP/2 (стандартизований у 2015) зменшив проблему багатьох маленьких запитів, але не зробив нестримну JavaScript або гігантські зображення дешевими.
  3. Google представив Core Web Vitals у 2020 році, щоб кількісно вимірювати досвід користувача, і це підняло продуктивність з «приємної» до «рядка бюджету».
  4. JPEG існує з початку 1990-х; сучасні формати як WebP (2010) та AVIF (поширеність з близько 2019) значно зменшують байти для фото на мобільних.
  5. Мобільний трафік перевищив десктоп давно у багатьох нішах; «ми переважно десктоп» часто базується на застарілому погляді на аналітику або внутрішніх упередженнях.
  6. CDN починали як прискорювачі статики, але еволюціонували в повноцінні edge-платформи з кешуванням, WAF, ресайзингом зображень та управлінням ботами — корисні для WordPress, але їх легко неправильно налаштувати.
  7. Патерн admin-ajax.php WordPress став звичним болем продуктивності, коли теми/плагіни покладалися на нього для «живих» функцій; багато сайтів випадково створювали собі DDoS.
  8. Ледаче завантаження стало з хаку в натив з атрибутом loading=”lazy”, але його все ще можна неправильно використовувати і затримати LCP, якщо ви ледаче завантажуєте герой-зображення.
  9. PHP 7 (2015) був стрибком у продуктивності; PHP 8+ додає покращення і JIT (переважно неважливий для типової WordPress), але великі виграші приходять від opcode-кешу та уникнення зайвої роботи.

Єдина потрібна ментальна модель: TTFB проти рендеру

Скарги на повільність на мобільних зазвичай зводяться до: «Я тапнув і він на мене дивиться». Це «поглядання» або сервер не надсилає байти (TTFB), або браузер задихається від отриманого (рендер/CPU).

Кошик A: Високий TTFB (сервер — вузьке місце)

Типові причини:

  • Немає кешу сторінки (або він обминається для мобільних / залогінених користувачів / рядка запиту).
  • Повільне виконання PHP через тяжкі плагіни, віддалені виклики або пропуски object-cache.
  • Затримка бази даних (погані запити, відсутні індекси, перевантажений MySQL, повільне сховище).
  • Origin далеко від користувачів і відсутній CDN.
  • Обмеження швидкості або WAF, що додають затримку для мобільних клієнтів.

Кошик B: Низький TTFB, але повільний paint (фронтенд — вузьке місце)

Типові причини:

  • Зображення LCP занадто велике, неоптимізоване, подається без адаптивних розмірів.
  • CSS, що блокує рендер, і затримки підвантаження шрифтів.
  • Занадто великі JavaScript-бандли, забагато сторонніх скриптів, довгі завдання.
  • Занадто багато DOM-вузлів від конструкторів сторінок і тем «зробити все».

Кошик C: Залежить (періодичне)

Якщо проблема приходить і йде, вважайте на шторм промахів кеша, періодичні cron-роботи, сплески трафіку або конкуренцію за ресурси. Періодичні проблеми з продуктивністю рідко вирішуються «зменшити JS», і майже завжди їх фіксують усуненням варіативності: кешування, ємність і ліміти.

Практичні завдання: команди, виходи, рішення (12+)

Це завдання на рівні виробництва. Кожне включає команду, що ви дивитесь, і яке рішення приймаєте далі. Виконуйте їх на вашому хості WordPress або на копії стейджингу. Якщо не можете виконувати команди (керований хостинг), попросіть провайдера виконати еквівалент або переїдьте туди, де ви можете спостерігати за системою самі.

Завдання 1: Виміряйте TTFB і загальний час від origin

cr0x@server:~$ curl -s -o /dev/null -w "dns:%{time_namelookup} connect:%{time_connect} tls:%{time_appconnect} ttfb:%{time_starttransfer} total:%{time_total} size:%{size_download}\n" https://example.com/
dns:0.012 connect:0.034 tls:0.081 ttfb:0.742 total:1.214 size:185432

Що це означає: ttfb — час до першого байта; total — завершення запиту; size — повернені байти.

Рішення: Якщо TTFB > ~0.6–0.8s послідовно, припиніть сперечатися про формати зображень і спочатку виправте кешування на сервері та латентність бекенду.

Завдання 2: Порівняйте попадання в кеш і промах за допомогою заголовків

cr0x@server:~$ curl -sI https://example.com/ | egrep -i "cache|age|cf-cache-status|x-cache|x-fastcgi-cache|server|vary"
server: nginx
vary: Accept-Encoding
cache-control: max-age=0, no-cache, no-store, must-revalidate
x-cache: MISS

Що це означає: Ця відповідь не дружня до кешування і показує промах (або відсутність кешу).

Рішення: Для публічних сторінок зазвичай потрібні кешовані заголовки та видимий статус кешу HIT на edge і/або origin. Якщо не вдається отримати HIT, мобільні постраждають першими.

Завдання 3: Підтвердіть, чи генерує WordPress сторінку (виклик PHP) або подає статичний HTML

cr0x@server:~$ tail -n 5 /var/log/nginx/access.log
203.0.113.10 - - [27/Dec/2025:10:11:22 +0000] "GET / HTTP/2.0" 200 185432 "-" "Mozilla/5.0" "upstream_response_time=0.690 request_time=1.201"
203.0.113.11 - - [27/Dec/2025:10:11:23 +0000] "GET /wp-content/uploads/2025/12/hero.jpg HTTP/2.0" 200 742193 "-" "Mozilla/5.0" "-"

Що це означає: Запит головної сторінки має upstream response time (~0.69s), що ймовірно означає звернення до PHP-FPM. Зображення подається напряму (без upstream).

Рішення: Якщо ваші публічні сторінки регулярно показують upstream times, впровадьте кеш повних сторінок (плагін кешу, Nginx fastcgi_cache, Varnish або CDN HTML кешування).

Завдання 4: Перевірте насичення PHP-FPM (повільні запити та черги)

cr0x@server:~$ sudo ss -lntp | grep php-fpm
LISTEN 0      511          127.0.0.1:9000      0.0.0.0:*    users:(("php-fpm8.2",pid=1123,fd=8))

Що це означає: PHP-FPM слухає. Цього замало; потрібно знати, чи зайняті воркери.

Рішення: Увімкніть сторінку статусу PHP-FPM і перевірте active/idle/queue. Якщо бачите чергу, ви обмежені CPU або недостатньо воркерів — кешування й зменшення коду частіше кращі за «більше воркерів».

Завдання 5: Перевірте, чи увімкнений і здоровий PHP opcache

cr0x@server:~$ php -i | egrep -i "opcache.enable|opcache.memory_consumption|opcache.interned_strings_buffer|opcache.max_accelerated_files"
opcache.enable => On => On
opcache.memory_consumption => 128 => 128
opcache.interned_strings_buffer => 16 => 16
opcache.max_accelerated_files => 10000 => 10000

Що це означає: Opcode-кеш увімкнений з помірними налаштуваннями.

Рішення: Якщо opcache вимкнено — увімкніть. Якщо пам’ять занадто мала, буде churn і втрата вигоди — збільшіть її. Це «нудна продуктивність», яка найкраща.

Завдання 6: Виявлення повільних MySQL-запитів

cr0x@server:~$ sudo tail -n 20 /var/log/mysql/mysql-slow.log
# Query_time: 2.314  Lock_time: 0.000 Rows_sent: 10  Rows_examined: 512340
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts
LEFT JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id)
WHERE wp_posts.post_type = 'product' AND wp_posts.post_status = 'publish'
ORDER BY wp_posts.post_date DESC LIMIT 0, 10;

Що це означає: Запит тривав 2.3 секунди і переглянув півмільйона рядків. Мобільний користувач відчуває це як «тап… чекаю… нічого».

Рішення: Видаліть/замініть патерн плагіна, що це викликає, додайте потрібні індекси (обережно) і покладайтесь на кеш сторінок, щоб це не виконувати на кожен перегляд.

Завдання 7: Подивіться, чи база даних обмежена I/O

cr0x@server:~$ iostat -xz 1 5
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          18.22    0.00    3.10   24.88    0.00   53.80

Device            r/s     w/s   rkB/s   wkB/s  await  aqu-sz  %util
nvme0n1         220.0   180.0  8200.0  5400.0  18.40    3.20  92.00

Що це означає: Використання диска високе, з немалими часами очікування; iowait підвищений. Сховище гаряче.

Рішення: Якщо MySQL на тій самій машині і ви бачите це, потрібні менше читань (кешування), кращі індекси, швидше сховище або розділення ролей. «Додати більше PHP-воркерів» не виправить насичений диск.

Завдання 8: Перевірте тиск на CPU і пам’ять у період повільної роботи

cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 2  1      0  31200  84200 612000    0    0   820   610  420  980 25  6 50 19  0
 3  2      0  29800  83500 605000    0    0  1120   900  470 1100 31  7 41 21  0

Що це означає: Черга виконання зросла, є заблоковані процеси, а iowait значущий (wa ~ 20%). Сторінка підкачки не використовується, отже це не свап.

Рішення: Це підтверджує I/O-обмеження або проблеми бази даних. Пріоритезуйте кешування та тонку настройку бази/сховища над фронтенд-змінами.

Завдання 9: Порахувати сторонні домени (латентна плата на мобільних)

cr0x@server:~$ curl -s https://example.com/ | grep -Eo 'https?://[^"]+' | awk -F/ '{print $3}' | sort -u | head
cdn.example.net
fonts.googleapis.com
fonts.gstatic.com
stats.vendor-a.com
tagmanager.vendor-b.com

Що це означає: Кожен сторонній домен додає DNS+TLS+запитну накладну і може блокувати рендер, якщо використовується неправильно.

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

Завдання 10: Знайдіть найважчі зображення і чи подаєте ви сучасні формати

cr0x@server:~$ find /var/www/html/wp-content/uploads -type f -name "*.jpg" -o -name "*.png" | xargs -r du -h | sort -hr | head
5.8M	/var/www/html/wp-content/uploads/2025/12/hero-homepage.jpg
4.1M	/var/www/html/wp-content/uploads/2025/12/header.png
3.9M	/var/www/html/wp-content/uploads/2025/11/banner-sale.jpg

Що це означає: Багатомегабайтні зображення поширені й жорстокі на мобільних, особливо як LCP-елементи.

Рішення: Конвертуйте герой-зображення у WebP/AVIF (де підтримано), змініть розміри на реальні розміри відрисовки й забезпечте адаптивний srcset. Якщо тема вимикає це, виправте тему або замініть її.

Завдання 11: Підтвердіть gzip/brotli стиснення для текстових ресурсів

cr0x@server:~$ curl -sI -H "Accept-Encoding: br,gzip" https://example.com/wp-content/themes/site/style.css | egrep -i "content-encoding|content-length|vary"
content-encoding: br
vary: Accept-Encoding

Що це означає: Brotli увімкнений. Добре.

Рішення: Якщо немає Content-Encoding, увімкніть gzip/brotli. Це невеликий ризик і значно допомагає мобільним для CSS/JS/HTML.

Завдання 12: Перевірте HTTP-кешованість статичних ресурсів

cr0x@server:~$ curl -sI https://example.com/wp-content/uploads/2025/12/hero-homepage.jpg | egrep -i "cache-control|expires|etag|last-modified"
cache-control: public, max-age=31536000, immutable
etag: "a1b2c3d4"

Що це означає: Чудово: довговічне кешування. Статичні ресурси мають кешуватися агресивно; використовуйте версіонування файлів для оновлень.

Рішення: Якщо бачите no-cache на зображеннях/CSS/JS — виправте правила сервера/CDN. Інакше кожен мобільний візит платить ту саму ціну за завантаження знову.

Завдання 13: Знайдіть навантаження від WordPress cron (непередбачена фонова робота)

cr0x@server:~$ wp cron event list --due-now --path=/var/www/html
+--------------------------+---------------------+---------------------+------------+
| hook                     | next_run_gmt        | next_run_relative   | recurrence |
+--------------------------+---------------------+---------------------+------------+
| wc_scheduled_sales       | 2025-12-27 10:12:00 | 1 minute ago        | hourly     |
| action_scheduler_run_queue | 2025-12-27 10:10:00 | 3 minutes ago       | every_minute |
+--------------------------+---------------------+---------------------+------------+

Що це означає: Заплановані події прострочені. Якщо WP-Cron запускається на запитах користувачів, це може непередбачувано сплескнути латентність.

Рішення: Вимкніть запуск WP-Cron під час завантаження сторінок і налаштуйте системний cron для періодичного виконання wp cron event run --due-now.

Завдання 14: Перевірте логи PHP на «продуктивність через винятки»

cr0x@server:~$ sudo tail -n 20 /var/log/php8.2-fpm.log
[27-Dec-2025 10:11:19] WARNING: [pool www] child 2245, script '/var/www/html/index.php' (request: "GET /") executing too slow (3.214 sec), logging
[27-Dec-2025 10:11:19] NOTICE: [pool www] child 2245 stopped for tracing, pid 2245

Що це означає: У вас повільне виконання PHP, а не лише повільні мережі. Щось у середовищі WordPress займає секунди.

Рішення: Додайте профілювання на рівні застосунку (на час), визначте плагін/функцію теми, що споживає час, і виправте або видаліть її. Не просто «збільшуйте таймаути».

Що оптимізувати в першу чергу (упорядковано, з думкою)

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

1) Отримайте реальний кеш сторінок для анонімних користувачів

Якщо ваш сайт подає одну й ту саму головну сторінку тисячам анонімних відвідувачів, але генерує її через PHP+MySQL щоразу, ви марнуєте CPU і додаєте латентність без причин. Кеш сторінок (на edge, реверс-проксі або плагін у WordPress) — найпотужніший важіль для зниження TTFB.

  • Edge HTML кешування найкраще, коли його можна безпечно застосувати (очищення при оновленнях, обхід для залогінених/кошик/checkout сторінок).
  • Nginx FastCGI cache відмінний, якщо ви контролюєте сервер.
  • Плагіни кешування краще ніж нічого, але плагіни можуть бути крихкими і непослідовними під навантаженням.

Правило: якщо ваші публічні сторінки не отримують cache HIT, виправте це перед тим, як торкатися мінімізації.

2) Виправте варіабельність TTFB: cron, обходи кешу та конкуренцію бекенду

Користувачі скаржаться не на середнє. Вони скаржаться на сплески. Мобіль підсилює сплески, бо мережа вже повільніша; додайте бекенд-життєвий шум — отримаєте «він зависає».

  • Перенесіть WP-Cron з запитів сторінок.
  • Зменшіть правила обходу кеша (рядки запиту, cookie, UTM-параметри).
  • Переконайтеся, що ключ кешу здоровий: мобіль/десктоп зазвичай повинні ділити HTML, якщо ви справді не подаєте різну розмітку.
  • Припиніть робити віддалені API-виклики в шляху запиту. Кешуйте їх або перемістіть у фонові задачі.

3) Оптимізуйте елемент LCP (зазвичай герой-зображення)

На мобільних сторінка «відчувається завантаженою», коли з’являється основний контент. Це LCP. Якщо ваш LCP — 5MB JPEG, зменшений CSS, ваші Core Web Vitals виглядатимуть як запис викупу.

  • Зменшіть розмір до фактичного відображуваного розміру (плюс трохи для retina).
  • Подавайте WebP/AVIF з fallback для JPEG.
  • Не ледаче завантажуйте LCP-зображення.
  • Preload його лише за необхідності, але правильно (уникайте preload десктоп-активів для мобільних).

4) Видаліть сторонні скрипти, поки сайт не почне поводитися

Тут починаються політичні питання. Сторонні скрипти — це борг продуктивності, який ви платите повсюди, у кожній географії, на кожному пристрої.

  • Видаліть те, що можна. Замініть те, що потрібно. Самохостіть, де це розумно.
  • Відкладіть некритичні скрипти. Переконайтеся, що вони не блокують рендер.
  • Слідкуйте за «розростанням менеджера тегів»: один контейнер стає п’ятьма вендорами, потім двадцятьма тегами, потім сум.

5) Зменшіть JavaScript і складність DOM (теми/конструктори сторінок)

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

  • Вимкніть невикористовувані блоки/віджети/модулі.
  • Не надсилайте слайдери, анімації та мегаменю користувачам, які просто хочуть читати.
  • Оберіть тему, що поважає бюджети продуктивності.

6) Зробіть сервер «нудним»: сучасний PHP, opcache, адекватний FPM, швидке сховище

Після того як кеш і payload вирішені, вам все ще потрібен стабільний origin:

  • PHP 8.1+ з увімкненим opcache.
  • PHP-FPM налаштований під ваш CPU/RAM (без гігантських количеств процесів, що трясуть систему).
  • MySQL настроєний для вашого набору даних, з увімкненим логом повільних запитів.
  • SSD/NVMe-сховище, а не спільні обертові диски, що маскуються під «хмару».

Жарт №2: Якщо ви «оптимізували», встановивши п’ять плагінів продуктивності, ви не оптимізували — ви створили маленький аргументований комітет.

Три корпоративні історії з полів продуктивності

1) Інцидент через хибне припущення: «Мобіль кешується за замовчуванням»

Середня компанія мала маркетинговий сайт на WordPress і блог, що генерував ліди для продажів. На десктопі все виглядало добре, і команда з гордістю оголосила, що ввімкнула кешування. Проте мобільні конверсії загадково падали, а в службі підтримки з’являлися скарги, що сайт «липкий» і «випадково повільний».

Хибне припущення було тонким: правила кешу CDN варіювалися за user-agent, бо колись додали «оптимізацію для мобільних», яка сервіла трохи інший HTML. CDN розглядав мобіль як окремий бакет кешу, але не зберігав його довго через консервативний TTL. Гірше: маркетинговий плагін встановив cookie для анонімних користувачів, і CDN був налаштований обходити кеш, коли існував будь-який cookie.

Десктопні користувачі частіше заходили безпосередньо і мали прогрітий кеш у корпоративних мережах; мобільні приходили через соціальні посилання з параметрами відстеження й частіше обходили кеш. Origin-сервери могли подати кешований десктоп-контент, але не могли рендерити кожну мобільну сторінку динамічно під час пікового трафіку кампанії.

Виправлення не було «додати сервери». Воно полягало у нормалізації ключа кешу: ігнорувати нешкідливі cookie, обрізати/нормалізувати поширені параметри відстеження на edge та припинити варіювання HTML за user-agent, якщо це не потрібно. Також ввели явні заголовки статусу кешу, щоб будь-хто міг бачити HIT/MISS без гадання.

Результат: TTFB став стабільним. Звіти про «випадкову повільність» зникли, бо випадковість була політикою кешу, а не загадкою.

2) Оптимізація, що обернулася проти: «Давайте мінімізуємо й об’єднаємо все»

Інша організація мала WordPress + WooCommerce. Мобільні сторінки були важкі, тож команда ввімкнула агресивну оптимізацію: об’єднати CSS, об’єднати JS, відкладати все, інлайнити «критичний CSS» та десяток інших перемикачів, що звучали продуктивно.

Тиждень лабораторних чисел покращився. Потім почалися скарги клієнтів: кнопки іноді не працювали, валідація на checkout інколи падала, а деякі сторінки товарів мали збої оформлення лише на певних Android-пристроях. Маркетинг назвав це «проблемою бренду», що в корпоративному варіанті означає «ми піднімаємо всю справу».

Що сталося: плагін оптимізації змінив порядок скриптів і вніс race condition. Розширення WooCommerce очікували наявність певних бібліотек у конкретному порядку; об’єднання й відкладення порушили ці припущення. Тим часом HTTP/2 означав, що об’єднання в гігантські бандли вже не давало того великого ефекту. Вони обміняли надійність на теоретичну швидкість.

Відкат відновив функціонал, але вони не відмовилися від роботи над продуктивністю. Прийняли вузький підхід: не об’єднувати, поки не доведено, що наклад запитів — вузьке місце; натомість видаляти невикористувані скрипти, затримувати тільки неінтерактивні сторонні теги і берегти шлях checkout — мінімальні трансформації, максимальна передбачуваність.

Урок: якщо оптимізація може зламати грошові потоки, вона має проходити як реліз з канарським тестом, моніторингом і планом відкату. «Це лише фронтенд» — якраз шлях опинитися у конференц-колі.

3) Нудна, але правильна практика, що врятувала день: спостережуваність кешу і базові показники ємності

Глобальна компанія вела кілька WordPress-ресурсів і мала повторювану проблему: кожна велика кампанія спричиняла «сюрпризи» в продуктивності. Інфраструктурна команда не хотіла героїки; вони хотіли передбачуваності.

Вони додали дві нудні речі. По-перше: явну інструментацію кешу. Кожна відповідь мала заголовки, що показували статус кешу edge, статус кешу origin і чи потрапив запит у PHP. По-друге: базові показники ємності. Вони вели простий дашборд: CPU origin, диск I/O, глибина черги PHP-FPM, затримка MySQL і частка HIT CDN.

Під час наступної кампанії вони побачили, що частка HIT впала тільки для мобільного трафіку. Не здогадки — дані. Виявилося, що недавно доданий інструмент A/B тестування встановлював cookie при першому візиті, що обходило edge-кеш. Вони налаштували CDN, щоб ігнорувати цей cookie для неперсоналізованих сторінок, і частка HIT відновилась за кілька хвилин.

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

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

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

1) Симптом: мобіль відчувається «застиглим», поки нічого не з’явиться

Корінна причина: високий TTFB через промахи кешу або динамічне рендерення для кожного запиту.

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

2) Симптом: перший візит жахливий, повторні — нормальні

Корінна причина: відсутній CDN для статичних ресурсів, короткі TTL або відсутнє immutable-кешування; мережеві витрати мобільної мережі домінують при першому завантаженні.

Виправлення: Довговічні заголовки кешування для статичних ресурсів, відвантаження на CDN, увімкнення brotli/gzip, забезпечення версіонування файлів.

3) Симптом: LCP жахливий; усе інше завантажується згодом

Корінна причина: герой-зображення занадто велике або затримане через lazy-load чи CSS/шрифти, що блокують рендер.

Виправлення: Оптимізуйте LCP-зображення (змінити розмір + сучасний формат); не ледаче завантажуйте його; preload лише правильний варіант; обріжте CSS, що блокує рендер.

4) Симптом: натискання меню/кошика/пошуку відчувається повільно

Корінна причина: головний потік заблокований через важкий JavaScript і сторонні скрипти; поганий INP.

Виправлення: Видаліть невикористовувані скрипти, відкладіть некритичні теги, уникайте величезного DOM від конструкторів, агресивно аудитуйте сторонні сервіси.

5) Симптом: продуктивність добра поза годинами пік, під час кампаній жахлива

Корінна причина: насичення origin (CPU, БД, диск), черги PHP-FPM, колапс HIT ratio.

Виправлення: Зробіть кешування стійким, додайте ємність, ізолюйте базу даних, зменшіть динамічну роботу і моніторьте глибину черг та I/O. Не «масштабуйте», не зменшивши витрати на запит.

6) Симптом: мобіль повільніший за десктоп специфічно

Корінна причина: варіація за user-agent, мобільна специфічна розмітка, різний ключ кешу або мобільу подають більші зображення через зламані адаптивні правила.

Виправлення: Уніфікуйте HTML між пристроями де можливо; переконайтеся, що srcset працює; підтвердіть, що CDN не розділяє кеш марно за пристроями.

7) Симптом: «Встановили плагін кешу, але нічого не змінилося»

Корінна причина: кеш не подається (обхід через cookie, залогінений трафік, рядки запиту або конфлікти плагінів), або вузьке місце — сторонній JS і зображення, а не генерація HTML.

Виправлення: Перевірте за заголовками й логами; якщо HTML кеш HIT, але LCP все ще поганий, переходьте до оптимізації елемента LCP та очищення JS/сторонніх сервісів.

8) Симптом: адмінка повільна; фронтенд іноді теж

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

Виправлення: Проведіть аудит плагінів, очистіть автозавантажені опції, зменшіть фонові задачі, додайте object caching тільки якщо розумієте витіснення й пам’ять.

Чеклісти / покроковий план

Ось практичний план, який можна виконати, не перетворюючи сайт на науковий проєкт.

Фаза 1: Встановіть базову лінію (той самий день)

  1. Виміряйте TTFB і загальний час з вашого мережевого шляху сервера (curl Завдання 1) і з щонайменше одного зовнішнього місця.
  2. Зафіксуйте заголовки кешу і статус кешу (Завдання 2). Зробіть скриншот доказів. Не покладайтеся на пам’ять.
  3. Визначте елемент LCP у ключових шаблонах (головна сторінка, категорія, товар/стаття). Якщо це зображення, занотуйте файл і розмір (Завдання 10).
  4. Перелічіть сторонні домени на сторінці (Завдання 9).
  5. Перевірте здоров’я origin-ресурсів під типічним трафіком (Завдання 7 і 8).

Фаза 2: Виправте перше вузьке місце (1–3 дні)

  • Якщо TTFB високий:
    • Впровадьте кеш повних сторінок для анонімних користувачів (edge або origin).
    • Нормалізуйте обходи кешу: ігноруйте маркетингові параметри; обережно ставтеся до cookie.
    • Перенесіть WP-Cron з запитів сторінок (Завдання 13).
    • Увімкніть opcache і підтвердіть його роботу (Завдання 5).
  • Якщо LCP високий, але TTFB ок:
    • Змініть розмір і перекодуйте LCP-зображення; переконайтеся, що воно не ледаче завантажується.
    • Забезпечте довгі TTL для статичних ресурсів (Завдання 12).
    • Увімкніть brotli/gzip (Завдання 11).
  • Якщо взаємодія затримується:
    • Видаліть або відкладіть сторонні скрипти.
    • Приберіть невикористані модулі теми і віджети конструктора сторінок.
    • Уникайте перемикачів «об’єднати все», поки не доведено, що це безпечно для комерційних шляхів.

Фаза 3: Стабілізувати й запобігти регресії (постійно)

  1. Додайте спостережуваність кешу: повертайте заголовки статусу кешу; логуйтесь upstream response time; відстежуйте HIT ratio.
  2. Тримайте лог повільних запитів увімкненим з ротацією.
  3. Прийміть бюджет продуктивності: макс. розмір LCP-зображення, макс. кількість сторонніх доменів, макс. KB JS для мобільних шаблонів.
  4. Тестуйте checkout і login шляхи при кожній зміні продуктивності (канарка і відкат).

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

1) Чому мій сайт WordPress повільний тільки на мобільних, а не на десктопі?

Десктоп приховує проблеми завдяки потужнішим CPU і часто нижчій латентності. Мобіль підсилює затримку і роботу головного потоку. Якщо TTFB високий, мобільні мережі роблять це відчутнішим; якщо JS важкий, мобільні CPU посилюють затримку.

2) З чого почати перевірку: зображення чи хостинг?

Перевірте TTFB спочатку. Якщо TTFB послідовно високий — це сервер/кеш/БД/edge. Якщо TTFB нормальний — виправляйте LCP-зображення і JS. Не купуйте потужніший сервер, щоб вирішити 5MB герой-зображення.

3) Чи потрібен CDN для мобільної продуктивності?

Якщо ваша аудиторія географічно розподілена або origin повільний — так. Навіть локально CDN допомагає кешувати статичні ресурси і іноді HTML. Але CDN не виправить незакешовані сторінки або зламані правила кешу.

4) Чи достатньо плагіна кешу?

Іноді. Але плагіни можуть обходитися через cookie, рядки запиту, залогінений стан і поведінку WooCommerce. Якщо можете, надавайте перевагу кешуванню на edge або зворотньому проксі, де можна довести HIT/MISS і контролювати правила обходу явно.

5) Чи варто об’єднувати CSS/JS, щоб зменшити запити?

Не за замовчуванням. З HTTP/2/HTTP/3 менше запитів менш критично, ніж менший обсяг і менше виконуваного JS. Об’єднання може зламати порядок скриптів і зробити кешування менш ефективним.

6) Мої Core Web Vitals показують поганий INP на мобільних. З чого почати?

Зменшіть роботу головного потоку: видаліть сторонні скрипти, скоротіть JS теми/конструктора сторінок і уникайте важких UI-віджетів. Потім шукайте довгі завдання в інструментах продуктивності, але крок «видалити спочатку» зазвичай працює.

7) WooCommerce повільний на мобільних — що інше?

Сторінки WooCommerce часто не можна кешувати так само, як блог, через кошики, сесії та персоналізацію. Ви маєте суворо визначати, що можна кешувати (часто сторінки товарів) і тримати checkout/cart стабільними та легкими в оптимізації, а не трансформувати їх надмірно.

8) Чи вирішить індексування бази даних самостійно мобільну повільність?

Іноді. Це може виправити високий TTFB, коли домінують повільні запити і кеш неможливий (або відсутній). Але більшість публічних сторінок WordPress не повинні виконувати важкі запити при кожному перегляді. Використовуйте кеш, щоб не потребувати героїчної оптимізації запитів для анонімного трафіку.

9) Чому результати продуктивності так сильно відрізняються між тестами?

Теплота кешу, варіативність мережі і фонові завдання (cron, очищення кешу, деплои) викликають коливання. Періодична зміна TTFB часто вказує на промахи кеша, черги PHP-FPM або конкуренцію I/O бази даних.

Практичні наступні кроки

Робіть їх у порядку і зупиняйтесь, коли метрика змінилася. Продуктивність — це черга вузьких місць, а не єдиний дракон.

  1. Виміряйте TTFB за допомогою curl і вирішіть: спочатку бекенд чи фронтенд.
  2. Доведіть, що кеш працює через заголовки і логи; виправте правила обходу кешу, що вбивають мобільний трафік.
  3. Оптимізуйте елемент LCP (майже завжди це зображення) і переконайтеся, що воно не ледаче завантажується.
  4. Видаліть сторонні скрипти, поки сайт не поводитиметься нормально на середньому телефоні.
  5. Стабілізуйте origin: увімкніть opcache, вимкніть WP-Cron з запитів, увімкніть лог повільних запитів, стежте за I/O і глибиною черг.
  6. Зафіксуйте правила: бюджет продуктивності, моніторинг і план відкату для «оптимізацій».

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

← Попередня
Налаштування MTU для VPN: чому великі файли зависають, коли веб працює (і як вибрати правильний MTU)
Наступна →
Оновлення Docker Compose без простоїв: міф, реальність і робочі патерни

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