- ブラウザURL入力
- DNS名前→IP
- ルータ経路選択
- ロードバランサ振り分け
- Nginx受け口今ここ
- アプリ処理
- DB保存
第31週でどの location が選ばれるかを見ました。今回はその location の中で、上流のアプリへ何を渡すかを決めます。
まず結論
`proxy_pass` の行末にスラッシュを付けるかどうかで、上流に渡るパスが変わります。URIを伴えば location に一致した部分が置換され、伴わなければリクエストURIがそのまま渡ります。
そして置換されるのは、クライアントが送った文字列そのものではありません。`%XX` をデコードし `..` を解決した後の「正規化されたURI」が対象です。
`location /api/ { proxy_pass http://backend/; }` に `/api/users` が来ました。上流のアプリが受け取るパスはどれでしょうか。
当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。
スラッシュ1文字が分岐点
proxy_pass の書き方は2通りしかありません。ホストの後ろにパスがあるか、無いかです。公式はこの2つを別の規則で扱います。
location proxy_pass リクエスト 上流が受け取った/api/ http://127.0.0.1:9001/ /api/users /users/noslash/ http://127.0.0.1:9001 /noslash/users /noslash/users/remap/ http://127.0.0.1:9001/remote/ /remap/users /remote/users
1行目と2行目の設定の違いは、末尾のスラッシュ1文字だけです。それで上流に届くパスが変わります。
URIを伴う場合——
the part of a normalized request URI matching the location is replaced by a URI specified in the directive
/api/users のうち location に一致する /api/ が、ディレクティブの / に置換されて /users になります。伴わない場合は——
the request URI is passed to the server in the same form as sent by a client
**「置換するか、しないか」であって、「取り除くか、残すか」ではありません。**3行目のように /remote/ を指定すれば /remap/ がそこへ置き換わります。
`location /noslash/ { proxy_pass http://backend; }`(末尾スラッシュ無し)に `/noslash/users` が来たとき、上流が受け取るのは何ですか。
置換されるのは、クライアントが送った文字列ではない
もう一段あります。置換の対象は normalized request URI です。正規化の定義は location 側に書かれています。
The matching is performed against a normalized URI, after decoding the text encoded in the “%XX” form, resolving references to relative path components “.” and “..”, and possible compression of two or more adjacent slashes into a single slash.
実機で確かめた結果です。
/api/a%2Fb -> UPSTREAM_GOT=/a/b/api/x/../users -> UPSTREAM_GOT=/users
%2F はスラッシュにデコードされ、.. は解決されてから上流へ渡ります。上流のアプリは、クライアントが送った文字列そのものを見ているわけではありません。
`location /api/ { proxy_pass http://backend/; }` に `/api/x/../users` が来ました。上流が受け取るのはどれですか。
proxy_pass のURI置換
location が基準にあたります。proxy_pass にURIを書くと基準部分がそれに置き換わり、書かなければ基準を含めたURIがそのまま渡ります。末尾スラッシュ1文字が「URIを書いたかどうか」を決めます。
proxy_pass の書き方 上流が受け取るhttp://backend/ /users ← /api/ が / に置換http://backend /api/users ← 置換されない
相対パスと絶対パスの解決
ファイルパスやURLの参照も、先頭のスラッシュの有無で解決先が変わります。スラッシュで始まればルートからの絶対指定になり、基準となる現在位置は捨てられます。始まらなければ現在位置からの相対指定として繋がります。
書き方 解決される先/users /users ← 基準を捨てて置き換えusers /api/v1/users ← 基準に繋ぐ
proxy_pass のスラッシュと、パス指定のスラッシュ。表層はまったく別の話ですが、共通している構造は何でしょうか。
どちらも「基準」が先にあり、書き方によってその基準が生き残るか捨てられるかが決まります。
この章のまとめ
proxy_passは URIを伴うか伴わないかの2通り。**末尾スラッシュだけでも「伴う」**にあたる。- 伴えば location に一致した部分が置換され(
/api/users→/users)、伴わなければそのまま渡る(/noslash/users)。 - 置換対象は normalized なURI。
%2Fはデコードされ..は解決された後に渡る(/api/x/../users→/users)。 - 正規表現location・named location では、公式が URIを付けずに書くよう指示している。違反時の挙動は未確認。
自分の現場で確かめる
読んだだけでは身につきません。手元の環境で実際に見てみてください。
理解度チェック
読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。
「`proxy_pass` の末尾のスラッシュは、付けても付けなくても同じ」という説明の評価はどれですか。
用語集(4語)
- URIを伴う proxy_pass
- `http://backend/` のようにホストの後ろにパスがある形。**末尾のスラッシュだけでも該当する。** 正規化されたリクエストURIのうち location に一致した部分が、ここに書いたURIへ置換される。
- URIを伴わない proxy_pass
- `http://backend` のようにホストだけの形。リクエストURIがクライアントの送った形のまま上流へ渡る。
- 正規化 (normalized) されたURI
- `%XX` 形式のデコード、`.` と `..` の解決、連続するスラッシュの単一化を済ませたURI。location の照合も proxy_pass の置換も、この形に対して行われる。
- 末尾スラッシュ無しURIへの301
- location がスラッシュで終わる前置文字列で定義され、proxy_pass 等で処理される場合、スラッシュ無しの同じURIへのリクエストには、スラッシュを付けたURIへの301が返る。