MeWrite Docs

Laravel: Encoded forward slash (%2F) not routing correctly

LaravelでURLエンコードされたスラッシュ(%2F)を含むパラメータが正しくルーティングされない問題

概要

LaravelのルートパラメータにURLエンコードされたスラッシュ(%2F)が含まれている場合、正しくルーティングされず404エラーになる問題です。ファイルパスやURLをパラメータとして渡す際に発生します。

エラーメッセージ

404 Not Found
Symfony\Component\HttpKernel\Exception\NotFoundHttpException
No route found for "GET /files/path%2Fto%2Ffile"
Route parameter decoded incorrectly

原因

  1. Webサーバーの処理: Apache/Nginxが%2Fをデコードしてしまう
  2. Laravelのルート解決: スラッシュがパス区切りとして解釈される
  3. URLデコードのタイミング: パラメータがデコードされる前にルートマッチングが行われる
  4. 正規表現の制限: デフォルトの正規表現がスラッシュを許可しない

解決策

1. whereでスラッシュを許可

1
2
3
4
5
6
7
// routes/web.php
Route::get('/files/{path}', [FileController::class, 'show'])
    ->where('path', '.*');  // すべての文字を許可(スラッシュ含む)

// または明示的にスラッシュを含める
Route::get('/files/{path}', [FileController::class, 'show'])
    ->where('path', '[a-zA-Z0-9\/\-\_\.]+');

2. グローバルパターンを定義

1
2
3
4
5
6
7
8
9
// app/Providers/RouteServiceProvider.php
public function boot(): void
{
    // グローバルにスラッシュを許可するパラメータを定義
    Route::pattern('path', '.*');
    Route::pattern('filepath', '.*');

    parent::boot();
}

3. Base64エンコードを使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// コントローラー
class FileController extends Controller
{
    public function show($encodedPath)
    {
        $path = base64_decode($encodedPath);
        // $path = "path/to/file"
        return response()->file(storage_path($path));
    }
}

// ルート
Route::get('/files/{encodedPath}', [FileController::class, 'show']);

// URL生成時
$url = route('files.show', ['encodedPath' => base64_encode('path/to/file')]);
// /files/cGF0aC90by9maWxl

4. Apache設定の変更

1
2
3
4
5
6
# .htaccess または Apache設定
# %2Fをデコードしないようにする
AllowEncodedSlashes NoDecode

# または
AllowEncodedSlashes On
1
2
3
4
5
6
7
8
9
# VirtualHost設定
<VirtualHost *:80>
    ServerName example.com
    AllowEncodedSlashes NoDecode

    <Directory /var/www/html/public>
        AllowOverride All
    </Directory>
</VirtualHost>

5. Nginx設定の変更

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# nginx.conf
server {
    listen 80;
    server_name example.com;

    # URIをデコードしない
    location / {
        # $request_uri を使用(デコードされない)
        # $uri はデコードされる

        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        # REQUEST_URIをそのまま渡す
        fastcgi_param REQUEST_URI $request_uri;
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
    }
}

6. クエリパラメータとして渡す

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// スラッシュを含むパスはクエリパラメータで渡す
Route::get('/files', [FileController::class, 'show']);

// URL: /files?path=path/to/file
// コントローラー
public function show(Request $request)
{
    $path = $request->query('path');
    // ...
}

7. カスタムエンコーディング

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// app/Helpers/PathEncoder.php
class PathEncoder
{
    public static function encode(string $path): string
    {
        // スラッシュを別の文字に置換
        return str_replace('/', '---', $path);
    }

    public static function decode(string $encoded): string
    {
        return str_replace('---', '/', $encoded);
    }
}

// ルート
Route::get('/files/{path}', [FileController::class, 'show']);

// URL生成
$url = route('files.show', ['path' => PathEncoder::encode('path/to/file')]);
// /files/path---to---file

// コントローラー
public function show($path)
{
    $realPath = PathEncoder::decode($path);
    // ...
}

8. ルートモデルバインディングのカスタマイズ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// app/Providers/RouteServiceProvider.php
public function boot(): void
{
    Route::bind('file', function ($value) {
        // URLデコードされた値を処理
        $path = urldecode($value);
        return File::where('path', $path)->firstOrFail();
    });

    parent::boot();
}

9. ミドルウェアでの処理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// app/Http/Middleware/DecodePathParameter.php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class DecodePathParameter
{
    public function handle(Request $request, Closure $next)
    {
        // ルートパラメータを取得してデコード
        $route = $request->route();

        if ($route && $route->hasParameter('path')) {
            $path = $route->parameter('path');
            // 二重エンコードの場合の対応
            $decoded = rawurldecode($path);
            $route->setParameter('path', $decoded);
        }

        return $next($request);
    }
}

10. フォールバックルート

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// routes/web.php
// 特定のプレフィックスで始まるURLをすべてキャッチ
Route::fallback(function (Request $request) {
    $path = $request->path();

    if (str_starts_with($path, 'files/')) {
        $filePath = substr($path, 6);  // 'files/' を除去
        return app(FileController::class)->show($filePath);
    }

    abort(404);
});

11. 正規表現でスラッシュを明示的に許可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// スラッシュを含む複数階層のパス
Route::get('/browse/{path}', [BrowseController::class, 'show'])
    ->where('path', '(.+)');  // 1文字以上の任意の文字列

// 複数のパラメータ
Route::get('/copy/{source}/{destination}', [CopyController::class, 'copy'])
    ->where([
        'source' => '.*',
        'destination' => '.*',
    ]);

デバッグ方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// ルートの確認
php artisan route:list

// リクエストパラメータの確認
Route::get('/debug/{path}', function ($path) {
    dd([
        'raw' => request()->getRequestUri(),
        'decoded' => $path,
        'server' => request()->server('REQUEST_URI'),
    ]);
})->where('path', '.*');

よくある間違い

  • whereを指定せずにスラッシュを含むパラメータを使う
  • ApacheのAllowEncodedSlashesを設定し忘れる
  • Base64エンコード時にURL安全な形式を使わない
  • クエリパラメータで簡単に解決できるのにルートパラメータにこだわる

関連エラー

参考リンク

Laravel の他のエラー

最終更新: 2025-12-14