自分への備忘録として。
http://example.com/api/hogehoge?foo=bar
にアクセスされたら、
http://example.com:8000/hogehoge?foo=bar
という形でリバースプロキシさせたいとき、ついついこんな風に書きたくなります。
location ~ ^/api/(.*)$ {
proxy_pass http://example.com:8000/$1$is_args$args;
}
が、これは間違い。下のように書くべき。
location ~ ^/api/.*$ {
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://localhost:8000;
}
どういうことかというと、nginxのlocationディレクティブにおける正規表現のキャプチャは、uriデコードされたものがキャプチャされてしまうのです。
なので、もし
http://example.com/api/hoge%20hoge
こういうURIエンコード済みのURIだった場合、
"http://example.com:8000/hoge hoge"
こんな空白入りのURIになってしまい、502 Bad Requestとかで死にます。
それを阻止するには、デコード前の状態をキャプチャしないとダメで、それを行うにはrewriteディレクティブを使います。
これは、公式ドキュメントにも書いてあります。
http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass
If the proxy_pass directive is specified with a URI, then when a request is passed to the server, the part of a normalized request URI matching the location is replaced by a URI specified in the directive:
(中略)
When the URI is changed inside a proxied location using the rewrite directive, and this same configuration will be used to process a request (break):
location /name/ {
rewrite /name/([^/]+) /users?name=$1 break;
proxy_pass http://127.0.0.1;
}
In this case, the URI specified in the directive is ignored and the full changed request URI is passed to the server.
しかも、rewriteに後続する、proxy_passディレクティブには変数とかは使用してはいけません。余計なものを付けなければ、変更された完全なリクエストURIがサーバーに渡されます。
あー怖かった。