Ускоряем свой WordPress-сайт с помощью Pound, Varnish, Nginx и mod_pagespeed

Подавляющее большинство онлайн-статей, посвященных ускорению WordPress-сайта, рассматривают (или хотя бы упоминают) использование плагина W3 Total Cache (или W3TC, если коротко). Это верно, ведь указанный плагин является полезным и практичным универсальным решением, позволяющим ускорить работу WordPress-сайта, проведя относительно небольшой объем работ.

Однако я не являюсь сторонником универсальных решений, главным образом потому, что они добавляют лишнюю сложность и могут приводить к проблемам совместимости (если использовать их в сборке с разными плагинами); также могут появляться сложности и с их обновлением.

Именно по этой причине я решил подыскать альтернативы, которые помогают ускорить работу нашего (защищенного) WordPress-сайта, и друзья посоветовали мне взглянуть на mod_pagespeed, который оказался идеальным решением для того, чтобы сделать наш WordPress-сайт действительно быстрым, причем с минимальными усилиями.

Требования

Эта статья предполагает, что у вас имеются по крайней мере минимальные знания Linux (все наши примеры основаны на Ubuntu), вы знаете, как использовать shell, и, что более важно, ваш сайт расположен на вашем собственном VPS. Виртуальный хостинг не подойдет для этого, поскольку нам нужна будет настроенная версия nginx, поддерживающая mod_pagespeed.

Есть много хороших и относительно дешевых VPS-провайдеров, которые предлагают свои услуги. Я рекомендую в данном случае воспользоваться Digital Ocean.

Обратите внимание, что все команды, которые начинаются со знака доллара, описывают unix-команды; вам не требуется вводить этот доллар в командную строку.

Основные компоненты

Как следует из заголовка, нам понадобится установить на сервер некоторые программы; самая главная (для кэширования) называется Varnish, она сохраняет весь ваш HTML-вывод во временную папку на диске, что позволяет обходиться без постоянных запросов к WordPress. У этой программы, правда, имеется один недостаток – она не поддерживает SSL-терминацию, по причине чего нам и требуется Pound.

Два последних компонента, которые нам понадобятся – это Nginx (веб-сервер, как, к примеру, Apache) и PHP-FPM (менеджер процессов PHP), поскольку Nginx не поддерживает модули.

Естественно, вам нужно будет сгенерировать ключ сертификата и приобрести сертификат для своей сборки. Сертификаты начального уровня достаточно дешевые, некоторые из них стоят от $10/год, что позволяет легко их приобретать для своих целей.

Начало работы

Предположим, что у вас есть чистый, свежий VPS с SSH. Подсказка: если вы используете Linux или Mac OS на локальной машине, вы можете добавить следующий шорткат к ~/.ssh/config (создайте этот файл, если он не существует):

Host MyHost
  HostName [host-ip]
  Port [host-port]
  User [host user, usually root]
  IdentityFile [path/to/your/ID_RSA, usually ~/.SSH/ID_RSA]

В итоге вы сможете запускать следующую команду для входа в шелл вашего сервера: $ ssh MyHost

После того как вы войдете в командную оболочку, вы сможете установить все необходимые компоненты через менеджер пакетов, в нашем случае: apt-get.

Выполняем следующую команду:

$ sudo apt-get install php5-fpm php5-cli php5-mysql varnish pound mariadb-server-5.5 unzip

Вам предложат дважды ввести root-пароль для MariaDB, которая является прямой заменой MySQL. Мы не будем устанавливать Nginx на данном этапе, поскольку версия, которая идет по умолчанию, не поддерживает mod_pagespeed, необходимый нам, поэтому мы установим его вручную. Чтобы сделать это, перейдите к официальной документации и выполните все шаги руководства, правда, с одним исключением. Замените строку:

./configure --add-module=$HOME/ngx_pagespeed-release-${NPS_VERSION}-beta

На следующую:

./configure --add-module=$HOME/ngx_pagespeed-release-${NPS_VERSION}-beta --with-http_gzip_static_module --with-http_realip_module

Это позволит нам включить модули для GZIP-сжатия статичных файлов и отслеживания корректных IP-адресов в вашей сборке WordPress. После того, как вы сделаете это, вам нужно будет установить Nginx в /usr/local/nginx/.

Если вы попытаетесь посетить URL (или IP) вашего сервера на данном этапе, то вы ничего не увидите, поскольку мы пока не настраивали все эти компоненты. После того, как мы это сделаем, схема взаимодействия между компонентами вашего сервера будет выглядеть следующим образом:

diagram

Pound

Давайте начнем с компонента outermost, который работает с двумя стандартными портами, 80 и 443 (HTTP и HTTPS, соответственно). Отредактируйте файл /etc/pound/pound.cfg и поместите в него следующий контент:

User            "www-data"
Group           "www-data"
LogLevel        1
Alive           30
Control "/var/run/pound/poundctl.socket"

ListenHTTP
    Address  0.0.0.0
    Port     80
    # This part makes sure you redirect all HTTP traffic to HTTPS
    Service
        HeadRequire "Host: yourdomain.com"
        Redirect "https://yourdomain.com"
    End
End
ListenHTTPS
    HeadRemove "X-Forwarded-Proto"
    AddHeader  "X-Forwarded-Proto: https"
    Address    0.0.0.0
    Port       443
    Cert       "/etc/ssl/yourdomain.com/yourdomain.com.pem"
    # This service removes the WWW-part
    Service
        HeadRequire "Host: www.mydomain.com"
        Redirect "https://yourdomain.com"
    End
    # The main service which passes requests to Varnish
    Service
        HeadRequire "Host: yourdomain.com"
        BackEnd
            Address 127.0.0.1
                    # 6081 is the default Varnish port
            Port    6081
        End
    End
End

Перед тем, как проводить рестарт Pound с новой конфигурацией, вам нужно будет добавить файл PEM-сертификата в определенную папку; этот файл вы можете создать, следуя представленным инструкциям. Также вам нужно будет отредактировать /etc/default/pound и задать startup=1.

Готово. Pound теперь сконфигурирован, и после запуска $ service pound restart (для Ubuntu, другие дистрибутивы могут иметь свои команды для управления службой) вы сможете посетить сконфигурированный домен. Естественно, вы получите ошибку (503 Service Unavailable), однако это не страшно, поскольку мы еще не все настроили.

Varnish

Далее нам нужно будет сконфигурировать Varnish. Редактируем файл /etc/varnish/default.vcl и помещаем в него следующий код (удаляем все остальное):

backend default {
  .host = "127.0.0.1";
  .port = "8080";
}

acl purge {
  "127.0.0.1";
  "localhost";
}

sub vcl_recv {
    if (req.request == "PURGE") {
      if (!client.ip ~ purge) {
        error 405 "Not allowed.";
      }
      ban("req.url ~ "+req.url+" && req.http.host == "+req.http.host);
      error 200 "OK";
    }

    # only using one backend
    set req.backend = default;

    # set standard proxied ip header for getting original remote address
    set req.http.X-Forwarded-For = client.ip;

    # logged in users must always pass
    if( req.url ~ "^/wp-(login|admin)" || req.http.Cookie ~ "wordpress_logged_in_" ){
        return (pass);
    }

    # don't cache search results
    if( req.url ~ "\?s=" ){
        return (pass);
    }

    # always pass through posted requests and those with basic auth
    if ( req.request == "POST" || req.http.Authorization ) {
         return (pass);
    }

    # else ok to fetch a cached page
    unset req.http.Cookie;
    return (lookup);
}



sub vcl_fetch {

    # remove some headers we never want to see
    unset beresp.http.Server;
    unset beresp.http.X-Powered-By;

    # only allow cookies to be set if we're in admin area - i.e. commenters stay logged out
    if( beresp.http.Set-Cookie && req.url !~ "^/wp-(login|admin)" ){
        unset beresp.http.Set-Cookie;
    }

    # don't cache response to posted requests or those with basic auth
    if ( req.request == "POST" || req.http.Authorization ) {
         return (hit_for_pass);
    }

    # only cache status ok
    if ( beresp.status != 200 ) {
        return (hit_for_pass);
    }

    # don't cache search results
    if( req.url ~ "\?s=" ){
        return (hit_for_pass);
    }

    # else ok to cache the response
    set beresp.ttl = 24h;
    return (deliver);
}



sub vcl_deliver {
    if (obj.hits > 0) {
        set resp.http.X-Cache = "HIT";
    }
    else {
        set resp.http.X-Cache = "MISS";
    }
    unset resp.http.Via;
    unset resp.http.X-Varnish;
}

sub vcl_hit {
  if (req.request == "PURGE") {
    purge;
    error 200 "OK";
  }
}

sub vcl_miss {
  if (req.request == "PURGE") {
    purge;
    error 404 "Not cached";
  }
}


sub vcl_hash {
    hash_data( req.url );
    if ( req.http.host ) {
        hash_data( regsub( req.http.host, "^([^\.]+\.)+([a-z]+)$", "\1\2" ) );
    } else {
        hash_data( server.ip );
    }
    # ensure separate cache for mobile clients (WPTouch workaround)
    if( req.http.User-Agent ~ "(iPod|iPhone|incognito|webmate|dream|CUPCAKE|WebOS|blackberry9\d\d\d)" ){
        hash_data("touch");
    }
    return (hash);
}

Я не буду сейчас раскрывать здесь все детали работы кода, поскольку официальная документация Varnish делает это гораздо лучше. Упомяну лишь про три вещи, которые здесь важны. Во-первых, это строка, которая позволяет нам очистить (purge) кэш (удалить его), что очень полезно в WordPress, поскольку в таком случае обновленные участки автоматически инициируют удаление кэша. Есть несколько плагинов для этого, мы используем Better WP Varnish, но вы можете вполне воспользоваться и любым другим.

Во-вторых, нужно упомянуть про обработку cookie. Данная конфигурация не позволяет залогиненным пользователям видеть кэшированные версии компонентов WordPress, что помогает осуществлять дебаггинг и предпросмотр.

Наконец, мы используем произвольный заголовок (X-Cache), который равняется либо HIT, либо MISS, поэтому вы можете легко проводить отладку и видеть, передал ли Varnish кэшированную версию (HIT) или нет.

Как в случае с Pound, нужно провести рестарт Varnish, чтобы загрузить новую конфигурацию: $ service varnish restart.

Nginx и mod_pagespeed

Итак, мы почти добрались до конца. Заключительный фрагмент нашего пазла – это Nginx. Поскольку мы не использовали менеджер пакетов для его установки, нам нужно будет сначала выполнить init script, который зарегистрирует Nginx в качестве daemon (программа или процесс, который запускается в фоновом режиме). Чтобы сделать это, выполним следующие команды:

$ cd /etc/init.d
$ wget https://raw.githubusercontent.com/JasonGiedymin/nginx-init-ubuntu/master/nginx
$ chmod +x nginx
$ update-rc.d nginx defaults

Код приведет к загрузке скрипта инициализации, сделает его исполнимым (поскольку это, по существу, программа) и зарегистрирует его в операционной системе – таким образом, он сможет запуститься во время начальной загрузки.

Как только это будет сделано, мы должны будем настроить Nginx, отредактировав файл /usr/local/nginx/conf/nginx.conf. Найдите первый блок server в файле и замените его код на следующий (мы отталкиваемся от предположения, что ваша система WordPress установлена в /srv/www/yourdomain.com):

server {
        listen       8080;
        server_name  test.com;

        root /srv/test.com;
        index index.php index.html;

        location / {
                try_files $uri $uri/ /index.php?$args;
        }

        location ~ .php$ {
                try_files $uri /index.php;
                include fastcgi_params;
                fastcgi_pass unix:/var/run/php5-fpm.sock;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_index index.php;
        }

        pagespeed On;
        pagespeed ModifyCachingHeaders on;

        # Needs to exist and be writable by nginx.
        pagespeed FileCachePath "/var/cache/ngx_pagespeed/";
        pagespeed LoadFromFile "https://yourdomain.com" "/srv/yourdomain.com";
        pagespeed MapOriginDomain "http://localhost" "https://yourdomain.com";
        pagespeed EnableFilters rewrite_css,combine_css,trim_urls;
                pagespeed DisableFilters sprite_images,convert_jpeg_to_webp,convert_to_webp_lossless;

}

Верхняя половина – это просто базовая конфигурация для нашего виртуального хоста, который включает перезапись и делегирует PHP-обработку PHP-FPM.

Здесь нам интересна будет часть pagespeed. После ее включения нам нужно правильным образом задать URL-адреса, чтобы mod_pagespeed знал, где применять свою «магию». Затем мы включаем/отключаем разные фильтры; это уже зависит от ваших требований, как именно mod_pagespeed должен изменять/оптимизировать ваш HTML. Я предпочитаю те фильтры, которые автоматически объединяют CSS и Javascript файлы, оптимизируют изображения и переименовывают их в соответствии со временем их последнего изменения.

Есть одно предостережение: убедитесь в том, что вы отключили фильтр convert_jpeg_to_webp. Название фильтра говорит о том, что он делает. Проблема с ним возникает из-за использования Varnish. Если самый первый посетитель вашего сайта будет использовать Chrome, Varnish отправит запрос к Nginx, и тот передаст изображения в формате WEBP, забив ими кэш. Все последующие посетители получат эту кэшированную версию, однако WEBP не слишком широко поддерживается браузерами, в итоге пользователи с другими браузерами могут не увидеть изображений!

Теперь настало время проверить, были ли наши усилия плодотворными; сделайте рестарт Nginx для загрузки новой конфигурации ($ service nginx restart) и посетите сайт. О да! Скорее всего, вы увидели совсем не то, что ожидали, верно? Произошло это по той причине, что Varnish уже был запущен, и, скорее всего, передал ошибочную версию, поскольку вы посещали сайт ранее. Исправить это легко: просто очистите весь кэш Varnish ($ varnishadm «ban req.url ~ /»). В итоге вы увидите корректный вывод:

Чтобы проверить корректность, вы можете проинспектировать ответ локально: $ curl -i https://yourdomain.com. В ответе вы должны видеть два заголовка: X-Cache и X-Page-Speed, которые сигнализируют о том, что вы получили в свое распоряжение веб-сайт, работающий гораздо быстрее!

Что по поводу тестов производительности?

Мне задавали вопросы по поводу сравнительных тестов производительности, однако в данном случае я не считаю, что это имеет смысл. Есть много разных тестов Varnish, однако mod_pagespeed связан не только со скоростью (несмотря на свое название). Как следует из его документации, он может делать гораздо больше, к примеру, проводить оптимизацию вывода вашего сайта с позиций конкатенации файлов, их минификации и обслуживания.

Источник: wptavern.com

Блог про WordPress
Комментарии: 9
  1. Дуо

    «…мы используем Better WP Varnish»
    Читаю и жду, когда же они начнут ставить плагины в свой WP… ну, чтобы не связываться с… плагинами, очевидно :)

    Дмитрий, спасибо за статью.

    1. Дмитрий (автор)

      Да, пожалуйста :)

  2. italmebeli.ru

    Метод трудоёмкий, проще воспользоваться плагином или взять хостинг получше.

  3. Александр

    Нужно попробовать, последнее время блог стал тормозить временами, не смотря на то что опубликовано всего лишь 10 статей. Печально даже подумать что может быть дальше

  4. Petrozavodsky

    Ну это забавная матрешка varnish, чтоб не ставить плагины pound чтоб pound чтоб ещё работал SSL
    Можно было тогда уже поставить апатч чтоб потому что на него легче модулем включить page speed потом потом на всякий случай перед ним nginx и дальше varnish, pound , что то ещё … Cloud flare сверху :-D

  5. Елена Маая

    А почему мы просто не включить кеширование (как статики так и PHP запросов) в самом NGINX?

  6. Эдвард

    Здравствуйте, Дмитрий! Занимаюсь ускорением сайта средствами сервера на VDS. Столкнулся с проблемой: при включении режима Fast CGI все кроме главной страницы выдают 404 ошибку. Покопался в Интернете и попробовал сделать постоянные ссылки по умолчанию. Это помогло и сайт работает, но ссылки ?p=123, как известно, не совсем хороши для SEO. Есть ли возможность заставить работать Fast CGI с «латинизированными» ссылками?

    1. Дмитрий (автор)

      Здравствуйте!

      В конфиг надо добавить:

      
      location / {
                  try_files $uri $uri/ /index.php?$args; # permalinks
              }
      
      
      1. Дмитрий (автор)

        И еще ссылка на документацию:

        https://www.nginx.com/resources/wiki/start/topics/examples/phpfcgi/

        +

        Проверьте, есть ли у вас в файле /etc/nginx/fastcgi_params следующие строки:

        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;

Добавить комментарий

Получать новые комментарии по электронной почте.