schedule2020-04-06

Laravelの.htaccessの設定を読み解く

この記事では.htaccessの設定内容を解説していきます。

Laravelの.htaccessについて

Laravelのプロジェクトにアクセスがあるとpublic/index.phpを経由してrouteに振り分けられます。 その時、URIが画像やCSSなどpubilicディレクトリに存在するファイルを指している時はそのファイルを返し、それ以外はrouteに設定したページが返るような制御が行われています。 この制御部分を設定しているのがpublic/.htaccessです。

<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>

Laravel github .htaccess

mod_rewrite

mod_rewriteはPCRE正規表現パーサーに基づくルールベースの書き換えエンジンです。 アクセスしてきたURLやヘッダーを書き換えたり、リダイレクトしたりする機能です。 RewriteEngine Onはこの機能を有効にします。 あとで説明しますがRewriteCondは条件を示し、それに合うものがRewriteRuleによって書き換えられます。

Apache Module mod_rewrite | httpd.apache.org

mod_negotiation

mod_negotiationはURIと合うディレクトリ内のファイルを探して選択する機能です。 2つのオプションに-が付いており、それらの機能をオフにしています。

  • Multiviewsは、受け取ったURIのファイルがないときファイル拡張子を賢く探してくれるもの。 例えばURIに/path/dir/fooときてfooが無かったら、foo.*となるファイルを探します。
  • IndexesはURLがディレクトリにマップするリクエストであって、 且つDirectoryIndexで指定したファイルが ディレクトリ内に無ければ、ディレクトリ内の一覧を整形して返します。

public/bar/index.htmlみたいなHTMLやphpファイルがあるときに/bar/で返したいときは、DirectoryIndex index.html index.phpの設定で可能です。 移行中や静的なページがある案件だとたまに設定します。

RewriteCondとRewriteRule

細かく見ていく前にRewriteCondRewriteRuleについて説明します。

RewriteCondはif文の条件みたいなもので、RewriteCond <条件> [flags]といった書き方です。 条件は正規表現で表します。 RewriteCondを続けて書くとANDになり、オプションに[OR]が付くとORです。

RewriteRuleは文字列を置換します。 書き方はRewriteRule <検索文字列> <置換文字列> [flags]です。

また、どちらも予約語があり%{REQUEST_URI}, %{HTTP_USER_AGENT}といったように使うことができます。

mod_rewrite - Apache HTTP Server Version 2.4

Handle Authorization Header

# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

HTTPヘッダーのAuthorizationを環境変数に代入しています。 [E=foo:value]とすると環境変数fooに値valueを入れます。

E|envの例をみると、画像ファイルのリクエストかどうかログに残したい場合にこのように書きます。

RewriteRule &quot;\.(png|gif|jpg)$&quot; &quot;-&quot; [E=image:1]
CustomLog &quot;logs/access_log&quot; combined env=!image

ただ、環境変数HTTP_AUTHORIZATIONにいれるとアプリケーション側で利用できるのでしょうか? 末尾に乗せるNginxの書き方では書いていないようですので謎。 分かり次第追記します。

Redirect Trailing Slashes If Not A Folder...

# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]

LaravelのDocumentootはpublicに向けます。 ここでは、(1行目)URIがpublic配下に実在するディレクトリを指しておらず、(2行目)URIの末尾にスラッシュがついていたら、(3行目)末尾のスラッシュを取ったURIへリダイレクトします。 オプション-dはディレクトリかどうかで!で反対を意味するためディレクトリじゃない場合となります。 RewriteRule%1は、2行目のRewriteCondのかっこの中の文字列を指しています。

[L,R=301]LがLastを表しこれ以降のRiwrite処理を行わない、RがRidirectでHTTPステータス301でリダイレクトするという意味になります。

まとめると、URIが/foo/bar/public/foo/bar/ディレクトリがない場合は/foo/barにリダイレクトします。

Send Requests To Front Controller...

# Send Requests To Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]

ここも先ほどと似ています。 !-dpublic配下にURIのディレクトリがない、かつ、!-fファイルもない場合です。 その場合、public/index.phpを実行します。

おまけ:Nginxではどう書くのか?

Nginxでは.htaccessのようにディレクトリごとに設定ファイルがあることを認めていません。 そのため.htaccessは無く、LaravelのプロジェクトではDeployment #nginx | laravel.comにあるように設定をする必要があります。

## .htaccessのリダイレクト設定に似た箇所を抜粋
index index.html index.htm index.php;

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

Nginxの方が後に出ただけあってスッキリ書ける印象。

おわりに

まだ理解できていないこともありますが、結構Apacheの仕様を読み込めてきました。 Apacheについてはこんなのも書いているので良ければどうぞ。

参考