extensions — ユーザー指定のファイル拡張子をチェックするバリデーションルール

validation
  • カテゴリ: validation
  • 掲載バージョン: Laravel 12・PHP 8.4
  • 名前空間 / FQCN / コマンド: ルール文字列(extensions:jpg,png のように指定)
  • 関連: file / image / mimes / mimetypes
  • 変更履歴: Laravel 9.x 系で追加(9.0 ドキュメントには未掲載。9.32 以降を推奨)(Qiita)

要点(TL;DR)

  • 何に使うか: 「ファイル名の拡張子」が指定したものかどうかをチェックする。
  • 最低限の使い方: 'file' => ['required', 'file', 'mimes:png', 'extensions:png'];
  • よくある罠
    • 中身(MIMEタイプ)はチェックしない → mimes / mimetypes と必ず併用する必要あり(laravel.com)
    • extensions だけだと、偽装されたファイル名でも通ってしまう
    • 拡張子は基本 小文字・ピリオド無し で書く(jpgpng など)

概要

extensions ルールは、アップロードされたファイルの「ユーザーが付けた拡張子」(ファイル名の .xxx 部分)をチェックするためのバリデーションルールです。(laravel.com)
mimes / mimetypes がファイルの中身から MIME タイプを推測して検証するのに対し、extensions はあくまでファイル名ベースで検証します。
そのため、実務では MIME と拡張子が一致していることを保証するために、mimes / mimetypes とセットで使うのが前提です。(laravel.com)

構文 / シグネチャ

// 文字列ルール
'field' => 'extensions:ext1,ext2,...';

// 配列ルール
'field' => ['extensions:ext1,ext2,...'];

引数

引数必須既定値説明
extensionsstring | arrayなし許可する拡張子のリスト。ピリオド不要。jpg,png,pdf など

※ 実際の実装ではカンマ区切り文字列として指定します。

戻り値

  • bool
    • 指定された拡張子リストのいずれかに一致すれば true(バリデーション通過)
    • それ以外は false(バリデーション失敗)

例外 / 副作用

  • 直接的な例外は投げません(通常のバリデーションエラーとして扱われる)。
  • ファイルの内容は読み取りません(ユーザー指定の拡張子のみ参照)。(laravel.com)

使用例

最小例

アップロードされたテキストファイルが .txt 拡張子かをチェックしつつ、MIME もテキストであることを保証する例。

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::post('/upload', function (Request $request) {
    $validated = $request->validate([
        'file' => [
            'required',
            'file',
            'mimes:txt',      // 中身(MIME)がテキストか
            'extensions:txt', // ファイル名が .txt か
        ],
    ]);

    // 実際の保存処理
    $path = $request->file('file')->store('uploads');

    return response()->json(['path' => $path]);
});

実務例

画像アップロード API で、「PNG または JPEG 以外のアップロードを防ぐ」かつ
「拡張子偽装(例: photo.png だが中身が PDF)」も避けたいケース。

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::post('/profile/photo', function (Request $request) {
    $validated = $request->validate([
        'photo' => [
            'required',
            'file',
            'mimes:jpeg,jpg,png',      // 中身が画像かどうか
            'extensions:jpeg,jpg,png', // 拡張子も画像か
            'max:2048',                // 2MB まで
        ],
    ]);

    $path = $request->file('photo')->store('profile-photos');

    // ユーザー情報にパスを保存するイメージ
    $request->user()->forceFill([
        'photo_path' => $path,
    ])->save();

    return response()->json(['path' => $path]);
});

通過 / 失敗ケース

'file' => ['mimes:txt', 'extensions:txt'] の場合:

ファイル名中身の MIME結果理由
memo.txttext/plain通過MIME=txt かつ 拡張子=txt
memo.sqltext/plain失敗拡張子が txt ではない
memo.txtapplication/pdf失敗MIME が text/plain ではない
memo.TXTtext/plain実装依存大文字小文字の扱いは環境依存になる場合があるので小文字推奨
memotext/plain失敗拡張子が存在しない

よくある落とし穴・注意

  • extensions 単体で使わない
    • 中身を一切見ずに、ファイル名だけで判定するため、偽装ファイルを防げません。
    • 公式も mimes / mimetypes と組み合わせて使うよう明記しています。(laravel.com)
  • 拡張子の書き方
    • ピリオドは不要('.jpg' ではなく 'jpg')。
    • 小文字で統一するのが無難。
  • 古い Laravel では使えない可能性
    • Laravel 9 以前のドキュメントには extensions の記載がなく、9.32 以降の利用が推奨されます。(Qiita)

代替・関連APIとの比較

  • mimes:jpg,png
    • ファイルの中身を読んで MIME タイプを判定し、「PNG 画像として正しいか」を検証。(laravel.com)
    • ファイル名が photo.txt でも、中身が PNG なら通ってしまう。
  • extensions:jpg,png
    • ファイル名の拡張子だけを見る。
    • 中身は PNG ではないが、ファイル名が photo.png なら通ってしまう。
  • 実務での選定基準
    • 安全に行くなら両方
      • 例: ['file', 'mimes:png', 'extensions:png']
    • 単純な UI チェックだけなら extensions だけでも良いが、セキュリティ的には非推奨。

テスト例(Pest)

<?php

use Illuminate\Support\Facades\Validator;

it('validates file extension and mime type', function () {
    // テスト用の UploadedFile を作成
    $file = new \Illuminate\Http\UploadedFile(
        __DIR__.'/fixtures/sample.txt', // 実ファイル
        'sample.txt',                  // クライアント側のファイル名
        'text/plain',                  // MIME
        null,
        true
    );

    $validator = Validator::make([
        'file' => $file,
    ], [
        'file' => [
            'required',
            'file',
            'mimes:txt',
            'extensions:txt',
        ],
    ]);

    expect($validator->passes())->toBeTrue();
});

トラブルシュート(エラー別)

症状 / エラー例原因対処
The file must have a file extension of: txt. 等のエラーアップロードしたファイルの拡張子が許可リスト外extensions: のリストを見直す/ユーザーに再アップロードを促す
The file must be a file of type: txt. 等のエラーmimes ルールに合致していない(MIME が異なる)ファイルの中身が正しいか、mimes の指定拡張子を見直す
期待したファイルなのにバリデーション失敗拡張子の大文字・小文字やピリオド付きでの指定ミスextensions:TXT ではなく extensions:txt など、小文字・ピリオド無しで指定
開発環境では通るが本番で失敗する本番と開発で Laravel バージョンや PHP の差異Laravel のバージョンを確認し、extensions がサポートされているか確認

カスタムメッセージ

resources/lang/ja/validation.php などでメッセージを上書きできます。

'extensions' => ':attribute は次のいずれかの拡張子である必要があります: :values。',

バリデーション定義側では通常どおり attribute 名を設定します。

$request->validate([
    'file' => ['required', 'file', 'mimes:txt', 'extensions:txt'],
], [
    'file.extensions' => 'アップロードできるのは .txt ファイルのみです。',
]);

参考リンク

  • Laravel 12.x Validation — File Validation (extensions の公式説明)(laravel.com)
  • Laravel 10.x バリデーション(日本語ドキュメント、mimes / mimetypes の説明)(Readouble)
  • Qiita: Laravelでファイル拡張子のバリデーションを実装する際の注意点(extensionsmimes の組み合わせ解説)(Qiita)
  • Laravel 8.x バリデーション(古いバージョンとの差分確認用)(Readouble)
レン (Wren)

こんにちは。レンです。

Laravelのコードの森に住んでいる、小さな案内役です。
ルーティングの枝やクラスの影を歩きながら、コードの流れや仕組みを眺めています。

このサイトでは、Laravelの基本から実装のコツまで、開発で役立つポイントを静かに整理しています。
難しいことを増やすのではなく、コードの見通しが少し良くなるヒントを届けるのが役目です。

「この処理はどこに書くのがいいのか」
「Laravelではどう考えると整理できるのか」

そんな疑問に、小さなメモを残すような気持ちで記事を書いています。

コードを書いている途中で迷ったとき、
このサイトが少し立ち止まって整理できる場所になればうれしいです。

レン (Wren)をフォローする