Оптимизируем WordPress по самый не балуй

14 января 2013

Вы, конечно же, в курсе, что этот блог работает на WordPress. Некоторое время назад я начал серьезно беспокоиться относительно быстродействия этого движка. Во-первых, меня волновало количество используемой им памяти. Например, когда по блогу начинают ходить поисковые боты, WordPress запросто может скушать 1 Гб оперативки. Во-вторых, я был обеспокоен временем, за которое у пользователя загружаются страницы. В случае попадания в кэш проблем не возникает, но иначе страница запросто может генерироваться 1-2 секунды.

Для оптимизации блога мною были предприняты следующие шаги.

В первую очередь отключаем все лишние плагины. Если плагин можно заменить куском кода в шаблоне или строкой в файле .htaccess, заменяем. В моем случае остались только следующие девять плагинов — это AddQuicktag, CodeColorer, Disqus, Google XML Sitemaps, Limit Login Attempts, RusToLat, WP-Optimize, WP-PageNavi, WP Super Cache. Строго говоря, от большинства из них также можно избавиться, но, как мы скоро увидим, в этом нет необходимости.

В моем случае предыдущий шаг не дал существенного эффекта. Тогда я попробовал отключить плагин Disqus и использовать вместо него так называемый «универсальный JavaScript код Disqus». Вместо Disqus с тем же успехом можно использовать комментарии ВКонтакта или Facebook’а, тут все зависит от вашей аудитории. В результате страницы стали генерироваться за ~500 мс вместо 1-2 секунд! Я смог удалить все комментарии из БД, что, вероятно, также дало небольшой прирост скорости.

Единственный существенный недостаток перехода с плагина на JavaScript заключается в том, что поисковые системы не будут индексировать комментарии. Однако если вы внимательно посмотрите, по каким запросам на ваш сайт приходят посетители, то скорее всего обнаружите, что к комментариям относятся примерно 0% запросов. По-моему, Миша Шакин когда-то тоже это отмечал.

Дополнение: Как выяснилось, Google прекрасно умеет индексировать комментарии Disqus’а, так что указанного недостатка на самом деле нет!

Раз использование сторонних сервисов дает такой профит, я решил заодно использовать для поиска по сайту Google. Поиск по сайту средствами WordPress, разумеется, при этом следует запретить, например, с помощью правил в .htaccess. Помимо разгрузки хостинга, положительный эффект здесь заключается в том, что Google на самом деле ищет по сайтам на WordPress лучше самого WordPress. В общем случае использование Google плохо тем, что некоторые страницы могут медленно заноситься в индекс. Однако мои посты находятся в индексе Google уже через несколько минут после публикации, так что тут проблемы нет.

Далее. Проверяем, не используется ли в шаблоне десяток CSS или JS файлов, тяжелых картинок и тп. CSS файлы по возможности объединяем и упаковываем, JS прогоняем через Google Closure Compiler (есть приложение и онлайн-версия). Смотрим, нельзя ли уменьшить размер HTML-кода. Например, я заметил, что в заголовках постов у меня используется следующий код:

<a href="..." rel="bookmark" title="заголовок">заголовок</a>

Я заменил его на:

<a href="...">заголовок</a>

… сэкономив тем самым посетителям несколько десятков байт трафика. Также размер страниц удалось немного уменьшить с помощью следующего кода в functions.php:

remove_action('wp_head', 'wlwmanifest_link');
remove_action('wp_head', 'rsd_link');
remove_action('wp_head', 'wp_shortlink_wp_head');
remove_action('wp_head', 'adjacent_posts_rel_link_wp_head');
remove_action('wp_head', 'wp_generator');
remove_action('wp_head', 'feed_links_extra', 3);

Кроме того, этот код уменьшает количество ссылок, по которым пытаются ходить поисковые боты. Затем я принялся за HTTP-заголовки. Из явного мусора (который виден при промохе мимо кэша) в них было обнаружено:

X-Pingback: http://eax.me/xmlrpc.php
Link: <http://eax.me/?p=8503>; rel=shortlink

X-Pingback выпиливается так:

function remove_pingback_header($headers) {
  unset($headers['X-Pingback']);
  return $headers;
}
add_filter('wp_headers', 'remove_pingback_header');

А вот код для выпиливания Link:

function empty_shortlink($shortlink, $id, $context, $allow_slugs) {
  return NULL;
}
add_filter('get_shortlink', 'empty_shortlink', 10, 4);

Также, чтобы не палить версию PHP (заголовок X-Powered-By), прописываем в файле php.ini:

expose_php=Off

Кажется, после этого изменения требуется перезапустить Apache. Заодно я проверил список используемых расширений PHP и модулей Apache. В итоге отключил штук 5 ненужных мне модулей/расширений.

Тут до меня дошло, что практически блог отдает только статические страницы. То есть, страницы изменяются, только если я публикую новые посты или редактирую старые. Тогда я зашел в настройки WP Super Cache, где увеличил время жизни кэша и поставил галочку «использовать mod_rewrite». Также я изменил еще пару-тройку вещей, включая правила плагина в .htaccess. К сожалению, точную последовательность действий я уже не воспроизведу. Тут главное не перемудрить — проверьте, что страницы тэгов успешно обновляются при публикации новых постов, что страницы берутся из кэша при просмотре сайта с мобильных устройств и тд. Обратите внимание, что для быстрого прогрева кэша в WP Super Cache есть кнопка «создать общий кэш».

Казалось бы, теперь мы просто отдаем пользователям статику в обход PHP, однако количество используемой памяти что-то не спешило снижаться. Видимо, PHP все-таки что-то делает. Тогда я решил добавить в index.php следующий код:

$fid = fopen("/path/to/index-php.txt", "a");
fwrite($fid,
  date("Y-m-d H:i:s")."\t".
  $_SERVER['REQUEST_URI']."\t".
  $_SERVER['REMOTE_ADDR']."\t".
  $_SERVER['HTTP_USER_AGENT']."\n");
fclose($fid);

Оказалось, что по блогу ходит множество ботов, которые ищут дырявые форумы, запрашивают страницы /article-name вместо /article-name/, а также несуществующие файлы с именами вроде apple-touch-icon.png и тд. Все эти запросы шли мимо кэша прямиком в WordPress. Для борьбы с этой проблемой я добавил в .htaccess следующее:

RewriteCond %{REQUEST_URI} ^/$ [OR]
RewriteCond %{REQUEST_URI} ^/[a-z0-9-]+/$ [OR]
RewriteCond %{REQUEST_URI} ^/tag/[a-z0-9-]+/$ [OR]
RewriteCond %{REQUEST_URI} ^/tag/[a-z0-9-]+/feed/$ [OR]
RewriteCond %{REQUEST_URI} ^/tag/[a-z0-9-]+/page/[0-9]+/$ [OR]
RewriteCond %{REQUEST_URI} ^/page/[0-9]+/$
RewriteRule . /index.php [L]

Поскольку некоторые посетители приходят в блог со всяким мусором в query string типа utm_source=, я также настроил редиректы с URL, содержащих какие-то аргументы в запросе, на те же URL, только без аргументов:

RewriteCond %{QUERY_STRING} !=""
RewriteCond %{REQUEST_URI} !codecolorer
RewriteCond %{QUERY_STRING} !^(p|page_id|preview)=
RewriteCond %{REQUEST_URI} !^/wp-(admin|login|cron|includes)
RewriteRule ^(.*)$ /$1? [R=301,L]

Специально для поисковых роботов Google в robots.txt я дописал:

Disallow: */feed
Disallow: */trackback
Disallow: */comment

Наконец, чтобы посетители, запросившие несуществующую страницу, видели нормальное сообщение об ошибке место «404 Not Found», я завел специальную страницу и прописал в .htaccess:

ErrorDocument 404 http://eax.me/not-found/

На данном этапе было совершенно очевидно, что в WordPress не приходит никаких лишних запросов, в большинстве случаев страницы вообще берутся из кэша в обход PHP. За исключением разве что RSS-фидов, ибо WP Super Cache по каким-то причинам не умеет их кэшировать. Следует отметить, что количество используемой памяти на сервере к этому моменту уже существенно снизилось (оно колебалось в районе 100-200 Мб, с редкими пиками до 300-400 Мб), но я все рано не был доволен результатом.

Я зашел на сервер по SSH и обнаружил, что каждый из процессов Apache занимает 17-18 Мб памяти. Значит, одновременно приходит десяток запросов — и вот 200 Мб памяти как ни бывало. Понятно, что за счет copy-on-write это не 200 Мб физической памяти, но при построении графиков в админке хостинга это, похоже, не учитывалось. Тогда я написал хостеру письмо с просьбой поднять Nginx.

Пришлось подождать какое-то время, но результат того стоил:

Использование памяти до и после включения nginx

Уменьшение количества используемой памяти происходит по той причине, что «тяжелый» Apache не занимается передачей данных клиенту, вместо него это делает «легкий» Nginx, поднятый локально. Apache быстро отрабатывает, отдает данные Nginx и завершает работу. Таким образом, одновременно работает меньше процессов Apache и свободной памяти остается больше.

А вот с какой скоростью происходит загрузка страниц:

Скорость загрузки страниц WordPress-блога

Скажем так, это не медленнее, чем загружается главная страница Яндекса. И все это на самом обычном шаред хостинге от RU-CENTER. Подозреваю, что переездом на выделенный сервер мне предстоит заниматься очень не скоро.

Помимо описанных выше оптимизаций я также экспериментировал со следующими.

Пробовал перенести картинки и всякие там архивы на Dropbox, но особой экономии памяти или иного выигрыша от этого не увидел и вернул, как было. Пробовал сохранять в кэш gzip’ованные страницы и отдавать их в таком виде посетителям (кстати, это работает даже без mod_gzip). Тоже не увидел профита, к тому же, несжатые страницы все равно приходится класть в кэш на случай, если придет клиент без поддержки gzip. Решил не мудрить и отключить это дело. Пробовал отключать wp-cron в WordPress и прописывать его в crontab. Все сломал и профита не увидел, вернул все взад. Также попробовал плагин WP-Minify, оптимизирующий HTML-код страниц. Прикольная штука, но на время загрузки страниц мало влияет, а вот скорость генерации увеличивает примерно на 100 мс. Выключил и удалил.

Еще нашел прикольный плагин Really Static, позволяющий генерировать статические сайты на WordPress. И потом хоть на narod.ru, хоть на GitHub их заливай. Или, чтоб было удобнее, можно сам WordPress поднять на поддомене с ограничением доступа по паролю, а на основном домене размещать только статику. Стану десятитысясником, обязательно взгляну на него еще раз, а пока от него будет больше неудобств, чем пользы.

Если вам понравилась эта заметка, возможно, вам также будет интересно ознакомиться с моим переводом серии постов Reduce WordPress CPU Usage, некогда опубликованной в блоге cravingtech.com. И еще одно. Не знаю, совпадение или нет, но когда я начал заниматься всеми этими оптимизациями, заметно увеличилось количество переходов с Google.

Итак, вы все еще считаете, что WordPress — тормозной движок? Или все-таки вы не умеете им пользоваться?

Дополнение: Вас также может заинтересовать статья Мой опыт переноса блога с шаред хостинга на VDS.

Метки: , , , , .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.