Webソケットを利用するサービスをNginx経由で動作させるための最低限の設定と、設定項目の意味を整理しました。
前提
本記事はnginxの設定方法をある程度知っていることを前提としています。
ディレクティブの説明などは別の記事をご参照ください。
一般公開しない、開発・分析用途を想定しています。
セキュリティやパフォーマンスについては考慮していないのでご注意ください。
具体例として、データ分析で人気のJupyterLabをNginx経由で利用するためのコードをベースに記事にしました。
Webソケットを使わない設定
まずは、シンプルな転送設定を確認します。
サブドメイン経由のアクセスをJupyterLabのサーバーへ転送しているだけです。
server {
listen 80;
server_name jupyter.sample.domain;
location / {
proxy_pass http://jupyterlab:8888;
}
}
この場合、ログインはできるのですがWebソケットによる通信を開始できず、サービス内のシェル機能や肝心の分析機能が動きません。
Webソケットが使える転送設定
次のように変更するとWebソケットが利用可能になります。
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name jupyter.sample.domain;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
location / {
proxy_pass http://jupyterlab:8888;
}
}
詳しい説明は公式のWebSocket proxyingにも記載があります。
日本語ではこちらの記事が詳しいです。
追加項目の解説
以降、順を追って追加した項目を説明します。
map
ある変数の内容に応じて、別の変数の値を決めたいときに使用する機能です。
if文で置き換える事も可能です。
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
$http_upgradeの値が空('')の場合は、
$connection_upgrade = close
それ以外の場合は、
$connection_upgrade = upgrade
という処理が行われています。
proxy_http_version 1.1;
HTTPプロトコルバージョンを指定します。
webソケットに必要なkeep-aliveはバージョン1.1以降で利用可能ですが、デフォルトでは1.0なので明示的に指定する必要があります。下記の「proxy_set_header Upgrade」や「proxy_set_header Connection」も1.1以降でのみ利用可能なヘッダ設定です。
proxy_set_header Host $host;
ホスト情報をプロキシサーバのものではなく、接続元のものに書き換えます。
これはWebソケットの利用に必ず必要な設定ではありませんが(先に挙げた公式の記事の設定例には入っていません)、JupyterLabでは無いとwebソケットの接続に失敗したので追加しました。
proxy_set_header Upgrade $http_upgrade;
プロトコルスイッチという仕組みで通信プロトコルをHTTP/1.1からwebsocketに変更します。
プロコルスイッチはHTTP/1.1から利用可能になった仕組みです。
先ほどの公式の記事に記載があります。
ここで紹介しているUpgradeと次のConnectionは、Hop-by-hopヘッダと呼ばれる特殊なヘッダ情報です。通常のEnd-to-endヘッダは情報を最終的な宛先に届けるために使用するのに対し、Hop-by-hopヘッダは(存在する場合は)プロキシに対して値を渡すために使用されます。
proxy_set_header Connection $connection_upgrade;
送信先にコネクションを維持するか否かを伝えるためのヘッダーです。
ここでは、$http_upgradeの値に応じて設定済みの、「upgrade」あるいは「close」を設定しています。
おまけ
追加項目の説明は以上ですが、少し重めな設定を記載します。
筆者自身、下記の設定をそのまま使っているわけではありませんが、うまくいかない時のトライ&エラー用も兼ねて載せておきます。
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name jupyter.sample.domain;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 600s;
# proxy_set_header Connection "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
proxy_set_header X-Frame-Options SAMEORIGIN;
proxy_buffering on;
proxy_buffers 256 16k;
proxy_buffer_size 16k;
# proxy_set_header X-Forwarded-For $remote_addr;
# GCPのLBへ転送する際に発生したセキュリティ対応
# proxy_set_header X-Content-Type-Options nosniff;
proxy_hide_header X-Content-Type-Options;
# セキュリティを強化するため、元々なかったヘッダを追加する場合に利用
# add_header X-Frame-Options SAMEORIGIN;
# add_header X-XSS-Protection "1; mode=block";
# add_header X-Content-Type-Options nosniff;
# add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; img-src 'self' https://gravatar.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; object-src 'none'";
client_max_body_size 50M;
location / {
proxy_pass http://jupyterlab:8888;
access_log /var/log/nginx/jupyterlab_access.log flush=5s;
error_log /var/log/nginx/jupyterlab_error.log flush=5s;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# include /etc/nginx/proxy.d/*.conf;
}
参考資料
Nginxのリバースプロキシ設定関連
Nginxを用いたWebSocketサーバのReverseProxy構成及びSSL/TLS接続
HTTPヘッダーについて
nginxでX-Forwarded-Forの値に$proxy_add_x_forwarded_forを安易に使わない