NginxでWordPressを使う時の設定は色々な書き方が出来ますが、ネットの情報は断片的な気がしたのでまとめてみました。
可読性良く設定をしたいのと、WordPress向けにこれだけ書けば不自由はないよ!っていうのを目的に設計してます。
目次
スポンサーリンク
環境
各種スペックはこんな感じでやってます。
- サーバは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;
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の設定に問題があっても出力されるエラーですが、設定に問題が無いようなら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プラグインがいくつかあって、自分が採用している方法に非常に近いプラグインを見つけたのでご紹介します。
使い方は非常に簡単でプラグインをインストールしたら管理面でキャッシュディレクトリを設定するだけです。
どんな処理をしているか確認すると、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; }
試したら問題なくサクッと動作したので自作プラグインは止めて今後はこれを使おうかな。
アクセスの多いサイトだと消している間にキャッシュをまた作ろうとするので、一時的に親ディレクトリが無い状態でも大丈夫なのか少し心配ではあります。
アクセス数が多い場合、キャッシュファイルの数もそれなりに増えるので消すのにも時間がかかる。こんな風に改良したらより良さそう。
- mvで一旦リネーム (すぐ終わる)
- 先にmkdirでディレクトリを復元 (キャッシュ再作成開始)
- 古いディレクトリは後でゆっくり消す
これは後で試す。
Nginx Cacheは凄いお手軽に使えるのでオススメなプラグインだと思います。
以上が運用上の所感です。
NginxでWordPressを使う時の設定方法はこれでおしまいです。
設定がファイルごとにまとまっていて、大変参考になりました。ありがとうございます。