Итак, в чем проблема? Бывает, что содержимое страниц сайта может меняться в зависимости от установленных cookie. Например на одном из интернет-магазинов меня попросили создать систему отображения разных рекламных баннеров в зависимости от города, с которого пользователь зашел на сайт. Город, если удалось его установить, записывается в cookie и для такого пользователя (и всех других с этого же города) нужно отображать определенный баннер.
Но по-умолчанию для WP Super Cache нет разницы, какие сookie установлены у пользователя. Он будет отдавать ту же, закешированную ранее страницу, поскольку ориентируется только на уникальный УРЛ этой страницы, а он никак не изменяется после установки нами куки. В результате мы получим такую же страницу, какой она была до установки куки. Оказалось, что сам будучи плагином, WP Super Cache поддерживает плагины внутри себя и мы можем написать свой, который бы учитывал нашу ситуацию.
Пример решения для тех, кому не интересен разбор деталей.
По-умолчанию сейчас WP Super Cache распространяется со следующими дополнениями, рассчитаными на разные популярные случаи жизни:
Как видим, плагин рассчитан на WordPress Multisite установку, поддерживает работу в паре с плагином Jetpack, ситуацию, когда сайт работает на нескольких доменах т.д. Также в эту папку можно положить свои плагины, написанные под свою уникальную ситуацию. Чем собственно мы и воспользуемся для решения нашей проблемы.
Алгоритм WP Super Cache таков, что для каждого уникального УРЛ геренируется своя копия страницы и помещается в папку, которая называется также, как и путь к этой странице в той части УРЛ, что находится после названия домена. Все это работает при условии наличия модуля mod_rewrite для веб-сервера Apache. Копии кешированных страниц хранятся в папке /wp-content/cache/supercache/%{SERVER_NAME} где последний параметр это название домена сайта. Например копия данной страницы хранится по указанному пути, который совпадает с ее УРЛ
На всякий случай лежат два файла — сжатый gzip-ом и несжатый. Последний на случай, если браузер пользователя не поддерживает технологию сжатия gzip. Это собственно и есть супер-кеширование данного плагина, так как при наличии mod_rewrite веб-сервер вообще не запускает PHP-скрипты WordPress, не обращается к Базе Данных, а лишь проверяет существование кешированной страницы для данного УРЛ и в случае ее наличия отдает пользователю.
Это самый быстрый способ выдать страницу в ответ на запрос. Для этого плагин и прописывает правила mod_rewrite в файле .htaccess при настройке. С виду они громоздкие и страшные, но на самом деле там четыре раза повторяется почти одно и то же сочетание правил для ситуаций с наличием или отсутствием HTTPS и GZIP.
RewriteCond %{REQUEST_URI} !^.*[^/]$ RewriteCond %{REQUEST_URI} !^.*[^/]$ RewriteCond %{REQUEST_URI} !^.*//.*$ RewriteCond %{REQUEST_METHOD} !POST RewriteCond %{QUERY_STRING} !.*=.* RewriteCond %{HTTP:Cookie} !^.*(comment_author_|wordpress_logged_in|wp-postpass_).*$ RewriteCond %{HTTP:X-Wap-Profile} !^[a-z0-9]+ [NC] RewriteCond %{HTTP:Profile} !^[a-z0-9\"]+ [NC] RewriteCond %{HTTP_USER_AGENT} !^.*(2.0\ MMP|240x320|400X240|AvantGo|BlackBerry|Blazer|Cellphone|Danger|DoCoMo|Elaine/3.0|EudoraWeb|Googlebot-Mobile|hiptop|IEMobile|KYOCERA/WX310K|LG/U990|MIDP-2.|MMEF20|MOT-V|NetFront|Newt|Nintendo\ Wii|Nitro|Nokia|Opera\ Mini|Palm|PlayStation\ Portable|portalmmm|Proxinet|ProxiNet|SHARP-TQ-GX10|SHG-i900|Small|SonyEricsson|Symbian\ OS|SymbianOS|TS21i-10|UP.Browser|UP.Link|webOS|Windows\ CE|WinWAP|YahooSeeker/M1A1-R2D2|iPhone|iPod|Android|BlackBerry9530|LG-TU915\ Obigo|LGE\ VX|webOS|Nokia5800).* [NC] RewriteCond %{HTTP_user_agent} !^(w3c\ |w3c-|acs-|alav|alca|amoi|audi|avan|benq|bird|blac|blaz|brew|cell|cldc|cmd-|dang|doco|eric|hipt|htc_|inno|ipaq|ipod|jigs|kddi|keji|leno|lg-c|lg-d|lg-g|lge-|lg/u|maui|maxo|midp|mits|mmef|mobi|mot-|moto|mwbp|nec-|newt|noki|palm|pana|pant|phil|play|port|prox|qwap|sage|sams|sany|sch-|sec-|send|seri|sgh-|shar|sie-|siem|smal|smar|sony|sph-|symb|t-mo|teli|tim-|tosh|tsm-|upg1|upsi|vk-v|voda|wap-|wapa|wapi|wapp|wapr|webc|winw|winw|xda\ |xda-).* [NC] RewriteCond %{HTTP:Accept-Encoding} gzip RewriteCond %{HTTPS} !on RewriteCond %{DOCUMENT_ROOT}/wp-content/cache/supercache/%{SERVER_NAME}/$1/index.html.gz -f RewriteRule ^(.*) /wp-content/cache/supercache/%{SERVER_NAME}/$1/index.html.gz [L]
Если разобрать логику этих правил, то упрощенно она такая — «Если это стандартный запрос страницы с обычного браузера, то проверить наличие кешированной копии страницы и отдать ее в случае наличия».
Кроме супер-кеширования с помощью mod_rewrite WP Super Cache поддерживает и РНР-кеширование, когда процесс отдачи страницы требует выполнения минимального объема PHP кода, что происходит чуть медленнее супер-кеширования, но тоже достаточно быстро. Для таких необычных запросов и ситуаций плагин генерирует копии страниц друго формата:
Примером такой ситуации может служить посещение страницы результатов поиска, УРЛ которой будет содержать строку GET-запроса — например https://stepasyuk.org.ua/?s=кеширование
Или посещение страницы администратором сайта, который только что обновил блог и ему надо показать саму свежую копию страницы и возможно еще несколько раз показать ее в будущем. Такие запросы относятся к нестандартным и их сложно или порой невозможно адекватно обработать на уровне модуля mod_rewrite. Поэтому в дело вступет PHP алгоритм.
Если описать сильно упрощенно, то этот PHP-алгоритм генерирует сжатые копии страниц, в названии которых содержится префикс wp-cache
и md5_хеш_уникальной_строки
, которая формируется для каждой такой нестандартной ситуации. Например при наличии в браузере куки залогиненного в WordPress пользователя, название которой начинается с wordpress_logged_in
, будет создан файл, где в названии будет содержаться хеш строки, которая содержит название этой куки. Пример такой строки wordpress_logged_in_2710c71779dfc69ae9a2ef8241b3c297
При повторном обращении к этой странице WP Super Cache получит такие же параметры cookie, что и в предыдущий раз, сформирует такую же строку из названия cookie, хеширует ее, проверит наличие кешированной версии страницы с получивишмся названием и отдаст ее, если с прошлого посещения ничего не поменялось. Ситуация, когда у пользователя установлена кука с названием содержащим wordpress_logged_in
предусмотрена в правилах mod_rewrite добавленных плагином:
RewriteCond %{HTTP:Cookie} !^.*(comment_author_|wordpress_logged_in|wp-postpass_).*$ ... RewriteRule ^(.*) /wp-content/cache/supercache/%{SERVER_NAME}/$1/index-https.html.gz [L]
Буквально логика этого правила такова — «если название Cookie НЕ начинается с comment_author
или wordpress_logged_in
или wp-postpass_
, то перейти к следующему правилу». Если же название все-таки начинается с предложенных вариантов, то mod_rewrite не выполнит правило, стоящее после этого условия. То-есть не перенаправит запрос в папку supercache, где уже лежит кешированная копия запрашиваемой страницы, а запустит PHP-код, который уже возьмет «разруливание» запроса в свои руки. Именно механизм разруливания на уровне PHP мы можем использовать для решения нашей проблемы с сообственными cookie.
Решение. Три простых шага
- Создадим файл с произвольным названием, например banner.php, нашего плагина для WP Super Cache в папке
/wp-content/plugins/wp-super-cache/plugins
- Процесс формирования уникальной строки названия файла кешированной копии страницы проходит через хук
wp_cache_get_cookies_values
в файле/wp-content/plugins/wp-super-cache/wp-cache-phase1.php
Только это не классический WordPress-хук, а внутренний для плагина WP Super Cache. И добавлять на него событие следует функциейadd_cacheaction( 'wp_cache_get_cookies_values', 'my_function_name' );
а не как принято в WordPress черезadd_filter();
илиadd_action();
Чтобы нам учесть ситуацию с наличием установленной нами cookie, допустим, с названием
banner
, мы должны в процесс формирования названия файла кешированной копии добавить значение этой куки. Для каждого баннера значение cookie будет уникально, поэтому в результате мы получим и уникальное название кешированной копии для каждого значения куки этой страницы:function cache_my_page_with_correct_banner( $string ){ // с учетом того, что наш баннер отображается только на главной if( isset( $_COOKIE['banner'] ) && is_home() ){ $string = 'banner_' . $_COOKIE['banner']; } return $string; } add_cacheaction( 'wp_cache_get_cookies_values', 'cache_my_page_with_correct_banner' );
- Теперь нам нужно учесть ситуацию с наличием нашей куки в правилах mod_rewrite, иначе запрос будет направлен в папку supercache, где лежит обычная копия нашей страницы. Для этого изменим строку, проверяющую Cookies и добавим к ней проверку наличия нашей куки — в конец строки вариант |banner:
RewriteCond %{HTTP:Cookie} !^.*(comment_author_|wordpress_logged_in|wp-postpass_|banner).*$
Поскольку данная строка повторяется в .htaccess 4 раза, то и столько же раз нужно внести название нашей куки в правила.
Вот и все — три простых шага и теперь кеширование будет работать корректно для страницы с любым значением нашей куки banner. Важно помнить, что при обновлении плагина из админ-панели WordPress, система полностью удаляет папку со старой версией плагина и в том числе и написанный нами. Поэтому стоит сохранить его копию для восстановления в будущем. Правила mod_rewrite при обновлении WP Super Cache не изменяются, но на всякий случай сохранить их тоже не помешает.
Послесловие
WP Super Cache — отличный плагин. При том, что написан он давно и по-возможностям не самый крутой и даже написан без ООП и даже код его недокументированный и переменные с функциями названы не в лучших традициях программирования. Но зато мне он нравится больше других. Напирмер W3 Total Cache имеет больше возможностей, но с ним админка на рядовом сервере тупит безбожно. Есть еще MaxCache для WordPress, но он платный и процесс установки требует ковыряния в коде, что подходит далеко не для всех. Поэтому WP Super Cache — оптимальное решение для большинства. Хотя в целом эта тема, какой плагин для кеширования лучше — предмет для холиваров. Спорить и доказывать свою точку зрения не буду, у каждого она может быть своя.
Иван
Спасибо, очень выручил )
Владимир
Здравствуйте Андрей. Очень круто и профессионально. Но у меня несколько другая проблема. Яндекс-вебмастерто то видит, то не видит мобильную версию, которая формируется плагином WPtouch. При отключении WP Super Cache — видит всегда. Пытался в robots.txt прописать директиву для мобильного бота на запрет посещения wp-content/cache, не помогло. Службы поддержки хоста и яшки советуют обратиться к разработчику программы, то есть формально отписались. Посоветуйте хоть что -нибудь, пожалуйста.
Даниил
Благодарю! Только вот плагин лучше класть не в папку плагина (при обновлении он сотрёт её), а в, допустим, `plugins/wp-super-cache-plugins`, и указать её как папку плагинов для WP Super Cache в `wp-config.php` строкой `$wp_cache_plugins_dir = dirname(__FILE__) . ‘/wp-content/plugins/wp-super-cache-plugins/’;`
Даниил
Ой, не `wp-config.php`, а `wp-content/wp-cache-config.php`. И можно вот так вот: `WP_CONTENT_DIR . «/plugins/wp-super-cache-plugins/»`
В общем, я оформил всё это дело более-менее: https://gist.github.com/Grawl/ef8a8194f8ae013641133d44bffbafb4
Айдар
Здравствуйте!
Делаю все как написано выдает 500 ошибку при сохранении .htaccess