この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:5
ページ更新者:T
更新日時:2026-06-11 07:10:02

タイトル: 画像保存時にInternal Server Error
SEOタイトル: Laravel / PHP 画像アップロード時の 500 Internal Server Error 原因と修正チェックリスト

この記事の要点
  • 第一に Laravel ログを見る: tail -f storage/logs/laravel.log。500 は必ず原因がスタックトレースに出る
  • サイズ上限: php.ini の upload_max_filesize / post_max_size / memory_limit、Nginx の client_max_body_size
  • storage 権限: chmod -R 775 storage bootstrap/cachechown -R www-data:www-data
  • storage:link 忘れ: php artisan storage:link。public/storage シンボリックリンクが無いと参照できない
  • 画像ライブラリ未導入: Intervention Image / GD / Imagick が無いと resize で 500(apt install php-gd

まず最初に: 真のエラーをログで確認

500 Internal Server Error はブラウザ側には詳細が出ません。必ずサーバ側ログを見ます:

# Laravel 本体のログ
tail -f /var/www/myapp/storage/logs/laravel.log

# PHP-FPM の致命的エラー
sudo tail -f /var/log/php8.2-fpm.log

# Nginx エラーログ(リクエスト弾かれるとここ)
sudo tail -f /var/log/nginx/error.log

# Apache の場合
sudo tail -f /var/log/apache2/error.log

# システム全体
sudo journalctl -xe -u nginx -u php8.2-fpm

laravel.log に何も出ていない場合はLaravel に到達する前で死んでいる可能性が高く、PHP-FPM / Nginx 側のエラーを見ます。

原因 1: アップロードサイズ上限

最も多いのがファイルサイズ上限超過。複数の場所で制限がかかっています:

場所設定既定推奨
php.iniupload_max_filesize2M20M〜
php.inipost_max_size8Mupload_max_filesize 以上
php.inimemory_limit128M256M〜
php.inimax_execution_time30120
Nginxclient_max_body_size1M20M〜
ApacheLimitRequestBody無制限必要に応じ
# 現在値を確認
php -i | grep -E "upload_max_filesize|post_max_size|memory_limit"

# または PHP コードで
<?php
echo 'upload_max_filesize: ' . ini_get('upload_max_filesize') . PHP_EOL;
echo 'post_max_size: ' . ini_get('post_max_size') . PHP_EOL;
echo 'memory_limit: ' . ini_get('memory_limit') . PHP_EOL;

php.ini の修正

; /etc/php/8.2/fpm/php.ini

upload_max_filesize = 20M
post_max_size = 25M
memory_limit = 256M
max_execution_time = 120
max_input_time = 120
file_uploads = On
# PHP-FPM 再起動
sudo systemctl restart php8.2-fpm

Nginx の修正

# /etc/nginx/sites-available/myapp

server {
    listen 80;
    server_name example.com;
    client_max_body_size 25M;   # ★ 追加

    location / {
        ...
    }
}
sudo nginx -t
sudo systemctl reload nginx

原因 2: storage / public のパーミッション

Laravel は storage/ 配下にファイルを書き込みます。Web サーバユーザー(www-data / nginx)に書き込み権限が必要:

cd /var/www/myapp

# 所有者を Web サーバユーザーに
sudo chown -R www-data:www-data storage bootstrap/cache

# 書き込み権限
sudo chmod -R 775 storage bootstrap/cache

# シンボリックリンク作成
php artisan storage:link
# → public/storage → ../storage/app/public が作られる

# 確認
ls -l public/storage
# public/storage -> /var/www/myapp/storage/app/public

権限不足の場合 laravel.log に次のように出ます:

[2026-05-17 10:23:14] production.ERROR: file_put_contents(/var/www/myapp/storage/app/public/uploads/xxx.jpg): Failed to open stream: Permission denied

原因 3: storage:link 忘れ

ファイルは保存できているのにURL で開けない場合、storage:link 未実行が原因:

php artisan storage:link

# 既に存在エラーが出る場合
ls -l public/storage
# 壊れたリンクなら一度削除
rm public/storage
php artisan storage:link

原因 4: GD / Imagick 未インストール

Intervention Image などでresize / crop / 変換を行うと、GD か Imagick が必要です:

# 確認
php -m | grep -E "gd|imagick"

# Ubuntu/Debian
sudo apt install php8.2-gd php8.2-imagick

# RHEL/CentOS
sudo dnf install php-gd php-imagick

# 再起動
sudo systemctl restart php8.2-fpm

# 確認
php -r "var_dump(extension_loaded('gd'), extension_loaded('imagick'));"

未インストールだとログに:

Intervention\Image\Exception\NotSupportedException: Encoder format (jpeg) is not supported.
# または
Class "Imagick" not found

原因 5: validation で 422 ではなく 500 になっている

本来バリデーション失敗は 422 だが、コードの作りで 500 になっていることも:

<?php
// ❌ NG: ファイルが nullable なのに getClientOriginalExtension を呼ぶ
public function store(Request $request)
{
    $ext = $request->file('image')->getClientOriginalExtension();
    // → Call to a member function getClientOriginalExtension() on null
}

// ✅ OK: 必ず validate してから
public function store(Request $request)
{
    $validated = $request->validate([
        'image' => 'required|image|max:5120',  // 5MB
    ]);

    $path = $request->file('image')->store('uploads', 'public');
    return response()->json(['path' => $path]);
}

原因 6: SELinux(CentOS / RHEL)

# storage/ への書き込みが SELinux にブロックされる
sudo setsebool -P httpd_unified 1
sudo chcon -R -t httpd_sys_rw_content_t /var/www/myapp/storage
sudo chcon -R -t httpd_sys_rw_content_t /var/www/myapp/bootstrap/cache

# 一時的に確認
sudo setenforce 0   # Permissive
# ↑ で直るなら SELinux 起因

切り分けチェックリスト

  1. storage/logs/laravel.log 確認
  2. PHP-FPM / Nginx エラーログ確認
  3. ファイルサイズが上限内か(5MB なら 5242880 byte)
  4. php -i | grep upload
  5. ls -l storage/ public/storage で権限と symlink
  6. php -m | grep gd
  7. php artisan storage:link 実行
  8. SELinux / AppArmor 確認
  9. ディスク容量 df -h
  10. inode 残量 df -i

典型的な正しい実装

<?php
// app/Http/Controllers/ImageController.php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\Facades\Image;

class ImageController extends Controller
{
    public function store(Request $request)
    {
        $request->validate([
            'image' => 'required|image|mimes:jpeg,png,jpg,webp|max:5120',
        ]);

        $file = $request->file('image');
        $filename = Str::uuid() . '.' . $file->getClientOriginalExtension();

        // リサイズして保存
        $img = Image::make($file)->resize(1200, null, function ($c) {
            $c->aspectRatio();
            $c->upsize();
        });

        Storage::disk('public')->put("uploads/{$filename}", (string) $img->encode());

        return response()->json([
            'url' => Storage::url("uploads/{$filename}"),
        ]);
    }
}

FAQ

Q: laravel.log に何も出ない
A: ① 権限不足で laravel.log 自体が書けない、② エラーが Laravel に到達する前(Nginx で弾かれ)、③ APP_DEBUG=false + コード内 catch で握り潰し。

Q: ローカルでは動くが本番で 500
A: 本番の php.ini / Nginx 設定値、GD/Imagick 有無、storage 権限を本番側で再確認。php artisan config:clear も忘れずに。

Q: 大きい画像で OOM Killer に殺される
A: memory_limit を 512M に。または resize 前に画像サイズチェック・サーバ側で imagick の policy.xml を緩める。