Laravel は PHP のフレームワークであり Web サイトの構築によく使われます。Web サイトを公開するために必要なサーバの設定も Laravel に用意されており、このデフォルトの設定ではまるパターンの一つを紹介します。
2021/05/31 時点の laravel/laravel リポジトリ(composer create-project laravel/laravelコマンドで参照するプロジェクト素組みリポジトリ)の Apache (Web サーバソフト)用設定ファイルが次です。
laravel/.htaccess at 131a10260e73e83e6b1cd63788b8f6584dd3e98a · laravel/laravel
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes
</IfModule>
RewriteEngine On
# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
# Send Requests To Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>
はまりやすいのはフォルダ参照 URL のリダイレクトである次です。
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d # リクエスト先がディレクトリでないならば次の行
RewriteCond %{REQUEST_URI} (.+)/$ # URLの末尾がスラッシュならば、末尾のスラッシュより前の部分をグループ %1 に格納して次の行
RewriteRule ^ %1 [L,R=301] # %1 に格納されたスラッシュなし URL に HTTP ステータス 301 でリダイレクト。LASTでこれ以上処理しない
曲者はこの HTTP ステータス 301 でリダイレクトの部分です。301 のリダイレクトは次リンクにある様に GET メソッドでリダイレクト先に送られます。このため POST 先の URL を / で締めた場合、GET でリダイレクトされることになり思ったように POST されません。
301 Moved Permanently – HTTP | MDN
これを対策するにはいくつか方法があり、二つを紹介します。
一つは次でメソッドを維持したままリダイレクトする方法です。
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d # リクエスト先がディレクトリでないならば次の行
RewriteCond %{REQUEST_URI} (.+)/$ # URLの末尾がスラッシュならば、末尾のスラッシュより前の部分をグループ %1 に格納して次の行
RewriteRule ^ %1 [L,R=308] # %1 に格納されたスラッシュなし URL に HTTP ステータス 308 でリダイレクト。LASTでこれ以上処理しない
例では 308 ですが、307 でもOKです。HTTP ステータスの 307, 308 はメソッドを維持したままリダイレクトを行うので POST が GET に化けることはありません。
307 Temporary Redirect – HTTP | MDN
308 Permanent Redirect – HTTP | MDN
この方法では通信相手が 307, 308 を適切に処理することを期待するため通信相手によっては失敗する点で欠点があります。
もう一つは次で、フォルダアクセスでも Laravel に処理を渡す方法です。
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d # リクエスト先がディレクトリでないならば次の行
RewriteCond %{REQUEST_URI} (.+)/$ # URLの末尾がスラッシュならば、末尾のスラッシュより前の部分をグループ %1 に格納して次の行
RewriteRule ^ index.php [L] # Laravel に処理を渡す。LASTでこれ以上処理しない
これは通信相手に何も期待せずサーバで完結します。しかしながら実際にファイルが存在する URL に末尾スラッシュをつけてしまった場合、デフォルトの .htaccess でも前述でもリダイレクトで返せていたファイルを返せなくなるという欠点があります。
私的におすすめなのは HTTP ステータス 308 を返してリダイレクトです。これは互いにプロトコルを守る前提で一番自然な形です。