work.log

エンジニアの備忘録的ブログ

NginxでWordPressを使う時の設定をまとめてみた

投稿:2015-03-10 18:57  更新:

NginxWordPressを使う時の設定は色々な書き方が出来ますが、ネットの情報は断片的な気がしたのでまとめてみました。

可読性良く設定をしたいのと、WordPress向けにこれだけ書けば不自由はないよ!っていうのを目的に設計してます。

内容を見直し2022年12月にアップデートしました。

スポンサーリンク

環境

各種スペックはこんな感じでやってます。

  • サーバはVPS (CPU: 3~4Core, Mem: 2GB~4GB 程度)
  • CentOS 7, Rocky Linux 9
  • Nginx: 1.22.1
  • PHP (php-fpm): 8.1.13

CentOS 7は2024年にサポート期限が切れるので、今後は後継のRocky Linuxを使う前提が良いように考えています。Rocky Linux以外だとAlmaLinuxという選択肢もありますが、基本同じようにインストール出来ます。

アップデートに追随しないと後々面倒になるので、NginxとPHPは常に新しい物を使うようにします。また、特別な理由が無い限りSSL運用を前提とした設定にします。

DBについてはこの記事では触れませんが、WordPressの動作要件を満たしたMySQLかMariaDBを適時インストールしてください。

Nginxのインストール

Nginxはyumやdnfを使うか、ソースからインストールします。設定内容はどちらも変わらないですが、最新版を利用したい場合はソースを使うと良いと思います。

また、Nginxのユーザーは nginx でなく www で運用するようにしてますので、このようなユーザーアカウントを作成しています。ここはお好みで調整して下さい。

# groupadd -g 80 www
# useradd -d /home/www -s /sbin/nologin -g www -u 80 www

Nginxをdnfでインストールする方法

dnf (またはyum) でNginxをインストールする場合はこのように。

# dnf -y install nginx

Nginxをソースからインストールする方法

ソースからNginxをインストールする時はこのようなオプションでコンパイルしています。

# ./configure \
--prefix=/etc/nginx \
--sbin-path=/usr/sbin/nginx \
--conf-path=/etc/nginx/nginx.conf \
--pid-path=/var/run/nginx.pid \
--lock-path=/var/run/nginx.lock \
--error-log-path=/var/log/nginx-error.log \
--user=www \
--group=www \
--http-client-body-temp-path=/home/www/tmp/client_body_temp \
--http-fastcgi-temp-path=/home/www/tmp/fastcgi_temp \
--http-proxy-temp-path=/home/www/tmp/proxy_temp \
--http-scgi-temp-path=/home/www/tmp/scgi_temp \
--http-uwsgi-temp-path=/home/www/tmp/uwsgi_temp \
--http-log-path=/var/log/nginx-access.log \
--with-pcre \
--with-http_gzip_static_module \
--with-http_image_filter_module \
--with-http_stub_status_module \
--with-http_sub_module \
--with-http_mp4_module \
--with-http_ssl_module \
--with-http_realip_module \
--with-http_slice_module \
--with-http_v2_module

# make ; make install

PHPとphp-fpmのインストール

PHPはremiリポジトリを使ってdnfでインストールします。

違うバージョンのPHPが入っている場合は先にアンイストールしておきます。

Rocky Linux

# dnf -y remove php-*
# dnf -y module reset php
# dnf -y install http://rpms.remirepo.net/enterprise/remi-release-9.rpm
# dnf -y module enable php:remi-8.1
# dnf -y install php php-fpm php-mbstring php-mysql php-devel php-gd php-pecl-zendopcache php-ldap php-pecl-ssh2 php-intl php-pear php-bcmath php-zip

CentOS 7

# yum -y remove php-*
# rpm -ivh https://rpms.remirepo.net/enterprise/remi-release-7.rpm
# yum -y install --enablerepo=remi-php81 php php-fpm php-mbstring php-mysql php-devel php-gd php-pecl-zendopcache php-pear php-bcmath php-zip

php-fpm.confのサンプル

メモリ4GBのVPSで実際に運用している設定内容をそのまま載せます。

場所は /etc/php-fpm.conf です。

[global]
pid = /var/run/php-fpm/php-fpm.pid
error_log = /var/log/php-fpm/error.log
log_level = notice
emergency_restart_threshold = 0
emergency_restart_interval = 0
process_control_timeout = 0
daemonize = yes
events.mechanism = epoll

[www]
listen = /var/run/php-fpm/php-fpm.sock  
listen.backlog = -1

listen.owner = www
listen.group = www
listen.mode = 0666

user = www
group = www

pm = dynamic
pm.max_children =  30
pm.start_servers = 10
pm.min_spare_servers = 10
pm.max_spare_servers = 25
pm.max_requests = 30
pm.status_path = /phpfpm_status

request_slowlog_timeout = 300
request_terminate_timeout= 300
slowlog = /var/log/php-fpm/$pool-slow.log

max_childrenの数はメモリと相談ですが、php-fpmプロセス全体のメモリ量が搭載メモリ以下、max_childrenの数はMySQLのmax_connectionsと同等かそれ以下になるように設定しておきます。

max_requestsは子プロセスが応答する最大リクエスト回数で、メモリリーク対策で少なめにしています。

プロセス数が少ないように見えますが、Nginxでキャッシュを作る前提だとこの内容で一日30万PV位は問題なく処理できるので問題無しです。

Nginxの設定

全般的な設定は nginx.conf に、それ以外は cond.d/ 配下に設定するようにNginxを構成します。

Nginxデフォルト設定のファイルを除くと、各種設定ファイルはこのように配置します。

/etc/nginx/
|
|-- conf.d/
|   |-- cache_path.conf (キャッシュの設定)
|   |-- common.conf (汎用的な設定)
|   |-- virtual.conf (サイトの設定)
|   `-- wordpress.conf (WordPressの設定)
|
|-- ssl/
|   `-- dhparam.pem (DH鍵交換に使用するパラメータファイル)
|
`-- nginx.conf (メイン設定)

conf.d と ssl ディレクトリは無いので先にmkdirしておきます。

# mkdir /etc/conf.d
# mkdir /etc/nginx/ssl
## ssl_dhparam で設定するパラメータファイルを作成 (時間がかかります)
# openssl dhparam 2048 -out /etc/nginx/ssl/dhparam.pem

nginx.conf (全体的な設定)

Nginxのメインファイルはこのように設定します。

user www www;
pid /var/run/nginx.pid;

## auto を指定すれば最大限利用できる CPU コア数を勝手に設定してくれる
## 指定したいならサーバの CPU コア数以下で設定する
worker_processes auto;
worker_rlimit_nofile 4096;

events {
	use epoll;
	multi_accept on;
	worker_connections 1024;
}

http {

	include mime.types;
	default_type text/plain;

	charset            utf-8;
	sendfile           on;
	tcp_nopush         on;
	tcp_nodelay        on;
	server_tokens      off;
	keepalive_requests 100;
	keepalive_timeout  3;

	server_names_hash_bucket_size 64;
	types_hash_max_size 2048;
	client_body_buffer_size 64k;
	client_body_temp_path /home/www/tmp/client_body_temp 1 2;

	## gzip圧縮を有効化
	gzip            on;
	gzip_vary       on;
	gzip_proxied    any;
	gzip_comp_level 6;
	gzip_types      text/plain text/css text/xml text/javascript
					application/json application/javascript application/x-javascript
					application/xml application/rss+xml application/atom+xml
					image/svg+xml;

	## SSL周り
	ssl_session_timeout 30m;
	ssl_session_cache   shared:SSL:10m;
	ssl_session_tickets off;
	ssl_protocols TLSv1.2 TLSv1.3;
	ssl_prefer_server_ciphers on;
	ssl_dhparam /etc/nginx/ssl/dhparam.pem;
	ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256;

	## fastcgi
	fastcgi_buffers         8 64k;
	fastcgi_buffer_size     64k;
	fastcgi_connect_timeout 60;
	fastcgi_send_timeout    60;
	fastcgi_read_timeout    300;

	## proxy (WordPressだけだったらここは不要)
	proxy_connect_timeout 60;
	proxy_send_timeout    60;
	proxy_read_timeout    120;
	proxy_http_version    1.1;
	proxy_cache_bypass    $http_upgrade;
	proxy_set_header      Upgrade            $http_upgrade;
	proxy_set_header      Connection         "upgrade";
	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_set_header      X-Forwarded-Proto  $scheme;
	proxy_set_header      X-Forwarded-Port   $server_port;
	proxy_temp_path       /home/www/tmp;

	## cache_pathについては別ファイルで設定する
	include conf.d/cache_path.conf;


## デフォルトのサーバ
server {

	listen *:80 default_server;
	root   /home/www/htdocs;

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

	index index.html;
	include conf.d/common.conf;

}

    ## バーチャルドメインの設定
	include conf.d/virtual.conf;

}

common.conf (汎用的な設定)

このファイルには毎回使うような汎用的な設定を書きます。

## .htpasswdとか . から始まるファイルへのアクセスは404で応答
## 403だとそのファイルがあるのが外からわかってしまう
location ~ /\. {
	return 404;
}

## ファイルが無くてもエラーログを出さない
location ~ /(favicon.ico|apple-touch-icon-*) {
	log_not_found  off;
	access_log  off;
}

## Let’s Encryptを使う時に必要
location ^~ /.well-known/acme-challenge/ {
	root /home/www/htdocs;
}

location = /.well-known/acme-challenge/ {
	return 404;
}

acme-challengeの部分はSSLで Let’s Encrypt を使う時の設定なので、ディレクトリとかは環境に合わせ変更してください。

ドメイン名毎に都度設定は面倒なので /home/www/htdocs に共通のドキュメントルートを作り、そこへLet’s Encryptの認証アクセスを振り分けて運用するようにしています。

cache_path.conf (キャッシュの設定)

Nginxがキャッシュを保存するパスとキャッシュ周りの設定をここに書きます。

## php-fpmでキャッシュを作る時はこれ
fastcgi_cache_path /home/www/cache/worklog.be levels=1:2 keys_zone=worklog:18m inactive=5m max_size=10g;

## proxy経由でキャッシュを作る時はこっち
proxy_cache_path /home/www/cache/proxy.worklog.be levels=1:2 keys_zone=proxy_worklog:18m inactive=5m max_size=10g;
levels = キャッシュを作成するディレクトリの階層レベル
keys_zone = キーゾーン名とメモリサイズ
inactive = キャッシュの有効期限
max_size = 最大キャッシュサイズ

keys_zone に設定しているパラメータは下記を参考に必要な分を計算して設定してます。

リンクはFreeBSDの場合についてだけど、CentOSとFreeBSDの混在の環境で共有し運用しているので、良くわからないがこれでよしとしてます😇

wordpress.conf (WordPressの汎用設定)

WordPressの設定は複雑なのでここに繰り返し使えるようまとめます。

index index.php;
error_page 404 /index.php?error=404;

set $is_mobile '';

## スマートフォン用のキャッシュを作る為の判定処理
## WordPress標準の wp_is_mobile() 関数と同じ判定処理
if ($http_user_agent ~* '(Mobile|Android|Silk/|Kindle|BlackBerry|Opera\sMini|Opera\sMobi)') {
	set $is_mobile 'mobile.';
}

## GET メソッド以外はキャッシュを作成しない
if ($request_method != GET) {
	set $do_not_cache 1;
}

## キャッシュして欲しくないファイルは除外
if ($request_uri ~* '/(wp-admin/|wp-login.php|wp-cron.php|xmlrpc.php|\??feed|wp-json|sitemap.xml)') {
	set $do_not_cache 1;
}

## ログイン済みのユーザー等、Cookie を持っていたらキャッシュを使わない
if ($http_cookie ~* 'comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in') {
	set $do_not_cache 1;
}

## 画像ファイル等はブラウザキャッシュを効かせる (60日)
location ~* \.(jpg|jpeg|gif|png|css|js|swf|ico|pdf|svg|eot|ttf|woff)$ {
	expires 60d;
	access_log off;
}

## uploadsディレクトリでのPHP実行を禁止
## 万が一、バックドアを仕掛けられた時の対策用に…
location ~ /wp-content/uploads/.+\.php$ {
	return 403;
}

## リクエストは index.php に投げる
location / {
	try_files $uri $uri/ /index.php?$args;
}

## NginxとPHP-FPMはソケットで繋ぐ
## HTTPステータスコードによって各キャッシュの有効期限を制御する
location ~ \.php {

	try_files $uri /index.php;

	include fastcgi_params;
	fastcgi_pass  unix:/var/run/php-fpm/php-fpm.sock;
	fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;

	fastcgi_no_cache     $do_not_cache;
	fastcgi_cache_bypass $do_not_cache;
	fastcgi_cache        $keys_zone;
	fastcgi_cache_key    $is_mobile$scheme://$host$request_uri;
	fastcgi_cache_valid  200 5m;
	fastcgi_cache_valid  301 302 1h;
	fastcgi_cache_valid  404 1m;
	fastcgi_cache_valid  any 1s;

}

HTTPステータスコードが200 OKなら5分のキャッシュを作るようにしています。ここを長くする場合は cache_path.conf のinactiveの値も一緒に調整します。

キャッシュの有効期限は長いほうがサーバ負荷も減りますが、メディア運用の立場で考えると5分位が何だかんだで都合が良かったりします。

virtual.conf (バーチャルドメインの設定)

ここではバーチャルドメインの設定を書きます。

## HTTPでのアクセスはHTTPSへ301リダイレクト
server {
	listen      80;
	server_name worklog.be www.worklog.be;
	return 301 https://$host$request_uri;
}

server {
	listen      443 ssl http2;
	server_name worklog.be www.worklog.be;
	root        /home/blog/worklog.be/htdocs;

	access_log /home/blog/worklog.be/log/nginx-access.log combined;
	error_log  /home/blog/worklog.be/log/nginx-error.log warn;

	## サイトのSSL証明書
	ssl_certificate     /etc/letsencrypt/live/worklog.be/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/worklog.be/privkey.pem;

	## キャッシュの設定:有効 -> 0 無効 -> 1
	set $do_not_cache 0;

	## キーゾーン名
	set $keys_zone worklog;

	## 必要な設定ファイルを読み込み
	include conf.d/common.conf;
	include conf.d/wordpress.conf;
}

設定ファイルを細かく分けたので、バーチャルドメインはこのようにシンプルに設定できます。

可読性が悪いと設定ミスをする原因になるのでよく触る部分程シンプルに保つように心掛けています。

Let’s EncryptでSSL証明書を取得する方法等はこちらを参照してください。

NginxとPHP-FPMのデーモンを起動

最後は設定に問題が無いかを確認してNginxとPHP-FPMのデーモンを起動します。

設定のチェック

Warningが出ない事を確認してからデーモンを起動します。

# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

# systemctl start nginx
# systemctl start php-fpm
# systemctl enable nginx php-fpm

起動後は ps コマンドでプロセスが動いているかを確認し終了です。

WordPressをNginxで運用する為の設定は以上で、あとはNginxを運用してみて気付いた点等の所感を書きます。

SELinuxを有効にしていると出力されるエラー

SELinuxが有効になっているとNginxのエラーログに下記のエラーが出てPHPが実行出来ないので注意。

FastCGI sent in stderr: “Primary script unknown” while reading response header from upstream, client: 192.168.0.1, server: worklog.be, request: “GET /index.php HTTP/2.0”, upstream: “fastcgi://unix:/var/run/php-fpm/php-fpm.sock:”, host: “worklog.be”

fastcgiの設定に問題があっても出力されるエラーですが、設定に問題が無いようならSELinuxも確認しておきましょう。

WordPressの運用はNginxとApacheどちらが良いか

WebサーバでNginxとApacheをそれぞれ運用してきてどちらが良いかっていう意見を書きます。

Apache → Nginxという流れで来ていますが、Apacheはpreforkを使う前提で言うとPHPを動かすならApache、画像や CSS, JS 等の静的ファイルならNginxというのがパフォーマンスが良い気がします。

Nginxはスレッド処理なのでメモリ消費が少ないとは言いつつも、PHP-FPMを結局立ち上げるので管理しないといけないプロセスが増えるっていう点があります。

ただ、WordPress何かは毎回動的生成に拘る必要も無いページが殆どだと思うので、5分とかそういう短い時間でもいいかキャッシュを作ってしまえば断然Nginxが速い!と思ってます。

あとApacheのmod_rewriteが凄い苦手という事もあるので、その処理もまだわかりやすく書けるNginxに軍配という形です。

インフラエンジニアだったら両方を触れるようにしておかないといけませんが、それ以外だったらNginxを覚えて置けばいいんじゃないかなという感想です。

Nginxで作ったキャッシュの削除方法について

Nginxで作成されたキャッシュは、特に何もしなければ有効期限が切れるまで削除されず、クライアントにはキャッシュが返されます。

高頻度で更新するサイトで無ければキャッシュ期間を短くするだけでも十分対応可能かとは思いますが、WordPressは様々なフックが用意されていますので更新したらキャシュも同時に消すような機能をWordPressに実装すれば良いです。

WordPressの更新系フックを使ったサンプル処理

function nginx_cache_purge() {
	/* ここにNginxのキャシュを削除する処理 */
}

/* 新規投稿、更新等のステータス変更で削除処理が発動 */
add_action( 'new_to_publish', 'nginx_cache_purge' );
add_action( 'publish_to_publish', 'nginx_cache_purge' );
add_action( 'pending_to_publish', 'nginx_cache_purge' );
add_action( 'draft_to_publish', 'nginx_cache_purge' );
add_action( 'auto-draft_to_publish', 'nginx_cache_purge' );
add_action( 'future_to_publish', 'nginx_cache_purge' );
add_action( 'publish_to_trash', 'nginx_cache_purge' );

どのページのキャシュを消すとかやると面倒臭くなるので、ざっくりと rm -rf /home/www/cache/* してますがこれで殆どのケースは大丈夫。

Nginxを介さずにキャシュを消してしまうとこのようなエラーログが出てしまいますが、これで困った事は置きてないので良しとしています。

2019/03/07 14:10:04 [crit] 6788#0: unlink() "/home/www/cache/worklog.be/e/6a/9e54a9d72f8d2c1e768b76f6059f96ae" failed (2: No such file or directory)

あとは、ページによってキャッシュするしない等をNginx側で細かく制御しても良さそうです。

WordPress用のキャッシュ削除プラグイン

今だとNginxのキャッシュを削除するWordPressプラグインがいくつかあって、自分が採用している方法に非常に近いプラグインを見つけたのでご紹介します。

使い方は非常に簡単でプラグインをインストールしたら管理面でキャッシュディレクトリを設定するだけです。

WordPressのNginx Cacheプラグインを設定する管理画面

どんな処理をしているか確認すると、cache_pathに指定したNginxが自動で作成するキャッシュの親ディレクトリを丸ごと消して再作成しているみたい。

	private function purge_zone() {

		global $wp_filesystem;

		if ( ! $this->should_purge() ) {
			return false;
		}

		$path = get_option( 'nginx_cache_path' );
		$path_error = $this->is_valid_path();

		// abort if cache zone path is not valid
		if ( is_wp_error( $path_error ) ) {
			return $path_error;
		}

		// delete cache directory (recursively)
		$wp_filesystem->rmdir( $path, true );

		// recreate empty cache directory
		$wp_filesystem->mkdir( $path );

		return true;

	}

試したら問題なくサクッと動作したので自作プラグインは止めて今後はこれを使おうかな。

アクセスの多いサイトだと消している間にキャッシュをまた作ろうとするので、一時的に親ディレクトリが無い状態でも大丈夫なのか少し心配ではあります。

アクセス数が多い場合、キャッシュファイルの数もそれなりに増えるので消すのにも時間がかかる。こんな風に改良したらより良さそう。

  1. mvで一旦リネーム (すぐ終わる)
  2. 先にmkdirでディレクトリを復元 (キャッシュ再作成開始)
  3. 古いディレクトリは後でゆっくり消す

これは後で試す。

Nginx Cacheは凄いお手軽に使えるのでオススメなプラグインだと思います。

以上が運用上の所感です。

NginxでWordPressを使う時の設定方法はこれでおしまいです。

スポンサーリンク

コメント

  1. JunkHack より:

    設定がファイルごとにまとまっていて、大変参考になりました。ありがとうございます。

コメントを残す