ローカルでは動くのに本番環境でファイルアップロードが失敗する——そんな謎のエラーを引き起こすサイズ・権限・php.ini設定の落とし穴を、実案件ベースのチェックリストで完全解説します。
ローカルでは動くのに、なぜ本番だけ失敗するのか
こんな経験はありませんか?
「開発環境でテストしたら問題なくアップロードできた。でも本番にデプロイしたら、画像が一切アップされない。エラーメッセージもなく、ただ失敗するだけ……」
PHPのファイルアップロードに関するトラブルは、Web開発の現場で頻繁に遭遇する問題のひとつです。特にやっかいなのが、ローカル環境では完璧に動作するのに、本番サーバーに上げた途端に機能しなくなるというケース。原因が一つとは限らず、複数の要因が絡み合っているため、経験豊富なエンジニアでも解決に時間を取られることがあります。
この記事では、弊社がこれまで神奈川・首都圏を中心に手がけてきた20年超の実案件から得た知見をもとに、「本番だけ失敗する」ファイルアップロード問題の原因と、実践的な解決手順をまとめます。
なぜ「本番だけ」失敗するのか——環境差異の本質
ローカルと本番で挙動が異なる最大の理由は、PHP・サーバーの設定値が環境ごとに独立して管理されているからです。多くの場合、ローカル開発環境はXAMPPやDockerなど開発者がカスタムしやすい構成になっており、php.iniの制限値が緩く設定されています。一方、本番のレンタルサーバーやVPSはセキュリティや安定性を優先し、デフォルトで厳しい制限がかかっていることが多い。
主な原因は大きく3つのカテゴリに分類できます。
flowchart TD
A[アップロード失敗] --> B{原因を特定}
B --> C[php.ini設定値の差異]
B --> D[ディレクトリの権限不足]
B --> E[Webサーバーの設定制限]
C --> F[upload_max_filesize / post_max_size / max_file_uploads]
D --> G[chmod 755/777の確認]
E --> H[Nginx client_max_body_size / Apache LimitRequestBody]この3つを順番に押さえていけば、ほとんどのケースは解決できます。それぞれ具体的に見ていきましょう。
原因①:php.iniの設定値——まず真っ先に確認すべき項目
ファイルアップロードに関係するPHPの設定値は複数あり、すべてが連動している点に注意が必要です。一つだけ変えても他がボトルネックになっていると、症状は変わりません。
// 現在の設定値を確認するための簡易スクリプト
// 本番確認後は必ず削除すること
echo '<pre>';
echo 'upload_max_filesize: ' . ini_get('upload_max_filesize') . "\n";
echo 'post_max_size: ' . ini_get('post_max_size') . "\n";
echo 'max_file_uploads: ' . ini_get('max_file_uploads') . "\n";
echo 'memory_limit: ' . ini_get('memory_limit') . "\n";
echo 'max_execution_time: ' . ini_get('max_execution_time') . "\n";
echo '</pre>';
設定値の優先順位を把握しておくことも重要です。post_max_size は upload_max_filesize より常に大きくなければなりません。たとえば、upload_max_filesize = 32M にもかかわらず post_max_size = 8M(デフォルト値)のままにしていると、リクエスト全体のサイズ制限に引っかかり、アップロードは静かに失敗します。
原因②:ディレクトリのパーミッション問題
アップロード先ディレクトリの権限設定は、見落とされがちな盲点です。あるクライアント案件で、本番環境にLaravelアプリをデプロイしてから「画像投稿だけできない」という問い合わせを受けたことがあります。調査してみると、storage/app/public ディレクトリのオーナーが root のままになっており、PHPが実行するWebサーバーユーザー(www-data)に書き込み権限がなかったというケースでした。
# アップロード先ディレクトリの権限を確認
ls -la /var/www/html/storage/app/public/
# Webサーバーのユーザーを確認(ApacheならApache、NginxならWWW-data)
ps aux | grep nginx
ps aux | grep apache
# 所有者をWebサーバーユーザーに変更
sudo chown -R www-data:www-data /var/www/html/storage
# 権限を適切に設定(755推奨、書き込みが必要なディレクトリは775)
sudo chmod -R 755 /var/www/html/storage
sudo chmod -R 775 /var/www/html/storage/app/public
# Laravelの場合は専用コマンドも活用
php artisan storage:link
777は絶対に使わないこと。「とりあえず権限を全開にすれば動く」という判断でパーミッションを777に設定してしまうエンジニアがいますが、これはセキュリティ上のリスクになります。755や775の組み合わせで適切に解決するのが正しいアプローチです。
原因③:WebサーバーレベルのBodyサイズ制限
PHPの設定を直しても解決しない場合、NginxやApache側にも独自のサイズ制限が存在することを見落としているケースがあります。PHPより前段でリクエストを受け付けるWebサーバーが、大きなPOSTリクエストを拒否していると、PHPの設定を変えても意味がありません。
# /etc/nginx/nginx.conf または各サーバーブロック内に追記
server {
# クライアントからのリクエストボディの最大サイズ
# 0にすると制限なし(非推奨)
client_max_body_size 64M;
# タイムアウト設定も合わせて調整
client_body_timeout 120s;
send_timeout 120s;
location / {
# PHPへの転送設定
fastcgi_read_timeout 120s;
}
}
設定変更後は必ずWebサーバーを再起動してください。
# Nginx
sudo systemctl reload nginx
# Apache
sudo systemctl reload apache2
# PHP-FPMも忘れずに再起動
sudo systemctl restart php8.2-fpm
よくある失敗パターン——これをやると沼にはまる
実案件で繰り返し見てきた「やりがちな失敗」を正直にお伝えします。
失敗①:エラーログを確認せずに感覚で直そうとする
最も多いパターンです。まずは /var/log/nginx/error.log、/var/log/apache2/error.log、Laravelであれば storage/logs/laravel.log を確認することが先決。エラーメッセージには必ず原因のヒントが含まれています。
失敗②:post_max_size を変えずに upload_max_filesize だけ増やす
先述のとおり、両者はセットで変更する必要があります。upload_max_filesize > post_max_size という状態は機能しません。post_max_size は upload_max_filesize の2倍程度を目安に設定してください。
失敗③:設定変更後にPHP-FPMを再起動しない
php.ini を変更してもPHP-FPMプロセスを再起動しないと設定が反映されません。「変えたのに変わらない」と悩む前に phpinfo() で現在の設定値を実際に確認する習慣をつけると良いでしょう。
失敗④:$_FILES エラーコードを確認していない
PHPはアップロード失敗時に詳細なエラーコードを返します。これを無視して独自の判定ロジックだけで判断しようとすると、原因の特定が遅れます。
// $_FILES のエラーコードを必ず確認する
if ($_FILES['file']['error'] !== UPLOAD_ERR_OK) {
$errorMessages = [
UPLOAD_ERR_INI_SIZE => 'php.iniのupload_max_filesizeを超過',
UPLOAD_ERR_FORM_SIZE => 'フォームのMAX_FILE_SIZEを超過',
UPLOAD_ERR_PARTIAL => '一部のみアップロードされた',
UPLOAD_ERR_NO_FILE => 'ファイルが送信されていない',
UPLOAD_ERR_NO_TMP_DIR => 'テンポラリディレクトリが存在しない',
UPLOAD_ERR_CANT_WRITE => 'ディスクへの書き込みに失敗',
UPLOAD_ERR_EXTENSION => 'PHP拡張によりアップロードが停止',
];
$code = $_FILES['file']['error'];
error_log('Upload error: ' . ($errorMessages[$code] ?? '不明なエラー: ' . $code));
}
開発・運用でお困りなら
システム開発
設計から運用まで、堅牢なシステムを構築します
※ 通常1営業日以内にご返信します
まとめ:本番デプロイ前に必ず実行するチェックリスト
本番環境でのファイルアップロード問題は、事前の確認で大半が防げます。次のデプロイ前に以下を必ず実行してください。
環境の違いによるトラブルは、設定値を把握・統一する運用フローを作ることで大幅に減らせます。ただ、サーバー構成やアプリケーションの要件によっては、上記の手順だけでは解決しないケースも存在します。
弊社Fivenine Designでは、Laravelを使ったファイルアップロード機能の実装から、本番サーバーの設定最適化まで一貫して対応しています。「調べてみたが解決しない」「そもそも設定を触るのが不安」という場合は、お気軽にご相談ください。