バックエンド 2026.09.30

PHPファイルアップロードが本番だけ失敗する原因と対処法

約14分で読めます

ローカルでは動くのに本番環境でファイルアップロードが失敗する——そんな謎のエラーを引き起こすサイズ・権限・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つを順番に押さえていけば、ほとんどのケースは解決できます。それぞれ具体的に見ていきましょう。


バックエンド開発でお困りですか?

API設計・DB最適化・システム構築など、ご相談ください

無料で相談する

原因①: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));
}

バックエンド開発でお困りですか?

API設計・DB最適化・システム構築など、ご相談ください

無料で相談する

開発・運用でお困りなら

システム開発

設計から運用まで、堅牢なシステムを構築します

200件以上の制作実績 顧客満足度97% 初回相談無料

※ 通常1営業日以内にご返信します

まとめ:本番デプロイ前に必ず実行するチェックリスト

本番環境でのファイルアップロード問題は、事前の確認で大半が防げます。次のデプロイ前に以下を必ず実行してください。


環境の違いによるトラブルは、設定値を把握・統一する運用フローを作ることで大幅に減らせます。ただ、サーバー構成やアプリケーションの要件によっては、上記の手順だけでは解決しないケースも存在します。

弊社Fivenine Designでは、Laravelを使ったファイルアップロード機能の実装から、本番サーバーの設定最適化まで一貫して対応しています。「調べてみたが解決しない」「そもそも設定を触るのが不安」という場合は、お気軽にご相談ください。

この記事をシェア

システム開発のご相談、受付中です

設計・開発・テスト・運用まで、ビジネスに合ったシステムを構築します。 初回相談は無料です。

※ 1営業日以内にご返信いたします

この技術でお困りなら

無料でプロに相談できます

相談する
AIに無料相談