Laravel×Nginx×PHP-FPMの設定例|pm.max_children・OPcacheで速くする

Laravel×Nginx×PHP-FPMの設定を見直す順番。Laravel 13とPHP 8.3以降が前提。1. Nginxの root を public にし、PHPとして実行するのは index.php だけにする。2. PHP-FPMの pm と pm.max_children を、メモリに収まる数にする。3. OPcacheを有効にし、validate_timestamps を0にするならデプロイで再起動する。4. デプロイのたびに php artisan optimize を実行する。5. APP_DEBUG=false と expose_php = Off を確かめる

LaravelをNginxとPHP-FPMで動かすときは、Nginxですべてのリクエストを public/index.php に渡し、PHP-FPMのプロセス数、OPcache、Laravelのキャッシュを本番向けに見直すと、リクエストごとのむだな処理を減らせます。Nginxの設定はLaravel公式ドキュメントの例を、PHP-FPMとOPcacheの値はPHPの公式マニュアルの説明をもとに決めます。

ここでの例は、Laravel 13とPHP 8.3以降を前提にしています。Laravel 13を動かすにはPHP 8.3以降が要ります(出典:Deployment(Laravel 13.x))。

目次

結論:Laravel×Nginx×PHP-FPMで見直す5つの設定

  1. Nginx:ドキュメントルートを public にし、PHPとして実行するのは index.php だけにする
  2. PHP-FPM:pm と pm.max_children を、サーバーのメモリと同時に受けたいリクエストの数に合わせる
  3. OPcache:有効にして、PHPマニュアルのおすすめの値から始める
  4. Laravelのキャッシュ:デプロイのたびに php artisan optimize を実行する
  5. 本番の設定:APP_DEBUG=false にし、storage と bootstrap/cache をWebサーバーのユーザーが書き込めるようにする

NginxでLaravelを動かす設定(公式の設定例)

Laravel 13.xの公式ドキュメントに載っているNginxの設定例です。server_name、root、fastcgi_pass は自分の環境に合わせて書きかえます(出典:Deployment(Laravel 13.x))。

server {
    listen 80;
    listen [::]:80;
    server_name example.com;
    root /srv/example.com/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";

    index index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

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

    error_page 404 /index.php;

    location ~ ^/index\.php(/|$) {
        fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
        fastcgi_buffer_size 32k;
        fastcgi_buffers 8 32k;
        fastcgi_busy_buffers_size 64k;
        fastcgi_hide_header X-Powered-By;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

設定のポイントは次のとおりです。

  • root はプロジェクトの public ディレクトリを指します。Laravelの公式ドキュメントは、public/index.php をプロジェクトのいちばん上のディレクトリへ移してはいけないとしています。移すと、中の設定ファイルなどがインターネットから見えてしまうおそれがあるためです(出典:Deployment(Laravel 13.x))。
  • try_files で、ファイルが見つからないリクエストを /index.php に渡し、Laravelのルーティングで処理します。
  • PHP-FPMに渡すのは index.php へのリクエストだけです。.env のようにドットで始まるファイルは deny all で見せません。
  • fastcgi_pass のソケットのパスは、PHP-FPM側の listen の値と同じにします。
  • fastcgi_hide_header X-Powered-By で、PHPのバージョンが入るヘッダーを返さないようにしています。

設定を変えたら、書き方に間違いがないかを確かめてから読み込み直します(出典:Command-line parameters(nginx))。

sudo nginx -t
sudo nginx -s reload

Laravel向けのPHP-FPMのプール設定

PHP-FPMは、PHPを動かす子プロセスをまとめて管理します。設定は「プール」ごとに書き、ファイルの場所はOSやインストールの方法で違います。PHPの公式Dockerイメージでは、/usr/local/etc/php-fpm.d/ に自分用の .conf ファイルを置くやり方がすすめられています(出典:php(Docker Hub 公式イメージ))。

次はプール設定の例です。pm.max_children などの数は例なので、あとの説明を見ながら自分のサーバーに合わせます。

[www]
user = www-data
group = www-data

listen = /var/run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
pm.max_requests = 500

request_terminate_timeout = 60s
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/www-slow.log

pmの3つのモード

pm は子プロセスの数の決め方で、必ず設定する項目です(出典:FPMの設定(PHPマニュアル))。

  • static:pm.max_children の数で固定します。
  • dynamic:pm.max_children、pm.start_servers、pm.min_spare_servers、pm.max_spare_servers をもとに、増やしたり減らしたりします。
  • ondemand:起動したときには作らず、リクエストが来たときに作ります。使われないまま pm.process_idle_timeout の時間(初期値は10秒)がたったプロセスは止めます。

pm.max_childrenの決め方

pm.max_children は、同時に処理できるリクエストの数の上限です(出典:FPMの設定(PHPマニュアル))。子プロセスはそれぞれメモリを使うので、増やしすぎるとサーバーのメモリが足りなくなります。アプリを動かしたときの1プロセスあたりのメモリを見て、PHP-FPMに使ってよいメモリに収まる数にします。

  • pm.start_servers:起動したときに作る子プロセスの数。初期値は min_spare_servers と max_spare_servers の平均で、dynamic のときだけ使います。
  • pm.min_spare_servers・pm.max_spare_servers:待機している子プロセスの数の下限と上限。dynamic のときだけ使います。
  • pm.max_requests:子プロセスが決まった数のリクエストを処理したら作り直します。外部のライブラリーのメモリリーク対策になり、初期値の0は作り直しません。

(出典:FPMの設定(PHPマニュアル))

ソケットと権限

NginxとPHP-FPMが同じサーバーにあるときは、Unixソケットを使います。Linuxでは、listen.owner・listen.group・listen.mode で、Nginxのユーザーがソケットを読み書きできるようにします。

PHPの公式マニュアルは、FastCGIの受け口が外から見えると、どんなコードでも実行されるおそれがあると注意しています。TCPでつなぐときは listen.allowed_clients でつないでよいアドレスを決め、コンテナではPHP-FPMのポートをホストに公開しません(出典:FPMの設定(PHPマニュアル))。

OPcacheを有効にしてPHPを速くする

OPcacheは、PHPのスクリプトをコンパイルした結果(バイトコード)を共有メモリにためておき、リクエストのたびにスクリプトを読み込んで解析する手間をはぶきます(出典:OPcache(PHPマニュアル))。PHPマニュアルには、次のおすすめの値が載っています(出典:OPcacheのインストール(PHPマニュアル))。

opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
opcache.enable_cli=1

opcache.validate_timestamps=0 にすると、ファイルの更新を確かめなくなります。この場合、デプロイでファイルを入れかえても、opcache_reset() か opcache_invalidate() を使うか、PHP-FPMを再起動するまで古いコードが動き続けます。0にするときは、デプロイの手順にPHP-FPMの再起動を入れます(出典:OPcacheの設定(PHPマニュアル))。

Laravelのキャッシュで速くする(php artisan optimize)

本番のサーバーへデプロイするときは、設定・イベント・ルート・ビューのキャッシュをまとめて作ります(出典:Deployment(Laravel 13.x))。

php artisan optimize

ひとつずつ作るときは、php artisan config:cache、php artisan event:cache、php artisan route:cache、php artisan view:cache を使います。optimize で作ったキャッシュを消すときは php artisan optimize:clear です。既定のキャッシュに入っているデータもすべて消えます(出典:Deployment(Laravel 13.x))。

config:cache を実行したあとは .env が読み込まれなくなり、設定ファイルの外で呼んだ env() は null を返します。env() は config ディレクトリの設定ファイルの中だけで使います(出典:Deployment(Laravel 13.x))。

キューのワーカーやOctaneのように動き続けるプロセスは、デプロイのあとに php artisan reload で止め、新しいコードで起動し直します。php artisan reload は止めるだけなので、止まったプロセスを自動で起動し直す仕組み(プロセスの監視)を用意しておきます(出典:Deployment(Laravel 13.x))。

本番の設定とディレクトリの権限

  • APP_DEBUG:本番では必ず false にします。true のままだと、エラーの画面に設定の中身などが出てしまうおそれがあります(出典:Deployment(Laravel 13.x))。
  • 書き込める場所:bootstrap/cache と storage は、Webサーバーのプロセスを動かすユーザーが書き込めるようにします(出典:Deployment(Laravel 13.x))。
  • expose_php:php.ini で設定する項目で、有効だと X-Powered-By ヘッダーでPHPのバージョンを知らせます。本番では expose_php = Off にします(出典:php.iniのコア設定(PHPマニュアル))。

権限を変えるコマンドの例です。www-data は、PHP-FPMを動かすユーザーに合わせます。

sudo chown -R www-data:www-data storage bootstrap/cache

Docker(docker compose)でNginxとPHP-FPMを分けるとき

PHPの公式Dockerイメージの php:8.4-fpm は、PHP-FPMを9000番ポートで待ち受けます。docker-php-ext-install でPHPの拡張を足し、本番では同じイメージに入っている php.ini-production を使います(出典:php(Docker Hub 公式イメージ)、docker-library/php 8.4 fpmのDockerfile)。

FROM php:8.4-fpm

RUN docker-php-ext-install pdo_mysql opcache
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"

WORKDIR /var/www/html
COPY . /var/www/html

NginxとPHP-FPMを別のコンテナにする compose.yaml の例です。PHP-FPMの9000番ポートは ports に書かず、同じネットワークのNginxからだけつなぎます。Nginxの設定では、fastcgi_pass を app:9000 に、root を /var/www/html/public にします。

services:
  app:
    build: .
    volumes:
      - ./:/var/www/html

  web:
    image: nginx:stable
    ports:
      - "80:80"
    volumes:
      - ./:/var/www/html
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - app

ファイルのいちばん上の version: は、いまのCompose Specificationでは情報のためだけの項目で、書くと「obsolete」という警告が出ます。新しく書くときは入れません(出典:Version and name top-level elements(Docker Docs))。

Nginx+PHP-FPMでよくあるトラブルと直し方

  • NginxからPHP-FPMにつながらない:Nginxの fastcgi_pass とPHP-FPMの listen が同じか。ソケットの listen.owner・listen.group・listen.mode でNginxのユーザーが使えるか。
  • 遅いリクエストがどこで止まっているか分からない:request_slowlog_timeout と slowlog を設定すると、時間がかかったリクエストのバックトレースがログに出る。
  • 長い処理が途中で止まる:request_terminate_timeout をこえると子プロセスが止められる。CSVの取り込みのような重い処理は、Laravelのキューで裏で動かす。
  • 時間がたつとメモリが増えていく:pm.max_requests で子プロセスを定期的に作り直す。
  • デプロイしても古いコードが動く:opcache.validate_timestamps=0 のときは、PHP-FPMを再起動するか、OPcacheをリセットする。
  • .envの値が読めない:config:cache のあとは、設定ファイルの外の env() が null になる。

(出典:FPMの設定(PHPマニュアル)、OPcacheの設定(PHPマニュアル)、Queues(Laravel 13.x)、Deployment(Laravel 13.x))

PHP-FPMのほかの選択肢(Laravel Octane)

Laravel Octaneは、FrankenPHP、Open Swoole、Swoole、RoadRunnerのアプリケーションサーバーでLaravelを動かします。アプリを一度起動してメモリに置いたまま、次々とリクエストを処理する仕組みです。PHP-FPMとはアプリの動かし方が違うので、使うときはOctaneのドキュメントに沿って準備します(出典:Octane(Laravel 13.x))。

PHPとLaravelのサポート期間

バージョンセキュリティ修正の期限
PHP 8.22026年12月31日
PHP 8.32027年12月31日
PHP 8.42028年12月31日
PHP 8.52029年12月31日
Laravel 12(PHP 8.2〜8.5)2027年2月24日
Laravel 13(PHP 8.3〜8.5)2028年3月17日

(2026年10月25日時点、出典:PHPのサポート中のバージョン、Release Notes(Laravel 13.x))

見直す順番(チェックリスト)

  1. Nginxの root が public になっていて、PHPとして実行するのが index.php だけか確かめる
  2. PHP-FPMの pm と pm.max_children を、メモリに収まる数にする
  3. OPcacheを有効にし、validate_timestamps を0にするならデプロイで再起動する
  4. デプロイで php artisan optimize を実行する
  5. APP_DEBUG=false と expose_php = Off を確かめる

参考リンク

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

社内エンジニアとして、SREとセキュリティの仕事をしています。サーバーの構築や運用、クラウド環境の設計にも関わってきました。趣味はゲームと漫画・アニメ。

コメント

コメントする

目次