PHPのタイムアウトの設定について

採用はこちら

PHPのタイムアウトは、max_execution_timeだけで決まるものではありません。

一般的なWebシステムでは、PHP本体に加えて、PHP-FPM、Nginx、Apache、CDN、ロードバランサー、データベース、外部APIなど、複数のレイヤーにタイムアウト設定があります。

ブラウザ
  ↓
CDN・ロードバランサー
  ↓
Nginx・Apache
  ↓
PHP-FPM
  ↓
PHPスクリプト
  ↓
データベース・外部API

そのため、PHPの実行時間を300秒に設定しても、Nginxやロードバランサーが60秒で接続を終了すれば、処理結果をブラウザへ返すことはできません。

タイムアウトの問題を解決するには、どのレイヤーで処理が終了しているのかを確認する必要があります。

目次

PHPのmax_execution_time

max_execution_timeの役割

max_execution_timeは、PHPスクリプトが実行できる最大時間を秒単位で指定する設定です。

max_execution_time = 30

この設定では、PHPスクリプトの実行時間の上限が30秒になります。

Web経由で実行されるPHPでは30秒が設定されていることが多く、CLI版PHPでは0、つまり時間制限なしになっていることが一般的です。

無制限にする場合は、次のように設定します。

max_execution_time = 0

ただし、Webアプリケーションで無制限に設定することは推奨されません。

無限ループや外部サービスの応答停止が発生すると、PHP-FPMのワーカーが長時間占有され、ほかのリクエストを処理できなくなる可能性があるためです。

PHPコード内で変更する方法

特定の処理だけ実行時間を延長したい場合は、ini_set()を使用できます。

<?php

ini_set('max_execution_time', '120');

set_time_limit()でも設定できます。

<?php

set_time_limit(120);

無制限にする場合は、0を指定します。

<?php

set_time_limit(0);

ただし、サーバーやホスティング環境によっては、PHPコード内から設定を変更できない場合があります。

また、PHP-FPMやWebサーバー側のタイムアウトは、set_time_limit()では変更できません。

set_time_limit()は呼び出し時点から数え直す

set_time_limit()を実行すると、原則として呼び出した時点から制限時間が数え直されます。

例えば、スクリプト開始から20秒後に次の処理を実行したとします。

set_time_limit(60);

この場合、スクリプト開始から合計60秒で終了するのではなく、set_time_limit()を呼び出した時点から、さらに60秒実行できる可能性があります。

長いループ処理の中で繰り返しset_time_limit()を呼び出すと、実質的に処理時間を延長し続けることも可能です。

HTTPリクエスト全体の経過時間とは限らない

max_execution_timeは、必ずしもHTTPリクエスト全体の実時間を制限する設定ではありません。

Linuxなどの非Windows環境では、次の時間が実行時間に含まれない場合があります。

  • データベースクエリの待機時間
  • 外部APIの応答待ち
  • ファイルやストリームの入出力
  • システムコール
  • sleep()による待機
  • 外部コマンドの実行時間

例えば、max_execution_timeが30秒でも、データベースからの応答を60秒待っている場合、HTTPリクエスト全体が30秒を超えることがあります。

したがって、次のような理解は正確ではありません。

max_execution_time = 30
↓
すべてのHTTPリクエストが必ず30秒以内に終了する

より正確には、max_execution_timeは主にPHPスクリプトの実行時間を制限する設定です。

外部通信やデータベース処理には、それぞれ個別のタイムアウトを設定する必要があります。

PHPのmax_input_time

max_input_timeの役割

max_input_timeは、PHPがリクエストの入力データを受信・解析するために使用できる最大時間を設定します。

対象となる主なデータは次のとおりです。

  • GETパラメータ
  • POSTデータ
  • Cookie
  • ファイルアップロード
  • multipart/form-data

設定例は次のとおりです。

max_input_time = 60

大容量ファイルを受け付ける場合や、低速回線からのアップロードを想定する場合は、値を増やすことがあります。

max_input_time = 300

デフォルト値の考え方

PHP内部の標準値は、次のように定義されています。

max_input_time = -1

-1の場合、max_execution_timeの値が参照されます。

ただし、PHP公式が配布しているphp.ini-productionや、多くのLinuxディストリビューションの設定ファイルでは、60秒が指定されていることがあります。

そのため、実際のサーバーで有効な値を確認することが重要です。

ファイルアップロード時の注意点

ファイルアップロードでは、max_input_time以外にも複数の設定が関係します。

max_input_time = 300
upload_max_filesize = 100M
post_max_size = 110M

upload_max_filesizeは、1ファイルあたりの最大アップロードサイズです。

post_max_sizeは、POSTリクエスト全体の最大サイズです。

POSTデータにはファイル本体だけでなく、次のような情報も含まれます。

  • フォーム項目
  • ファイル名
  • MIMEタイプ
  • CSRFトークン
  • multipartの境界情報
  • 各パートのヘッダー

そのため、通常は次の関係になるよう設定します。

post_max_size > upload_max_filesize

例えば、100MBのファイルを受け付ける場合は、次のように少し余裕を持たせます。

upload_max_filesize = 100M
post_max_size = 110M

複数ファイルを同時にアップロードする場合は、合計サイズを考慮する必要があります。

Nginx側のアップロード設定

Nginxを使用している場合は、PHPへ到達する前にNginx側で拒否されることがあります。

確認する主な設定は次のとおりです。

client_max_body_size 110M;
client_body_timeout 300s;

client_max_body_sizeは、Nginxが受け付けるリクエストボディの最大サイズです。

この値がPHPのpost_max_sizeより小さい場合、PHP側の設定を変更しても大きなファイルはアップロードできません。

PHPのdefault_socket_timeout

default_socket_timeoutの役割

default_socket_timeoutは、ソケットベースのストリーム通信で使用される標準タイムアウトです。

default_socket_timeout = 60

次のような処理に影響する可能性があります。

<?php

$content = file_get_contents('https://example.com/api');
<?php

$handle = fopen('https://example.com/data.csv', 'r');
<?php

$socket = fsockopen('example.com', 80);

ただし、すべてのネットワーク通信に一律で適用されるわけではありません。

次のようなライブラリや拡張機能は、独自のタイムアウト設定を持つ場合があります。

  • cURL
  • Guzzle
  • Symfony HttpClient
  • Laravel HTTP Client
  • PDO
  • MySQLi
  • Redis
  • MongoDB
  • AWS SDK

外部APIへ接続する場合は、default_socket_timeoutだけに依存せず、HTTPクライアント側で明示的に設定する方が安全です。

接続タイムアウトと読み取りタイムアウト

接続確立までの待ち時間と、接続後のデータ受信待ちは別のタイムアウトです。

fsockopen()では、引数で接続タイムアウトを指定できます。

<?php

$socket = fsockopen(
    'example.com',
    80,
    $errorCode,
    $errorMessage,
    5
);

接続後の読み書きには、stream_set_timeout()を使用します。

<?php

if ($socket === false) {
    throw new RuntimeException(
        sprintf('接続に失敗しました: %s (%d)', $errorMessage, $errorCode)
    );
}

stream_set_timeout($socket, 10);

ただし、stream_set_timeout()だけで処理全体が必ず10秒以内に終了するとは限りません。

厳密な処理期限が必要な場合は、アプリケーション側で開始時刻や終了予定時刻を管理する必要があります。

PHP-FPMのタイムアウト

request_terminate_timeout

PHP-FPMでは、request_terminate_timeoutを使用して、一定時間を超えたリクエストのワーカープロセスを終了できます。

request_terminate_timeout = 120s

主な設定ファイルは、環境によって次のような場所にあります。

/etc/php/8.3/fpm/pool.d/www.conf
/etc/php/8.4/fpm/pool.d/www.conf
/etc/php-fpm.d/www.conf

0を指定すると無効になります。

request_terminate_timeout = 0

max_execution_timeが正常に機能しない場合や、外部I/O待ちによってPHPプロセスが長時間停止している場合でも、PHP-FPM側からワーカーを終了できる点が特徴です。

例えば、次のように設定できます。

; php.ini
max_execution_time = 90
; PHP-FPMプール設定
request_terminate_timeout = 120s

PHPスクリプトには90秒の制限を設け、何らかの理由で終了しない場合は、PHP-FPMが120秒でワーカーを停止させる構成です。

request_terminate_timeout_track_finished

fastcgi_finish_request()実行後の処理や、PHPの終了処理にもrequest_terminate_timeoutを適用する場合は、次の設定を確認します。

request_terminate_timeout_track_finished = yes

デフォルトではnoになっている環境があります。

例えば、次のようなコードでは、ブラウザへのレスポンスを返した後もPHP-FPMのワーカーが処理を続けます。

<?php

fastcgi_finish_request();

runLongTask();

レスポンス完了後に長時間処理を実行すると、ユーザーには画面が返っていても、PHP-FPMワーカーは占有されたままです。

長時間処理は、ジョブキューやバッチサーバーへ移す方が適しています。

request_slowlog_timeout

処理が遅いPHPリクエストを調査する場合は、slowlogを有効にできます。

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

指定時間を超えたリクエストについて、PHPのバックトレースが記録されます。

ただし、slowlogはリクエストを強制終了する設定ではありません。

主な用途は、次のような遅延原因の調査です。

  • 重いループ処理
  • 遅い関数呼び出し
  • データベース待ち
  • 外部API待ち
  • ファイルI/O
  • ライブラリ内部の停止

タイムアウト値を延長する前に、slowlogやアプリケーションログから原因を調査することが重要です。

PHP-FPM設定変更後の反映

設定変更後は、PHP-FPMを再読み込みまたは再起動します。

UbuntuやDebian系の例です。

sudo systemctl reload php8.3-fpm

再起動する場合は次のとおりです。

sudo systemctl restart php8.3-fpm

RHEL、CentOS、Amazon Linux系では、サービス名がphp-fpmになっていることがあります。

sudo systemctl restart php-fpm

Nginxのタイムアウト

fastcgi_connect_timeout

fastcgi_connect_timeoutは、NginxがPHP-FPMとの接続を確立するまで待機する時間です。

fastcgi_connect_timeout 10s;

これはPHPスクリプトの実行時間ではありません。

PHP-FPMが停止している場合や、Unixソケットへ接続できない場合などに関係します。

通常、接続確立に長時間かかること自体が異常であるため、数百秒などの大きな値を設定することは推奨されません。

fastcgi_send_timeout

fastcgi_send_timeoutは、NginxからPHP-FPMへリクエストデータを送信する際のタイムアウトです。

fastcgi_send_timeout 60s;

厳密には、リクエスト全体の送信時間ではなく、連続した書き込み処理の間隔に関係します。

通常のGETリクエストでは問題になりにくいですが、次のようなケースでは影響する可能性があります。

  • 大容量のPOSTデータ
  • PHP-FPMがリクエストデータを読み取れない
  • PHP-FPMワーカーが不足している
  • ネットワーク越しにFastCGI接続している
  • PHP-FPM側で処理が詰まっている

fastcgi_read_timeout

fastcgi_read_timeoutは、NginxがPHP-FPMからレスポンスを受け取る際のタイムアウトです。

fastcgi_read_timeout 120s;

重要なのは、レスポンス全体の総時間ではないという点です。

PHP-FPMからデータを受信する読み取り処理の間隔に対するタイムアウトとして動作します。

例えば、PHP-FPMが一定間隔でデータを返し続けている場合、リクエスト全体が120秒を超えても接続が維持される可能性があります。

一方、PHP-FPMから120秒間まったくデータが返らない場合は、Nginxが接続を終了する可能性があります。

PHP-FPM向けの設定例

location ~ \.php$ {
    include fastcgi_params;

    fastcgi_pass unix:/run/php/php8.3-fpm.sock;

    fastcgi_connect_timeout 10s;
    fastcgi_send_timeout 60s;
    fastcgi_read_timeout 120s;
}

設定変更後は、構文チェックを行います。

sudo nginx -t

問題がなければ、Nginxを再読み込みします。

sudo systemctl reload nginx

proxy_read_timeoutとの違い

NginxからPHP-FPMへFastCGIで接続する場合は、基本的にfastcgi_read_timeoutを使用します。

一方、NginxからHTTPアプリケーションサーバーへリバースプロキシする場合は、proxy_read_timeoutを使用します。

location /api/ {
    proxy_pass http://app_backend;
    proxy_read_timeout 120s;
}

例えば、次のようなバックエンドに転送する場合です。

  • Node.js
  • Java
  • Go
  • Python
  • 別のNginx
  • 外部HTTPサービス

proxy_read_timeoutも、レスポンス全体の総時間ではなく、連続する読み取り処理の間隔に対するタイムアウトです。

Apacheのタイムアウト

Apacheとmod_php

PHPがApacheモジュールとして動作している場合は、max_execution_timeが主なPHP側の制限になります。

環境によっては、Apache設定や.htaccessで次のように指定できます。

php_value max_execution_time 120
php_value max_input_time 300

ただし、この設定が利用できるのは、主にPHPがApacheモジュールとして動作している環境です。

PHP-FPMやFastCGI構成では、php_valueが利用できず、500 Internal Server Errorになることがあります。

エラーログには、次のようなメッセージが出る場合があります。

Invalid command 'php_value'

PHP-FPM環境では、次の方法を使用します。

  • php.ini
  • PHP-FPMプール設定
  • .user.ini
  • ini_set()
  • FastCGIパラメータ

Timeout

ApacheのTimeoutは、ネットワーク通信などに使用される広範囲なタイムアウト設定です。

Timeout 120

影響範囲が広いため、単純に大きな値へ変更すると、接続が長時間残り続ける可能性があります。

特定のプロキシ処理だけ調整したい場合は、より限定的な設定を使用する方が安全です。

ProxyTimeout

ApacheからPHP-FPMや別のバックエンドへプロキシしている場合は、ProxyTimeoutを確認します。

ProxyTimeout 120

ProxyTimeoutが設定されていない場合は、通常Timeoutの値が利用されます。

Apache全体のTimeoutを変更するより、必要なプロキシ処理だけに適切なタイムアウトを設定する方が、影響範囲を抑えられます。

PHP設定ファイルの場所と優先順位

PHP設定は複数の場所に存在する

PHPの設定は、次のような場所から変更できます。

php.ini
追加のiniファイル
PHP-FPMプール設定
.user.ini
.htaccess
ini_set()
set_time_limit()

ただし、すべての設定項目をどこからでも変更できるわけではありません。

PHPの設定項目には、変更可能な範囲が定められています。

主な区分は次のとおりです。

  • PHP_INI_ALL
  • PHP_INI_USER
  • PHP_INI_PERDIR
  • PHP_INI_SYSTEM

PHP_INI_ALLの設定は、比較的広い範囲から変更できます。

一方、PHP_INI_SYSTEMの設定は、php.iniやサーバー設定など、システムレベルでしか変更できません。

CLIとWebで異なるphp.iniが使われる

同じサーバーでも、CLI、PHP-FPM、Apacheで異なるphp.iniを使用していることがあります。

Ubuntu系では、次のように分かれていることがあります。

/etc/php/8.3/cli/php.ini
/etc/php/8.3/fpm/php.ini
/etc/php/8.3/apache2/php.ini

コマンドラインで次を実行すると、CLI版PHPが読み込んでいる設定を確認できます。

php --ini

ただし、ここで表示される設定は、PHP-FPMが利用している設定とは限りません。

CLIでは設定が反映されているのに、ブラウザ経由では反映されていない場合は、異なるphp.iniを編集している可能性があります。

現在の設定値を確認する方法

PHPコードで確認する

ブラウザ経由で実際に使われている設定は、次のコードで確認できます。

<?php

var_dump([
    'sapi'                   => PHP_SAPI,
    'php_version'            => PHP_VERSION,
    'loaded_ini_file'        => php_ini_loaded_file(),
    'max_execution_time'     => ini_get('max_execution_time'),
    'max_input_time'         => ini_get('max_input_time'),
    'default_socket_timeout' => ini_get('default_socket_timeout'),
]);

確認できる主な情報は次のとおりです。

  • 実行中のSAPI
  • PHPのバージョン
  • 読み込まれているphp.ini
  • 実行時間制限
  • 入力時間制限
  • ソケットタイムアウト

phpinfo()で確認する

より詳細な情報を確認する場合は、phpinfo()を使用できます。

<?php

phpinfo();

確認する主な項目は次のとおりです。

  • Loaded Configuration File
  • Scan this dir for additional .ini files
  • Additional .ini files parsed
  • max_execution_time
  • max_input_time
  • default_socket_timeout
  • Server API

Server APIには、次のような値が表示されます。

FPM/FastCGI
Apache 2.0 Handler
Command Line Interface

ただし、phpinfo()はサーバーの内部情報を大量に表示します。

公開環境に置いたままにすると、セキュリティ上のリスクになるため、確認後は必ず削除してください。

CLIで確認する

CLI版PHPの設定は、次のコマンドで確認できます。

php --ini
php -i | grep max_execution_time
php -r "echo ini_get('max_execution_time'), PHP_EOL;"

これらの結果は、ブラウザ経由で実行されるPHP-FPMやApache版PHPとは異なる場合があります。

PHP-FPMの設定を確認する

環境によっては、次のコマンドでPHP-FPMの設定をテストできます。

php-fpm8.3 -tt

または次のコマンドです。

php-fpm -tt

設定ファイルの構文エラーや、読み込まれているプール設定を確認する際に役立ちます。

cURLのタイムアウト

接続タイムアウトと全体タイムアウト

外部APIへcURLで接続する場合は、PHP全体とは別にタイムアウトを設定します。

<?php

$ch = curl_init('https://api.example.com/data');

curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CONNECTTIMEOUT => 5,
    CURLOPT_TIMEOUT        => 20,
]);

$response = curl_exec($ch);

if ($response === false) {
    $message = curl_error($ch);
    curl_close($ch);

    throw new RuntimeException($message);
}

curl_close($ch);

各設定の役割は次のとおりです。

CURLOPT_CONNECTTIMEOUT
接続を確立するまでの最大時間

CURLOPT_TIMEOUT
cURL処理全体の最大時間

外部APIでは、接続タイムアウトを短めにし、処理全体のタイムアウトを少し長めに設定する方法が一般的です。

CURLOPT_CONNECTTIMEOUT => 3,
CURLOPT_TIMEOUT        => 10,

ミリ秒単位で設定する

より細かく制御したい場合は、ミリ秒単位の設定も利用できます。

curl_setopt_array($ch, [
    CURLOPT_CONNECTTIMEOUT_MS => 3000,
    CURLOPT_TIMEOUT_MS        => 10000,
]);

通信のどこが遅いか確認する

通信時間の内訳は、curl_getinfo()で確認できます。

<?php

$info = curl_getinfo($ch);

var_dump([
    'name_lookup_time'    => $info['namelookup_time'] ?? null,
    'connect_time'        => $info['connect_time'] ?? null,
    'app_connect_time'    => $info['appconnect_time'] ?? null,
    'start_transfer_time' => $info['starttransfer_time'] ?? null,
    'total_time'          => $info['total_time'] ?? null,
]);

これにより、次のどこで時間がかかっているかを判断しやすくなります。

  • DNS名前解決
  • TCP接続
  • TLS接続
  • サーバーの応答開始
  • レスポンス全体の受信

データベースのタイムアウト

PHPのタイムアウトとは別に管理される

PHPの実行時間を延長しても、データベース側で接続やクエリが終了する場合があります。

データベースには、主に次のタイムアウトがあります。

  • 接続タイムアウト
  • クエリ実行時間
  • ロック待ち時間
  • アイドル接続時間
  • 読み取りタイムアウト
  • 書き込みタイムアウト

PDOのATTR_TIMEOUT

PDOでは、次のようにタイムアウトを指定できます。

<?php

$pdo = new PDO(
    'mysql:host=db;dbname=app;charset=utf8mb4',
    'user',
    'password',
    [
        PDO::ATTR_TIMEOUT => 5,
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    ]
);

ただし、PDO::ATTR_TIMEOUTの挙動はドライバによって異なります。

次のように理解するのは正確ではありません。

PDO::ATTR_TIMEOUT = 5
↓
すべてのSQLが必ず5秒で中断される

接続タイムアウトとして扱われる場合や、ドライバ側で十分にサポートされていない場合があります。

クエリ実行時間を制限したい場合は、データベース固有の設定も確認する必要があります。

タイムアウト延長よりSQL改善を優先する

データベース処理が遅い場合は、PHPのタイムアウトを延長する前に、次の対策を検討します。

  • 実行計画を確認する
  • 適切なインデックスを追加する
  • N+1クエリを解消する
  • 不要なカラムを取得しない
  • 一括処理へ変更する
  • ページネーションを導入する
  • 対象件数を制限する
  • バックグラウンドジョブへ移す
  • スロークエリログを確認する

タイムアウトの延長だけでは、アクセス増加時にサーバー負荷がさらに高くなる可能性があります。

代表的なエラーと確認ポイント

Maximum execution time exceeded

次のエラーは、PHPの実行時間制限に到達した場合に発生します。

Fatal error: Maximum execution time of 30 seconds exceeded

主な確認項目は次のとおりです。

  • max_execution_time
  • set_time_limit()
  • 無限ループ
  • 大量データ処理
  • 非効率なアルゴリズム
  • 重い画像処理
  • 過剰なファイル操作

一時的に制限を延長する方法はありますが、まず処理内容に問題がないか確認することが重要です。

504 Gateway Timeout

504 Gateway Timeoutは、ゲートウェイやプロキシが上流サーバーから期限内に応答を受け取れなかった場合に発生します。

主な原因として、次が考えられます。

  • Nginxのfastcgi_read_timeout
  • Nginxのproxy_read_timeout
  • ApacheのProxyTimeout
  • CDNのタイムアウト
  • ロードバランサーのタイムアウト
  • PHP-FPMの処理遅延
  • データベースの処理遅延
  • 外部APIの応答遅延
  • PHP-FPMワーカー不足

Nginxでは、エラーログに次のようなメッセージが出ることがあります。

upstream timed out while reading response header from upstream

504エラーが発生した場合、PHPのmax_execution_timeだけを変更しても解決しないことがあります。

どの機器やソフトウェアが504を返したのかを、レスポンスヘッダーや各種ログから確認します。

502 Bad Gateway

502 Bad Gatewayは、ゲートウェイやプロキシが上流サーバーから正常なレスポンスを受け取れなかった場合に発生します。

主な原因は次のとおりです。

  • PHP-FPMが停止している
  • PHP-FPMのソケットへ接続できない
  • PHP-FPMワーカーが異常終了した
  • request_terminate_timeoutでプロセスが終了した
  • メモリ不足でプロセスが終了した
  • PHP-FPMがクラッシュした
  • NginxとPHP-FPMの設定が一致していない
  • Unixソケットの権限が正しくない

Nginxのログには、次のようなメッセージが出ることがあります。

connect() to unix:/run/php/php-fpm.sock failed
recv() failed (104: Connection reset by peer)
upstream prematurely closed FastCGI stdout

502や504というステータスコードだけで原因を断定することはできません。

Webサーバー、PHP-FPM、アプリケーション、データベースのログを照合する必要があります。

PHPのhard_timeout

hard_timeoutの役割

新しいPHPでは、hard_timeoutもタイムアウト調査の対象になります。

max_execution_timeに到達した後、PHPはリソース解放や終了処理を行います。

その終了処理自体が停止した場合、hard_timeoutに指定された時間を超えると、PHPが強制終了されます。

max_execution_time = 30
hard_timeout = 2

この場合、30秒の実行時間制限に到達した後、終了処理が2秒以上停止すると、強制終了される可能性があります。

通常のWebアプリケーションでは頻繁に変更する設定ではありません。

ただし、max_execution_timeを少し超えたタイミングでプロセスが異常終了する場合は、確認する価値があります。

用途別の設定例

一般的なWebページ

一般的なWebページでは、長時間の処理を許可しすぎないことが重要です。

max_execution_time = 30
max_input_time = 60
default_socket_timeout = 30

PHP-FPMは、PHPの制限より少し長く設定します。

request_terminate_timeout = 45s
request_slowlog_timeout = 5s

Nginxでは次のように設定します。

fastcgi_connect_timeout 5s;
fastcgi_send_timeout 30s;
fastcgi_read_timeout 45s;

通常のWebページで数十秒以上待たせる構成は、ユーザー体験やサーバー効率の面で適していません。

CSVエクスポート

管理画面からCSVを生成する場合は、通常のページより長い処理時間が必要になることがあります。

max_execution_time = 120
request_terminate_timeout = 150s
fastcgi_read_timeout 150s;

ただし、大量データを同期処理で生成すると、PHP-FPMワーカーを長時間占有します。

データ量が多い場合は、次の構成が適しています。

エクスポート要求を受け付ける
  ↓
ジョブキューへ登録する
  ↓
バックグラウンドでCSVを生成する
  ↓
完了後にユーザーへ通知する
  ↓
生成済みファイルをダウンロードする

大容量ファイルアップロード

100MBのファイルを受け付ける場合の例です。

max_execution_time = 300
max_input_time = 300
upload_max_filesize = 100M
post_max_size = 110M
memory_limit = 256M

Nginx側でもサイズ上限を合わせます。

client_max_body_size 110M;
client_body_timeout 300s;

ただし、CDNやロードバランサーを使用している場合は、アップロードサイズやリクエスト時間に固定上限が設けられていることがあります。

PHPとNginxだけでなく、経路上のサービスも確認する必要があります。

CLIバッチ処理

長時間処理は、WebリクエストではなくCLIで実行する方が適しています。

php bin/import.php

PHPコードでは、必要に応じて時間制限を解除します。

<?php

declare(strict_types=1);

set_time_limit(0);

foreach ($records as $record) {
    processRecord($record);
}

CLI版PHPは、標準で時間制限なしになっていることが多いですが、設定ファイルによって値が変更されている可能性があります。

外部APIやデータベースには、個別のタイムアウトを設定します。

また、OS側からバッチ全体の実行時間を制限する方法もあります。

timeout 30m php bin/import.php

無制限のバッチ処理は、停止や無限ループを検知しにくいため、PHP以外のレイヤーで上限を持たせると安全です。

タイムアウト値を設計する考え方

内側でエラー処理できるようにする

タイムアウトは、アプリケーションに近い内側の処理から先に終了するよう設計すると、エラー処理やログ記録を行いやすくなります。

一例は次のとおりです。

外部API接続タイムアウト      3秒
外部API全体タイムアウト     10秒
アプリケーション処理期限   20秒
PHP max_execution_time      30秒
PHP-FPM強制終了             40秒
Nginx FastCGIタイムアウト   45秒
ロードバランサー            50秒

外側のサーバーから突然切断される前に、アプリケーション側で処理を終了できれば、次の対応が可能です。

  • エラーレスポンスを返す
  • 例外を記録する
  • リトライする
  • 代替処理を実行する
  • ユーザーへ適切なメッセージを表示する

ただし、この順序は絶対的なルールではありません。

CDNやロードバランサーの上限が固定されている場合は、その時間を基準に内側を設計します。

max_execution_timeだけに依存しない

Linux環境では、max_execution_timeが外部I/O待ちを含まない場合があります。

そのため、PHPの設定だけでアプリケーション全体の処理期限を管理するのは不十分です。

必要に応じて、アプリケーション側で終了予定時刻を管理します。

<?php

$deadline = microtime(true) + 20.0;

foreach ($items as $item) {
    if (microtime(true) >= $deadline) {
        throw new RuntimeException('処理時間の上限を超えました。');
    }

    processItem($item);
}

外部APIへ複数回接続する場合は、残り時間に応じて各リクエストのタイムアウトを調整する設計も有効です。

タイムアウトを延長する前に確認すること

どのレイヤーが終了させているか特定する

タイムアウト問題では、最初にエラーの発生元を確認します。

主な確認対象は次のとおりです。

  • ブラウザの開発者ツール
  • CDNのログ
  • ロードバランサーのログ
  • Nginxのアクセスログ
  • Nginxのエラーログ
  • Apacheのエラーログ
  • PHP-FPMのログ
  • PHP-FPMのslowlog
  • アプリケーションログ
  • データベースのスロークエリログ
  • 外部APIの通信ログ

タイムアウト延長が根本解決とは限らない

タイムアウトを延長すると、一時的にエラーが消えることがあります。

しかし、処理が遅い原因を放置すると、次の問題が起きる可能性があります。

  • PHP-FPMワーカーの枯渇
  • 同時アクセスへの耐性低下
  • メモリ使用量の増加
  • データベース接続の占有
  • 外部API接続数の増加
  • サーバー全体の応答低下
  • 障害の長期化

処理時間が長い場合は、次の改善を優先します。

  • SQLを最適化する
  • キャッシュを利用する
  • 取得件数を減らす
  • 処理を分割する
  • 非同期処理へ移す
  • ジョブキューを導入する
  • 外部APIの呼び出し回数を減らす
  • リトライ回数を制限する
  • 適切なインデックスを追加する
  • タイムアウト発生箇所を計測する

PHPタイムアウト設定のまとめ

PHPのタイムアウトを調整する場合は、次の設定を確認します。

対象主な設定役割
PHPmax_execution_timePHPスクリプトの実行時間
PHPmax_input_time入力データの受信・解析時間
PHPdefault_socket_timeoutソケットストリームの既定タイムアウト
PHPhard_timeout終了処理が停止した場合の強制終了
PHP-FPMrequest_terminate_timeout長時間リクエストのワーカーを終了
PHP-FPMrequest_slowlog_timeout遅いリクエストのバックトレースを記録
Nginxfastcgi_connect_timeoutPHP-FPMへの接続確立待ち
Nginxfastcgi_send_timeoutPHP-FPMへの送信待ち
Nginxfastcgi_read_timeoutPHP-FPMからの応答待ち
Nginxproxy_read_timeoutHTTPバックエンドからの応答待ち
Nginxclient_body_timeoutクライアントからのリクエストボディ待ち
ApacheTimeoutApache全体の通信タイムアウト
ApacheProxyTimeoutプロキシ通信のタイムアウト
cURLCURLOPT_CONNECTTIMEOUT接続確立の最大時間
cURLCURLOPT_TIMEOUTcURL処理全体の最大時間
PDO・DBドライバ・DB固有設定接続、クエリ、ロック待ちの制限

PHPのタイムアウト設定で最も重要なのは、すべての値を単純に長くすることではありません。

エラーログやslowlogを確認し、どのレイヤーで、どの処理が、どの程度の時間を消費しているのかを特定する必要があります。

Maximum execution time exceededが発生している場合はPHP側、504 Gateway Timeoutの場合はNginxやロードバランサーなどの上流、502 Bad Gatewayの場合はPHP-FPMの停止や接続異常を中心に調査します。

長時間処理が必要な機能は、Webリクエスト内で完了させるのではなく、ジョブキューやバックグラウンド処理へ移すことが、安定したシステム運用につながります。

以上、PHPのタイムアウトの設定についてでした。

最後までお読みいただき、ありがとうございました。

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