エラーの内容
スクリプトがこの壁にぶつかって停止します:
Fatal error: Maximum execution time of 30 seconds exceeded in /var/www/html/process.php on line 142
PHPには実行時間の上限(max_execution_time)が組み込まれており、デフォルトは30秒です。スクリプトがこれを超えると、PHPは強制終了します。クリーンアップも正常終了もなく、ただ死ぬだけです。
このエラーは主に、大量のCSVインポート、応答の遅いサードパーティAPIの呼び出し、PDF・レポート生成、画像リサイズ処理、または数千件のデータベース行を反復するループなどで発生します。
根本原因
スクリプトが単一のHTTPリクエスト内で処理しすぎています。ロジック自体が遅いか、外部の何か(10秒かかるDBクエリ、応答しないAPI、メモリに丸ごと読み込まれた200MBのファイルなど)の待機でブロックされています。
重要な区別として:PHP CLI(php script.php)はデフォルトでmax_execution_time = 0(無制限)です。このエラーはほぼ例外なく、ApacheやNginxがHTTPリクエストを処理するWebコンテキストで発生します。
修正方法1:php.iniで上限を引き上げる
最も恒久的な修正方法で、サーバー全体に適用されます。まず、有効なphp.iniを探します:
php --ini | grep 'Loaded Configuration'
# または
php -r "echo php_ini_loaded_file();"
ファイルを編集して上限を引き上げます:
; php.ini
max_execution_time = 120
次にWebサーバーを再起動します:
# Apache
sudo systemctl restart apache2
# Nginx + PHP-FPM
sudo systemctl restart php8.2-fpm
sudo systemctl restart nginx
Web公開スクリプトで0に設定するのは避けてください。実行時間を無制限にすると、一つの遅いリクエストがPHP-FPMワーカーを無期限に占有し続けるため、DoS攻撃の温床となります。
修正方法2:スクリプト内で設定する(即時オーバーライド)
php.iniを変更できない場合、つまり共有ホスティング、他人のサーバー、深夜2時の緊急対応など、set_time_limit()を使えば設定ファイルへのアクセスなしに対応できます:
<?php
// スクリプトの最上部に記述する
set_time_limit(120); // 120秒
// またはループの各反復でカウンターをリセットする
foreach ($large_dataset as $item) {
set_time_limit(30); // 各アイテムごとに30秒リセット
process($item);
}
set_time_limit(0)はそのスクリプト実行中の上限を完全に無効化します。50,000件のレコードを処理するCLIバッチジョブには問題ありませんが、不特定多数が使う公開エンドポイントには使わないでください。
**注意:**PHPがセーフモードで実行されている場合、set_time_limit()は何もしません。現代の環境では稀ですが、レガシーインフラを使っている場合は知っておく価値があります。
修正方法3:.htaccessでオーバーライドする(Apacheのみ)
AllowOverrideが有効な共有Apacheホスティングでは、グローバル設定に触れることなくディレクトリ単位で設定できます:
# .htaccess
php_value max_execution_time 120
再起動は不要です。Apacheはリクエストごとに.htaccessを読み込みます。
修正方法4:プロジェクトルートのphp.ini(cPanel・一部ホスト)
多くの共有ホストでは、プロジェクトフォルダに直接php.iniまたは.user.iniを置くことができます:
# public_html/php.ini または public_html/.user.ini
max_execution_time = 120
どのファイルが有効かはホストのドキュメントで確認してください。.user.iniはPHP-FPM向けの設定ファイルで、モダンなスタックを使っているホストではより一般的です。
修正方法5:PHP-FPMプール設定
PHP-FPMを直接使っている場合は、グローバルなphp.iniを変更せず、プールごとに上限をオーバーライドできます:
# /etc/php/8.2/fpm/pool.d/www.conf
php_admin_value[max_execution_time] = 120
設定を反映させるためにFPMサービスを再起動します:
sudo systemctl restart php8.2-fpm
修正方法6:Laravel・Artisanコマンドの場合
キュージョブや時間のかかるArtisanコマンドの場合、handle()メソッドの先頭で上限を設定します:
<?php
public function handle()
{
set_time_limit(0); // CLIでは無視されるが、明示的な記述が暗黙より優れる
ini_set('max_execution_time', 0);
// 時間のかかる処理
}
キューワーカーのタイムアウトも確認してください。PHPの上限とLaravelのワーカータイムアウトは別々のカウンターです:
# タイムアウトを明示して(秒単位)ワーカーを実行する
php artisan queue:work --timeout=120
修正方法7:遅いコードを診断して修正する
上限を引き上げるのは時間稼ぎにすぎません。何も根本的に解決していません。スクリプトが本当に5分必要なら、それは調査すべき警告サインです。
まず、実際にどこで時間がかかっているかを計測します:
<?php
$start = microtime(true);
// ... 対象のコードブロック ...
$elapsed = microtime(true) - $start;
error_log("Block A took: {$elapsed}s");
よくある原因:
- 遅いDBクエリ —
EXPLAINで分析してください。50万行のテーブルにインデックスがないと、10ミリ秒のクエリが45秒のフルスキャンになることがあります。 - 外部APIの呼び出し — 常に明示的なタイムアウトを設定してください:
curl_setopt($ch, CURLOPT_TIMEOUT, 10)。設定しないと、応答しないサーバーをPHPが無限に待ち続けます。 - 大きなファイルの処理 — 全体をメモリに読み込む代わりにストリーム処理してください。1GBのファイルを
file_get_contents()で読み込むと、メモリも時間も枯渇します。 - 大きな配列のネストしたループ — O(n²)のロジックはすぐに限界に達します。チャンク処理を使うか、アルゴリズムを見直してください。
バルク処理の場合、本当の解決策はHTTPリクエストから処理を切り離すことです:
<?php
// Webリクエスト内で10,000件を同期処理してはいけない。
// ジョブをディスパッチして即座に返す。
ImportJob::dispatch($file_path);
return response()->json(['status' => 'processing']);
修正の確認
設定した値を信頼せず、実行時に実際に有効な値を確認してください:
<?php
echo ini_get('max_execution_time');
またはphpinfo()(本番環境には絶対に残さないこと)を使い、max_execution_timeを検索してください。マスター値とローカル値の両方が表示されます。値が異なる場合は何かが設定をオーバーライドしています。.htaccess、プール設定、.user.iniの順に確認してください。
失敗していたスクリプトを再実行し、fatalエラーなく完了することを確認してください。
クイックリファレンス
- php.ini —
max_execution_time = 120(グローバル、再起動が必要) - スクリプト先頭 —
set_time_limit(120);(スクリプト単位、即時反映) - .htaccess(Apache) —
php_value max_execution_time 120(再起動不要) - .user.ini —
max_execution_time = 120(PHP-FPMのディレクトリ単位) - CLI — デフォルトは
0(無制限)のため、このエラーは発生しない
予防策
このエラーを引き起こすパターンは予測可能です。HTTPリクエスト内での同期的・ブロッキングな処理です。早い段階でそのパターンを断ち切ってください。
外部システムに接触する処理には実行時間のロギングを追加しましょう。遅いリクエストの監視も設定してください。Apacheの%Dフォーマット変数はリクエスト時間をマイクロ秒で記録します。New RelicやDatadogを使えば、あるルートが継続的に10秒以上かかる場合にアラートを受け取れます。
経験則として、Webリクエストは3秒以内に返すべきです。30秒以上必要な処理は、バックグラウンドジョブ(キューワーカー、cronタスク、非同期プロセス)で行うべきです。HTTPレスポンスは「処理をキューに追加しました」と返してすぐに終了するべきです。

