nginxベースの高速なWordPress環境をお名前.comのVPSで構築 5ページ

nginxのプロキシ設定とチューニング

 nginxでのWordPress動作が確認できたら、プロキシによるキャッシュを設定して、各種チューニングを行っていこう。最終的な設定ファイルはリスト6のようになる。プロキシを利用するには、このnginxの設定のほかに後述する2つの作業が必要となる。

リスト6 WordPress向けの/etc/nginx/conf.d/default.conf

root /var/www/wordpress;
index index.php;

proxy_cache_path  /var/cache/nginx/cache1 levels=1 keys_zone=cache1:128m;
proxy_cache cache1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_cache_valid 200 404 30m;

server {
        listen 80 default_server;
        gzip on;
        gzip_disable msie6;
        gzip_types text/css application/x-javascript;

        location ~ /\. {deny all; access_log off; log_not_found off; }
        location = /xmlrpc.php {deny all; access_log off; log_not_found off; }
        location = /robots.txt  { access_log off; log_not_found off; }
        location = /favicon.ico { access_log off; log_not_found off; }
        location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
                log_not_found off;
                proxy_pass http://unix:/var/run/nginx.sock;
        }

        set $do_not_cache 0;
        if ($uri ~* "\.php$") {
                set $do_not_cache 1;
        }

        set $proxy_cache_key "$scheme$proxy_host$request_uri";
        if ($http_user_agent ~* "iPhone") {
#               set $do_not_cache 1;
                set $proxy_cache_key "iphone::$proxy_cache_key";
        }

        if ($http_cookie ~ "(wordpress_logged_in_|comment_author_)(.*)") {
#               set $do_not_cache 1;
                set $proxy_cache_key "$2::$proxy_cache_key";
        }

        location / {
                proxy_no_cache $do_not_cache;
                proxy_cache_bypass $do_not_cache;
                proxy_cache_key $proxy_cache_key;
                proxy_pass http://unix:/var/run/nginx.sock;
        }
}

server {
        listen unix:/var/run/nginx.sock;
        try_files $uri $uri/ /index.php;

        location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
                expires 24h;
                log_not_found off;
        }

        location ~* \.php$ {
                fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include fastcgi_params;
#                fastcgi_intercept_errors on;
        }
}

 設定ファイルは冒頭にあるプロキシ関連の各種設定と、2つの「server」設定で構成されている。

プロキシ各種設定

 プロキシ設定でポイントとなるのは、「proxy_cache_path」と「proxy_cache_valid」である。「proxy_cache_path」ではキャッシュしたファイルが保管されるパスと、キャッシュゾーンの名前、容量を指定する。ここでは「cache1」のプロキシキャッシュ保管場所として「/var/cache/nginx/cache1」を指定し、その容量は128MB、キャッシュをファイルで格納する際にキーを1階層のディレクトリごとに分けて格納する、という設定を行っている。

「proxy_cache」では利用するキャッシュゾーンの名前を指定する。「proxy_cache_valid」はステータスコードごとにどのくらいの間キャッシュを保持するかを設定するものだ。ここではステータスコードが通常応答の「200」と、ファイルが存在しない「404」の場合30分間キャッシュを保持する、という設定になる。

プロキシサーバー設定

 中段の「server」設定はプロキシサーバーとしての設定である。

 まず、「gzip」設定によってtext/plain(通常のHTML)の応答は圧縮されて送出される。「gzip_disable」は圧縮送信を行わないクライアントを指定する設定だ。ここで指定している「msie6」はgzip圧縮に対応していないいくつかのWebブラウザに対応するエイリアスである。標準のHTMLだけでなくJavaScriptとCSSも圧縮送信したほうがレスポンスが良くなるので、「gzip_types」でその設定を加えている。

 続く一連の「location」設定は、特定のアクセスを禁止することでサーバー負荷を下げるものだ。1行目の設定はドットファイルに対するアクセスを禁止するもので、WordPress環境で自動生成される/.htaccessファイルへのアクセスを禁止するのが主眼である。このアクセスはログにも記録しない。2行目の「/xmlrpc.php」の設定は、pingbackを利用してない場合の無駄な負荷を防ぐ設定としてある。この設定を行うとpingbackが機能しなくなってしまうので、pingbackを利用する場合はこの行をコメントアウトしておく必要がある。

 最後の「location ~* \.(js|css|png|jpg|jpeg|gif|ico)$」は、画像ファイルなどへのアクセスをその後に記述したキャッシュ設定を経由せずにすぐにプロキシに通す設定である。以下の設定ではログイン/非ログインやユーザーエージェントによって切り分けして複数キャッシュを行っているが、画像ファイルなどは複数キャッシュされてしまうと無駄が発生してしまう。なお、「~*」の記号は正規表現マッチで大文字小文字を区別しないというものであり、「~」の場合は大文字小文字を区別する。

 次の「set」から続く行ではキャッシュ方法を制御している。内容は以下のとおりだ。

  1. .phpファイルへのアクセスはキャッシュしない
  2. ユーザーエージェントがiPhoneだったらキャッシュのキーを変えて別格納する
  3. ログイン状態もしくはコメントを記入したことがあれば、その情報ごとにキャッシュを分ける

 一般的に利用されるパーマリンク設定をした場合、.phpファイルへ直接アクセスがあるのは基本的に管理者作業しかないので、この部分はキャッシュしないようにしている。

 2.の設定は、WordPressではユーザーエージェントに対応してデザインを変更する機能があり、その機能への対応である。「$proxy_cache_key」に任意の文字を加えることで別のキーでページをキャッシュできる。また、WordPressにはログイン状態やコメントを記入したことがある場合に名前などの情報を画面上に表示する機能もあり、3.の設定ではこれに対応してキャッシュ分けが行えるようにしている。

 なお、利用しているプラグインなどによってはこれらがうまく機能しないこともあるので、その場合はコメントアウトしてある「set $do_not_cache 1;」の行のほうを有効にし、キャッシュを行わないようにすれば問題が解決する可能性がある。

 最後に「location / {」のブロックにて、直上で組み立てたプロキシ設定でキャッシュを行う。それぞれの意味は表1のようになっている。

表1 キャッシュ設定の意味
設定 意味
proxy_no_cache キャッシュしない
proxy_cache_bypass キャッシュがあっても無視
proxy_cache_key キャッシュのキー(添え字+URL)

HTTPサーバー部分

 後半の「server」設定は、基本的にはリスト5と同様のHTTPサーバーとしての設定である。待ち受けポートはUNIXドメインソケットとして「/var/run/nginx.sock」に変更し、JavaScriptやCSSのファイルや画像ファイルに対しては「expires」を設定することでExpireヘッダーなどを送出している。これによってWebブラウザ側でキャッシュが行われ、サーバー負荷の軽減ならびにユーザー側でのレスポンス向上が期待できる。また、「log_not_found off」設定で間違ったURLでのアクセスに対してログを記録しないようにし、不正アクセスによる負荷を軽減している

404ページをキャッシュする

 WordPressでは404ページの処理が比較的重い。そのため、不正アタックのようなアクセスや不正URLへのアクセス、リンク切れURLへのボットアクセスも負荷となってしまう。404ページをどのようにキャッシュしておくかが負荷軽減のポイントとなるが、WordPressが出力する404ページのHTTPヘッダーでは「Cache-Control:no-cache」などのキャッシュを禁止するヘッダーが送出され、プロキシサーバーではページをキャッシュできない。そこで、プロキシ構成にする場合はWordPressのコードを変更し、404ページでこのヘッダーを送出しないよう修正しておく。具体的には、wp-includes/class-wp.phpファイルの506行目をコメントアウトする。

リスト7 /var/www/wordpress/wp-includes/class-wp.phpの修正箇所

503                 // Guess it's time to 404.
504                 $wp_query->set_404();
505                 status_header( 404 );
506 //              nocache_headers();
507         }

 このnocache_headers()をコメントアウトすることで「Cache-Control:no-cache, must-revalidate, max-age=0」などのヘッダーが送出されず、nginxのプロキシでキャッシュされるようになる。

プロキシキャッシュを削除できるようにする

 記事を投稿したり修正したりした後、キャッシュを削除してすぐに内容を更新したい場合も多いだろう。しかし、nginxにはキャッシュを削除する機能がない。キャッシュを削除するには、キャッシュを保管しているディレクトリの中身を削除するという運用方法になる。VPSにログインしてシェルから削除を実行することも可能ではあるが、Webブラウザから削除できるように、以下のファイルを/var/www/wordpress/wp-admin/以下に設置しておくと便利だ。

リスト8 /var/www/wordpress/wp-admin/clear-cache.php

<?php
require_once( './admin.php' );
system( "/bin/rm -rf /var/cache/nginx/cache1/*" );
?>
clear cache

 ここでは「require_once( ‘./admin.php’ );」でWordPressによるアクセス制限を有効にし、続けてsystem関数でrmコマンドを発行している。「http://<VPSのURL>/wp-admin/clear-cache.php」といったURLにWebブラウザでアクセスしてこのスクリプトを実行すれことで、プロキシのキャッシュを削除できる。

nginx+WordPressで手軽に高速化が可能

 以上の作業を行った後、nginxを再起動すれば図1で示した環境の完成である。

# /etc/init.d/nginx restart

 先ほどと同じ条件(約16.5KB、圧縮後約6.5KB)によるabによるベンチマークを実施すると、

702.26リクエスト/秒

 という結果となった。一度キャッシュされたページに対してはボトルネックとなっているPHPでの処理が実行されず、残りはファイル転送だけなので非常に高速である。

 なお、ネットワークを介さないサーバー内部からのab実行では5000~10000リクエスト/秒という結果となり、この場合は送出ファイルサイズが処理能力を決める主要因となる傾向になっている。たとえば、デフォルトテーマのTwentyElevenのstyle.css(約53KB、圧縮後約14.5KB)で220リクエスト/秒ほど、WordPressデフォルトのトップページ(約8.5KB、圧縮後約3KB)で1300リクエスト/秒ほどといった数字となった。

 OSの再インストールからの手順を動作確認をしながら解説してきたためそれなりの文量となったが、nginxのWordPress環境構築は比較的単純な手順で行える。サイトのボリュームによってチューニングポイントは多少異なるが、基本的な設定だけで手軽に高速な環境が運用できるので、リソースが少ないVPSでもどこまで耐えられるか、ぜひ一度試していただきたい。