Webサーバのフロントエンドとしてnginxを使った際に、バックエンドのWebアプリでもクライアントがリクエストした元のURLの情報(プロトコル、ホスト名、パス)を取得する方法を紹介する。
リバースプロキシ : nginx
nginxに届いた以下のクライアントからのリクエスト
https://hogehoge.com/some-path?value=1
を、次のURLに変換して内部のWebアプリにリクエストする。
http://192.168.1.4:5000/some-path?value=1
Webアプリで
クライアントがリクエストした元のURL
を取得したい。
上に示した「動作例」では以下が取得したいものに相当。
https://hogehoge.com/some-path?value=1
バックエンドにリバプロする際、ヘッダーに
クライアントがリクエストした元のURL
の情報を付加可能。
具体的にはnginx.conf のserverまたはlocationのスコープに、次のproxy_set_headerディレクティブを以下のように追加する。
proxy_set_header X-Forwarded-For $host; # ホスト名
proxy_set_header X-Forwarded-Proto $scheme; # プロトコル
proxy_set_header Request-Path $request_uri; # リクエストURLのパス部分
先の例で、クライアントがhttps://hogehoge.com/some-path?value=1
をリクエストしている場合、nginxの変数には以下のような値が格納される。
proxy_set_headerの第一引数にはヘッダーのフィールド名を指定する。こちらは用途別に推奨されるフィールド名があるようだが、筆者はこの辺に精通していないので適切な名前になっていない可能性がありご注意を。
$host
(=hogehoge.com
)に関して。筆者が試した環境では$remote_addr
、$proxy_add_x_forwarded_for
も同様にhogehoge.com
が格納されていた。
Webアプリで実際に取得してみる
PythonのFlaskを使うと、例えば次のようにFlaskのRequestクラスから取得できる。
from flask import Flask, request
app = Flask(__name__)
@app.route("/some-path")
def some_path():
# 元のURLの情報を取得
host = request.headers.getlist("X-Forwarded-For") # $hostを取得
scheme = request.headers.getlist("X-Forwarded-Proto") # $schemeを取得
request_uri = request.headers.getlist("Request-Path") # $request_uriを取得
return ""
if __name__ == "__main__":
app.run(host='0.0.0.0')
どういう時に使える?
Webサーバでユーザ認証する場合などに、クライアントからのリクエストをAuth0等の外部サイトに誘導する場合がある。認証が済んだ後にもともとリクエストしたページに直でリダイレクトさせたい場合、WebサーバでそのURLを覚えておく必要がある。本記事の内容はこのようなケースで活用できるはず。