Dev Study
← Webを支えるソフトウェア
レッスン 6 / 13·約 6 分·第3期 Webを支えるソフトウェア

Nginx設定を書く — proxy_pass とスラッシュ

リクエストがアプリケーションに届くまで
  1. ブラウザURL入力
  2. DNS名前→IP
  3. ルータ経路選択
  4. ロードバランサ振り分け
  5. Nginx受け口今ここ
  6. アプリ処理
  7. DB保存

第31週でどの location が選ばれるかを見ました。今回はその location の中で、上流のアプリへ何を渡すかを決めます。

まず結論

`proxy_pass` の行末にスラッシュを付けるかどうかで、上流に渡るパスが変わります。URIを伴えば location に一致した部分が置換され、伴わなければリクエストURIがそのまま渡ります。

そして置換されるのは、クライアントが送った文字列そのものではありません。`%XX` をデコードし `..` を解決した後の「正規化されたURI」が対象です。

? 読む前に予測

`location /api/ { proxy_pass http://backend/; }` に `/api/users` が来ました。上流のアプリが受け取るパスはどれでしょうか。

当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。

抽象原理

スラッシュ1文字が分岐点

proxy_pass の書き方は2通りしかありません。ホストの後ろにパスがあるか、無いかです。公式はこの2つを別の規則で扱います。

実機(nginx/1.30.4)で上流が受け取ったURI表
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.

実機で確かめた結果です。

正規化が先に効く(location /api/ + proxy_pass http://…/)コマンド出力
/api/a%2Fb -> UPSTREAM_GOT=/a/b
/api/x/../users -> UPSTREAM_GOT=/users

%2F はスラッシュにデコードされ、.. は解決されてから上流へ渡ります。上流のアプリは、クライアントが送った文字列そのものを見ているわけではありません。

✓ ここまでの確認

`location /api/ { proxy_pass http://backend/; }` に `/api/x/../users` が来ました。上流が受け取るのはどれですか。

⇄ 2つを見比べる
具体例 A

proxy_pass のURI置換

location が基準にあたります。proxy_pass にURIを書くと基準部分がそれに置き換わり、書かなければ基準を含めたURIがそのまま渡ります。末尾スラッシュ1文字が「URIを書いたかどうか」を決めます。

同じリクエスト `/api/users` の行き先表
proxy_pass の書き方 上流が受け取る
http://backend/ /users ← /api/ が / に置換
http://backend /api/users ← 置換されない
具体例 B

相対パスと絶対パスの解決

ファイルパスやURLの参照も、先頭のスラッシュの有無で解決先が変わります。スラッシュで始まればルートからの絶対指定になり、基準となる現在位置は捨てられます。始まらなければ現在位置からの相対指定として繋がります。

基準が /api/v1/ のときの解決先表
書き方 解決される先
/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を付けずに書くよう指示している。違反時の挙動は未確認。

自分の現場で確かめる

読んだだけでは身につきません。手元の環境で実際に見てみてください。

環境:

理解度チェック

読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。

問題 1 / 3正解: 0

「`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が返る。