WordPress高速化: WordPressをnginxで動かす

高速化

前回は、Debian 6.0(Squeeze)で構築したサーバにnginxを導入して、PHPを使ったWebページ(WordPress以外)を表示できるようにしました。

今回はついにWordpressをnginxに引っ越してみます。

なお、Apacheで運用しているWebサイトをキープするため、nginxはポート8080を使うようにします。nginxの動作確認ができたらApache2を停止し、nginxをポート80に割り当てる予定です。

Webサイトの構成

私は一つのWebサーバに複数のドメインを持たせて、WordpressでWebサイトを構築しています。一つがこのscratchpad.jpです。もう一つは全然まだ手をつけていないのですが、仮にblog.example.comとしておきます。

このためWordpressは「マルチサイト」という形態になります。

私の場合はこの二つのWordpressサイト用に次のディレクトリ・シンボリックリングがあります。

  • scratchpad.jp
    • /www/scratchpad.jp:
      /usr/share/wordpressへのシンボリックリンク
    • /www/uploads/scratchpad.jp:
      WordPressサイトのデータ格納用
    • /var/lib/wordpress/blogs.dir/scratchpad.jp:
      /www/uploads/scratchpad.jpへのシンボリックリンク
  • blog.example.com
    • /www/blog.example.com:
      /usr/share/wordpressのシンボリックリンク
    • /www/uploads/blog.example.com:
      WordPressサイトのデータ格納用
    • /var/lib/wordpress/blogs.dir/blog.example.com:
      /www/uploads/blog.example.comへのシンボリックリンク

なおDebianでは/usr/share/wordpress/wp-content/blogs.dirは/var/lib/wordpress/blogs.dirへのシンボリックリンクになっています。

それぞれのサイトにアクセスがあった場合に、nginxが適切なファイル・ディレクトリを見に行くように設定する必要があります

参考情報

「nginx wordpress」で検索するといろいろなウェブサイトが出てきました。

英語ですが、下記のページはnginxの公式サイト(?)と情報とWordpressの公式サイト(?)の情報なので、正確なことが期待できます。両方を見比べて自分の環境に合ったほうを適応していきます。

共通の設定ファイル

ここのWordpressサイトの設定をする前に、「Nginx (codex.wordpress.org) 」を参考に全サイトで共有につかう設定ファイルを用意します。

まず/etc/nginx/globalというディレクトリを作り、そのなかに下記のファイルを作っていきます。

  • restrictions.conf
# Global restrictions configuration file.
# Designed to be included in any server {} block.

# Only allow these request methods
# Do not accept DELETE, SEARCH and other methods
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
    return 444;
}

location = /favicon.ico {
    log_not_found off;
    access_log off;
}

location = /robots.txt {
    allow all;
    log_not_found off;
    access_log off;
}

# Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
# Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
location ~ /\. {
    deny all;
#     access_log off;
#     log_not_found off;
}

# Deny access to any files with a .php extension in the uploads directory
# Works in sub-directory installs and also in multisite network
# Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
location ~* /(?:uploads|files)/.*\.php$ {
    deny all;
}

# http://www.cyberciti.biz/tips/linux-unix-bsd-nginx-webserver-security.html
## Start: Size Limits & Buffer Overflows ##
client_body_buffer_size 1K;
client_header_buffer_size 1k;
client_max_body_size 25M;
large_client_header_buffers 2 1k;
## END: Size Limits & Buffer Overflows ##

## Deny certain Referers ###
if ( $http_referer ~* (babes|forsale|girl|jewelry|love|nudit|organic|poker|porn|sex|teen) )
{
    return 403;
}
  • wordpress.conf
# WordPress single blog rules.
# Designed to be included in any server {} block.

# This order might seem weird - this is attempted to match last if rules below fail.
# http://wiki.nginx.org/HttpCoreModule
location / {
    try_files $uri $uri/ /index.php?$args;
}

# Add trailing slash to */wp-admin requests.
rewrite /wp-admin$ $scheme://$host$uri/ permanent;

# Directives to send expires headers and turn off 404 error logging.
location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
    access_log off; log_not_found off; expires max;
}

# Uncomment one of the lines below for the appropriate caching plugin (if used).
#include global/wordpress-wp-super-cache.conf;
#include global/wordpress-w3-total-cache.conf;

# Pass all .php files onto a php-fpm/php-fcgi server.
location ~ \.php$ {
# Zero-day exploit defense.
# http://forum.nginx.org/read.php?2,88845,page=3
# Won't work properly (404 error) if the file is not stored on this server, which is entirely possible with php-fpm/php-fcgi.
# Comment the 'try_files' line out if you set up php-fpm/php-fcgi on another machine. And then cross your fingers that you won't get hacked.
    try_files $uri =404;

    fastcgi_split_path_info ^(.+\.php)(/.+)$;
#NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini

    include fastcgi_params;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
#     fastcgi_intercept_errors on;
    fastcgi_pass php;
}
  • wordpress-ms.conf
# WordPress multisite subdirectory/domain rules.
# Designed to be included in any server {} block.

# This order might seem weird - this is attempted to match last if rules below fail.
# http://wiki.nginx.org/HttpCoreModule
location / {
    index index.php;
    try_files $uri $uri/ /index.php?$args;
}

# Directives to send expires headers and turn off 404 error logging.
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
    expires 24h;
    log_not_found off;
}

location ~ ^/[_0-9a-zA-Z-]+/files/(.*)$ {
    try_files /wp-content/blogs.dir/$blogid/files/$2 /wp-includes/ms-files.php?file=$2 ;
    access_log off; log_not_found off; expires max;
}

#avoid php readfile()
location ^~ /blogs.dir {
    internal;
    alias /var/lib/wordpress/blogs.dir/$blogid ;
    access_log off; log_not_found off; expires max;
}

# Pass uploaded files to wp-includes/ms-files.php.
rewrite /files/$ /index.php last;

# For multisite: Use a caching plugin that creates symlinks to the correct subd
irectory structure to get some performance gains.
set $cachetest "$document_root/wp-content/cache/ms-filemap/${host}${uri}";
if ($uri ~ /$) {
    set $cachetest "";
}
if (-f $cachetest) {
# Rewrites the URI and stops rewrite processing so it doesn't start over and attempt to pass it to the next rule.
    rewrite ^ /wp-content/cache/ms-filemap/${host}${uri} break;
}

if ($uri !~ wp-content/plugins) {
    rewrite /files/(.+)$ /wp-includes/ms-files.php?file=$1 last;
}

# Uncomment one of the lines below for the appropriate caching plugin (if used).
#include global/wordpress-ms-wp-super-cache.conf;
#include global/wordpress-ms-w3-total-cache.conf;

# Rewrite multisite '.../wp-.*' and '.../*.php'.
if (!-e $request_filename) {
    rewrite /wp-admin$ $scheme://$host$uri/ permanent;
    rewrite ^/[_0-9a-zA-Z-]+(/wp-.*) $1 last;
    rewrite ^/[_0-9a-zA-Z-]+(/.*\.php)$ $1 last;
}

# Rewrite multisite /uploads/ -> /www/uploads/$blogid
rewrite ^/uploads/(.*)$ /wp-content/blogs.dir/$blogid/$1;
rewrite ^/(.*)(favicon.ico$) /wp-content/blogs.dir/$blogid/favicon.ico;
rewrite ^/sitemap.xml$ /wp-content/blogs.dir/$blogid/sitemap.xml;
rewrite ^/BingSiteAuth.xml$ /wp-content/blogs.dir/$blogid/BingSiteAuth.xml;

# Pass all .php files onto a php-fpm/php-fcgi server.
location ~ \.php$ {
# Zero-day exploit defense.
# http://forum.nginx.org/read.php?2,88845,page=3
# Won't work properly (404 error) if the file is not stored on this server, which is entirely possible with php-fpm/php-fcgi.
# Comment the 'try_files' line out if you set up php-fpm/php-fcgi on another machine. And then cross your fingers that you won't get hacked.
    try_files $uri =404;

    fastcgi_split_path_info ^(.+\.php)(/.+)$;
#NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini

    include fastcgi_params;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
#     fastcgi_intercept_errors on;
    fastcgi_pass php;
}

58~61行目は私の環境に合わせるために追加しました。
61行目でwp-uploads以下にアクセスがあったら/www/ブログ名/wp-content/blogs.dir/ブログ名以下を見に行くようにしています。シンボリックリンクの設定により、結果的には/www/uploads/ブログ名以下にアクセスすることになります。
62行目はfavicon.icoを/www/uploads/ブログ名/favicon.icoへ、62行目はsitemap.xmlを/www/uploads/ブログ名/sitemap.xmlへ、63行目はBingSiteAuth.xmlを/www/uploads/ブログ名/BingSiteAuth.xmlへアクセスするようにしています。

  • wordpress-ms-wp-super-cache.conf
# WP Super Cache rules.
# Designed to be included from a 'wordpress-ms-...' configuration file.

set $cache_uri $request_uri;

# POST requests and urls with a query string should always go to PHP
if ($request_method = POST) {
    set $cache_uri 'null cache';
}

if ($query_string != "") {
    set $cache_uri 'null cache';
}

# Don't cache uris containing the following segments
if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") {
    set $cache_uri 'null cache';
}

# Don't use the cache for logged in users or recent commenters
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in") {
    set $cache_uri 'null cache';
}

# START MOBILE
# Mobile browsers section to server them non-cached version. COMMENTED by default as most modern wordpress themes including twenty-eleven are responsive. Uncomment config lines in this section if you want to use a plugin like WP-Touch
#if ($http_x_wap_profile) {
#    set $cache_uri 'null cache';
#}

#if ($http_profile) {
#    set $cache_uri 'null cache';
#}

#if ($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)) {
#    set $cache_uri 'null cache';
#}

#if ($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-)) {
#    set $cache_uri 'null cache';
#}
#END MOBILE

# Use cached or actual file if they exists, otherwise pass request to WordPress
location / {
    try_files /wp-content/cache/supercache/$http_host/$cache_uri/index.html $uri $uri/ /index.php?$args ;
}
  • wordpress-ms-w3-total-cache.conf
#W3 TOTAL CACHE CHECK
set $cache_uri $request_uri;

# POST requests and urls with a query string should always go to PHP
if ($request_method = POST) {
    set $cache_uri 'null cache';
}
if ($query_string != "") {
    set $cache_uri 'null cache';
}

# Don't cache uris containing the following segments
if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") {
    set $cache_uri 'null cache';
}

# Don't use the cache for logged in users or recent commenters
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in") {
    set $cache_uri 'null cache';
}

## for single site
# Use cached or actual file if they exists, otherwise pass request to WordPress
#location / {
#    try_files /wp-content/w3tc/pgcache/$cache_uri/_index.html $uri $uri/ /index.php?$args ;
#}

## for multisite with subdirectories
#if ($request_uri ~* "^/([_0-9a-zA-Z-]+)/.*" ){
#    set $blog $1;
#}
#
#set $blog "${blog}.";
#
#if ( $blog = "blog." ){
#    set $blog "";
#}
#
# Use cached or actual file if they exists, otherwise pass request to WordPress
#location / {
#    try_files /wp-content/w3tc-$blog$host/pgcache$cache_uri/_index.html $uri $uri/ /index.php?$args ;
#}

## for multisite with subdomains/domain-mapping
location / {
    try_files /wp-content/w3tc-$host/pgcache/$cache_uri/_index.html $uri $uri/ /index.php?$args;
}

最後の二つはプラグイン(WP Super CacheかW3 Total Cache)を使用していない場合は不要ですが、一応作成しておきます。

個別の設定ファイル

それでは各Wordpressサイトの設定を行います。ここではscratchpad.jpの設定を記します。

設定ファイルは/etc/nginx/sites-availableにscratchpad-blogとして作成します。

map $http_host $blogid {
    default scratchpad.jp;
}

server {
    listen 80;
    server_name scratchpad.jp;
    root /www/scratchpad.jp;
    index index.php;

    error_log /var/log/nginx/scratchpad-error.log warn;
    access_log /var/log/nginx/scratchpad-access.log;

    include global/restrictions.conf;

# Additional rules go here.
    rewrite_log off;

# Only include one of the files below.
# include global/wordpress.conf;
    include global/wordpress-ms.conf;
}

動作確認

まずはWordpressサイトを有効化します。

sudo ngxensite scratchpad-blog

もちろん複数のWordpressサイトがある場合はそれぞれに対して実施します。

次に設定ファイルが正しいかどうかをチェックします。

sudo nginx –t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

問題がなければ設定ファイルを再読み込みします。

sudo /etc/init.d/nginx reload

ここまできたらWordpressサイトにアクセスしてみます。

本運用

動作確認に成功したら、/etc/nginx/sites-availableの全ファイルの「listen 8080」を「listen 80」に書き換え、その他の「:8080」という部分は削除します。

そしてapacheを停止させたあと、nginxの設定ファイルを再読み込みします。

sudo /etc/init.d/apache2 stop
sudo /etc/init.d/nginx reload

一発でうまくいけばよいのですが、何かおかしいところがある場合は急いでnginxの設定ファイルを直します。

私はブログに貼り付けた画像がいっさい表示されない問題が発生しました。

すべて不具合を修正したらApacheが起動しないように無効にしておきます。

sudo update-rc.d apache2 remove

SSLの設定

上記でWordpressで構築したサイトは見えるようになりますが、Wordpressの管理画面にSSLでログインするようにしている場合は、nginxがSSLを処理できないため失敗します。

そこで次のようなファイルを/etc/nginx/sites-availableにscratchpad-blog-sslとして作成します。

map $http_host $blogid {
    default scratchpad.jp;
}

server {
    listen 443;
    server_name scratchpad.jp;
    root /www/scratchpad.jp;
    index index.php;

    error_log /var/log/nginx/scratchpad-ssl-error.log warn;
    access_log /var/log/nginx/scratchpad-ssl-access.log;

    ssl on;
    ssl_certificate /etc/ssl/certs/scratchpad.jp.crt;
    ssl_certificate_key /etc/ssl/private/scratchpad.jp.key;

    include global/restrictions.conf;
    include global/wordpress-ms.conf;
}

そしてこの設定を有効にしたあとnginxに読み込ませます。

sudo ngxensite scratchpad-blog-ssl
sudo /etc/init.d/nginx reload

まとめ

これでnginxでWordpressを動かすことができました。

特に問題なければerror_logでnoticedとしているところはwarnに、「rewrite_log on」としている場合は「rewrite_log off」にすると良いでしょう。

次回は気になるパフォーマンスをapacheと比較してみたいと思います。