2026年5月23日
2026年5月23日
Nginx で WordPress を動かす設定(server ブロックの書き方)|PHP-FPM 連携・パーマリンクの 404・FastCGI キャッシュ
Nginx で WordPress を動かすときにつまずきやすいのは、パーマリンクの 404、PHP-FPM との接続、location の書き順によるセキュリティ設定の取りこぼしの3つです。この記事では、Nginx と PHP-FPM で WordPress を動かす server ブロックを一から書き、動作確認と FastCGI キャッシュの導入までを説明します。
例は 2026年10月時点の Ubuntu 24.04 LTS(標準パッケージは Nginx 1.24、PHP 8.3)です。ドメインは example.com、WordPress の設置先は /var/www/html とします。別の OS やバージョンでは、パッケージ名とソケットのパスを読み替えてください。
よくある症状と原因
| 症状 | 主な原因 |
|---|---|
| 投稿ページなどが 404 になる(トップページは表示される) | location / に try_files が無く、存在しない URL が index.php に渡らない |
| 502 Bad Gateway | fastcgi_pass のソケットのパスが PHP-FPM の実際のパスと違う、PHP-FPM が起動していない |
| PHP ファイルがダウンロードされる | .php を PHP-FPM に渡す location が無い |
| uploads に置かれた PHP が実行できてしまう | 拒否する location が、PHP を処理する location より後に書かれている |
Nginx には Apache の .htaccess に当たる仕組みがありません。WordPress の管理画面でパーマリンクを保存しても Nginx の設定は変わらないので、リライトは server ブロックの try_files で行います。
手順1:PHP-FPM と必要な拡張を入れる
sudo apt update
sudo apt install nginx php-fpm php-mysql php-curl php-gd php-intl \
php-mbstring php-xml php-zip php-imagick
# PHP-FPM のソケットのパスを確認する
ls /run/php/
# 例: php8.3-fpm.sock ← このパスを fastcgi_pass に書く
# PHP-FPM の起動と自動起動
sudo systemctl enable --now php8.3-fpm
php-fpm などの名前で入れると、その OS の標準バージョン(Ubuntu 24.04 では 8.3)が入ります。ソケットのファイル名はバージョンを含むので、ls /run/php/ で実際の名前を確かめてから設定に書きます。
ファイルの所有者は、PHP-FPM の実行ユーザー(Ubuntu の既定は www-data)にします。
sudo chown -R www-data:www-data /var/www/html
sudo find /var/www/html -type d -exec chmod 755 {} \;
sudo find /var/www/html -type f -exec chmod 644 {} \;
この所有者にすると、管理画面からのプラグイン・テーマの更新が FTP 情報を聞かれずに動きます。一方で Web サーバーのプロセスがすべてのファイルを書き換えられる状態になるので、更新を WP-CLI などで行う運用なら、書き込みを wp-content 配下に限る方法もあります。
手順2:WordPress 用の server ブロックを書く
/etc/nginx/sites-available/wordpress.conf を作ります。証明書は Let's Encrypt で取得済みの前提です(取得前に試すなら、まず 80 番の server だけで動かします)。
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# HTTP は HTTPS へ転送する
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com;
root /var/www/html;
index index.php index.html;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
client_max_body_size 64m;
# パーマリンク: ファイルもディレクトリも無ければ index.php に渡す
location / {
try_files $uri $uri/ /index.php?$args;
}
# ドットで始まるファイル(.git、.htaccess など)を拒否する
# Let's Encrypt の更新で使う /.well-known/ は除く
location ~ /\.(?!well-known/) {
deny all;
}
location = /wp-config.php {
deny all;
}
# uploads 内の PHP は実行させない(PHP の location より前に書く)
location ~* /(?:uploads|files)/.*\.php$ {
deny all;
}
# PHP-FPM に渡す
location ~ \.php$ {
try_files $uri =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_read_timeout 300;
}
# 静的ファイルはブラウザにキャッシュさせ、無い場合のログを出さない
location ~* \.(?:css|js|jpe?g|png|gif|webp|avif|ico|svg|woff2?)$ {
expires 30d;
access_log off;
log_not_found off;
}
}
設定の要点は次のとおりです。
- location の書き順:正規表現の location(
~と~*)は、設定ファイルに書いた順に照合され、最初に一致したものが使われます。uploads の PHP を拒否する location をlocation ~ \.php$より後に書くと、先に PHP の location に一致して実行されてしまいます。拒否する location は PHP の location より前に置きます。 - try_files:
$uri $uri/ /index.php?$argsで、実在するファイルはそのまま返し、それ以外はindex.phpにクエリ文字列ごと渡します。これが.htaccessのリライトの代わりです。 - PHP の location の try_files:
try_files $uri =404;は、存在しない.phpへのリクエストを PHP-FPM に渡さず 404 にします。 - ドットファイルの拒否:単純に
location ~ /\.と書くと/.well-known/acme-challenge/も拒否され、Let's Encrypt の HTTP 認証による証明書更新が失敗します。上の例では.well-known/を除いています。 - http2:Ubuntu 24.04 の Nginx 1.24 では
listen 443 ssl http2;と書きます。Nginx 1.25.1 以降ではlisten 443 ssl;と別行のhttp2 on;が新しい書き方で、古い書き方は警告が出ます。nginx -vで確かめてください。 - 静的ファイルの期限:
expiresを長くするほど、ファイルを差し替えたときにブラウザに古いものが残ります。WordPress はテーマやプラグインの CSS・JS に?ver=を付けて出力するので更新は反映されますが、同じ URL のまま画像を差し替える運用なら短めにします。
手順3:設定を有効にして確認する
# 設定を有効にする
sudo ln -s /etc/nginx/sites-available/wordpress.conf /etc/nginx/sites-enabled/
# 既定のサイトが 80 番を使っている場合は外す
sudo rm /etc/nginx/sites-enabled/default
# 文法を確認してから反映する
sudo nginx -t
sudo systemctl reload nginx
nginx -t で「syntax is ok」「test is successful」と出てから reload します。続けて、応答を確認します。
# HTTP は 301 で HTTPS に転送される
curl -sI http://example.com/ | head -n 3
# パーマリンクの URL が 200 になる(実在する投稿の URL で試す)
curl -sI https://example.com/sample-post/ | head -n 1
# 未ログインで管理画面を開くと 302 でログイン画面に転送される
curl -sI https://example.com/wp-admin/ | grep -i -E "^HTTP|^location"
# uploads に置いた PHP が実行されず 403 になる
echo '<?php echo "ng";' | sudo tee /var/www/html/wp-content/uploads/test.php > /dev/null
curl -sI https://example.com/wp-content/uploads/test.php | head -n 1
sudo rm /var/www/html/wp-content/uploads/test.php
uploads のテストで 200 が返り本文に ng と出る場合は、手順2の location の順番を見直してください。
手順4:WordPress 側の設定
パーマリンクは管理画面の「設定」→「パーマリンク」で選んで保存します。Nginx で動いていることを WordPress は自動で判別するので、URL に index.php は付きません。WP-CLI なら次のように設定できます。
cd /var/www/html
sudo -u www-data wp rewrite structure '/%postname%/'
sudo -u www-data wp option get permalink_structure
wp rewrite structure はパーマリンクの形式を保存し、WordPress 内部のリライトルールを作り直します。Nginx では .htaccess を使わないので、.htaccess を書き出す --hard は付けなくてかまいません。
サイトの URL は、インストール時に https://example.com で設定していれば変更は不要です。HTTP で入れた場合は「設定」→「一般」の「WordPress アドレス (URL)」と「サイトアドレス (URL)」を https:// に変えます。この構成では Nginx 自身が HTTPS を受けていて fastcgi_params が PHP に HTTPS を伝えるので、ロードバランサーの後ろに置くときに使う X-Forwarded-Proto の判定を wp-config.php に書く必要はありません。
問題が起きたときは、Nginx と PHP-FPM のエラーログを見ます。
sudo tail -n 50 /var/log/nginx/error.log
sudo journalctl -u php8.3-fpm -n 50
手順5:FastCGI キャッシュで高速化する
FastCGI キャッシュは、PHP が生成したページを Nginx がファイルに保存し、次からは PHP を通さずに返す仕組みです。ログイン中のユーザーやコメント投稿者に他人向けのページを返さないよう、除外条件を合わせて設定します。
まず http コンテキストに、キャッシュの保存先とキーを定義します。Ubuntu では /etc/nginx/conf.d/ 内のファイルが http ブロックの中で読み込まれるので、/etc/nginx/conf.d/fastcgi-cache.conf として保存します。
fastcgi_cache_path /var/cache/nginx/wordpress levels=1:2 keys_zone=WORDPRESS:100m
inactive=60m max_size=1g;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
次に、手順2の HTTPS の server ブロックにキャッシュしない条件を追加し、PHP の location をキャッシュ付きのものに置き換えます。
# キャッシュしない条件(server ブロック内、location の外)
set $skip_cache 0;
if ($request_method = POST) {
set $skip_cache 1;
}
if ($query_string != "") {
set $skip_cache 1;
}
if ($request_uri ~* "/wp-admin/|/wp-json/|/xmlrpc.php|wp-.*\.php|/feed/|sitemap") {
set $skip_cache 1;
}
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in|woocommerce_items_in_cart") {
set $skip_cache 1;
}
location ~ \.php$ {
try_files $uri =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_read_timeout 300;
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 60m;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
add_header X-FastCGI-Cache $upstream_cache_status;
}
fastcgi_cache_validは 200 だけにしています。リダイレクト(301・302)をキャッシュすると、ログイン後の転送先などが他の人に返ることがあるためです。- Cookie の条件で、ログイン中のユーザー、コメント投稿者、パスワード保護ページを見た人、WooCommerce でカートに商品を入れた人をキャッシュの対象外にしています。会員サイトやネットショップでは、使っているプラグインの Cookie とページ(カート、決済、マイページ)も除外に加えてください。
- オープンソース版の Nginx には、キャッシュを URL 単位で消すコマンドがありません。記事を更新しても、最大で
fastcgi_cache_validの時間は古いページが返ります。更新をすぐ反映したい場合は、キャッシュの削除に対応したプラグイン(例:Nginx Helper)を使うか、保存先のファイルを消します。
設定を反映して、キャッシュが効いているかを確認します。
sudo mkdir -p /var/cache/nginx/wordpress
sudo nginx -t && sudo systemctl reload nginx
# 1回目は MISS、2回目は HIT になる
curl -sI https://example.com/ | grep -i x-fastcgi-cache
curl -sI https://example.com/ | grep -i x-fastcgi-cache
# キャッシュをすべて消す
sudo find /var/cache/nginx/wordpress -type f -delete
ログインした状態のブラウザでは BYPASS になるのが正しい動きです。
まとめ
- パーマリンクは
location /のtry_files $uri $uri/ /index.php?$args;で通します。管理画面での保存は Nginx の設定を変えません。 fastcgi_passのソケットのパスはls /run/php/で確かめた実際の名前を書きます。- 正規表現の location は書いた順に照合されるので、uploads の PHP を拒否する location は PHP の location より前に置き、curl で 403 になることを確かめます。
- ドットファイルの拒否から
/.well-known/を除き、証明書の更新を止めないようにします。 - FastCGI キャッシュは 200 の応答だけを保存し、POST、クエリ文字列、管理画面、ログイン Cookie などを除外します。
関連記事: