🚀

[Rails] Nginx × Pumaを連携させる方法

2020/12/14に公開

Nginxを仕事で使う際、ネット上には既に参考になる設定が存在するのですが、
何故その設定が必要になるのか、その詳細などが分からなかったので調べてみました。

環境

  • Amazon Linux2
  • Nginx version 1.18.0
  • ACMとALBを使っている

Nginxの設定ファイル

実際に作ったnginxの設定ファイルがこちらになります。(一部変更)
上から順に解説して行きます。

upstream app {
  server unix:///var/www/rails/tmp/sockets/puma.sock fail_timeout=0;
 }

 server {
  listen 80 default_server;
  server_name _;

  location = / {
    proxy_pass http://app;
  }
}

server {
  listen 80;
  server_name example.com;

  error_log /var/www/rails/log/error.log;
  access_log /var/www/rails/log/access.log;

  root /var/www/rails/public;

  try_files $uri/index.html $uri.html $uri @app;

  location @app {
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Forward_For $proxy_add_x_forwarded_for;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;

    proxy_pass http://app;
  }
}

UNIXドメインソケットによる接続

まず、NginxとPumaを接続するための設定を書いていきます。
upstreamコンテキストで、バックエンドのアプリケーションサーバ、
pumaを使う前提なので、puma.sock を指定すればOKです。

upstream app {
  server unix:///var/www/rails/tmp/sockets/puma.sock fail_timeout=0;
}

このupstreamに付けた app という名前は後述する proxy_pass ディレクティブでまた使います。

UNIXドメインソケットはこちらの記事が参考になりました。
https://ascii.jp/elem/000/001/415/1415088/

serverコンテキストに外部からのアクセス方法を記述する

listenディレクティブ

受け付けるポート番号を指定します。
特定のIPアドレスでのみリクエストを受付けたい場合には、 IPアドレス:ポート番号 の様に
書けるみたいですが、まだ試したことはありません。

server_nameディレクティブ

ここでホスト名を指定します。

例)

192.168.0.1
hogepiyo.com

ホスト名がどのserver_nameにもマッチしなかった場合の設定を記述する場合は、
listenディレクティブに、default_server を追加することでこの様に書けます。

server {
  listen 80 default_server;
  server_name _;

  location = / {
    proxy_pass http://app;
  }
}

ALBのヘルチェックのために記述しました。locationのパスはヘルスチェック用のパスがあるならそちらを使用します。

error_log,access_logディレクティブでログを残す

このティレクティブでは、ログファイルの出力先のファイル名を記述します。

rootディレクティブ

静的コンテンツのパスをドキュメントルートに設定します。
railsでは public フォルダです。

root /var/www/rails/public

try_filesディレクティブ

引数の順番でファイルが存在するか調べてなければ、最後の引数の処理をします。
ここでは、ファイルが見つからなければwebアプリケーションへ転送するように設定しています。

try_files $uri/index.html $uri.html $uri @app;

locationディレクティブ

locationディレクティブではURLのパス名ごとの設定を記述できるのですが、
ここでは、@プレフィックスを使ってパス名には無関係にpumaへ転送するように設定しています。

# ファイルがなければ、pumaへ
try_files $uri/index.html $uri.html $uri @app;

location @app {
  # 省略

  # proxy_passディレクティブで転送先のサーバを指定
  proxy_pass http://app;
}

proxy_redirectディレクティブ

リダイレクトに使われるLocationヘッダをどの様に書き換えるかを指定します。
今回は応答を書き換えない off を指定しました。

off の他にも, default、また第一引数に書き換え対象の文字列、第二引数に書き換え先の文字列を指定出来ますが、off 以外指定したことないのでよく分からんです。

ヘッダの設定を追加する

何も設定しないと、webアプリケーションからはnginx(プロキシ)からのアクセスに見えてしまう
のと、基本的にクライアントからのリクエストのヘッダ情報はそのままバックエンドに送られます
が、 一部書き換えられてしまうヘッダ情報もあるので、注意が必要になります。

proxy_set_header ディレクティブを使ってヘッダの情報を付与していきます。
今回追加したのはコチラ。

追加したヘッダ情報 役割
Host サーバーのホスト名。upstream名に書き換えられるので、今回はnginxで使用できる変数 $host を指定しました。
X-Forwarded_For ロードバランサやプロキシを経由する時に送信元を判別するために利用する。複数の可能性がある。 $proxy_add_x_forwarded_for を指定しました。
X-Real_IP ロードバランサやプロキシを経由する時に送信元を判別するために利用する。X-Forwarded_Forと違い1つだけ。$remote_addr を指定しました。
X-Forwarded-Proto ALBをSSLの終端としているので、webアプリケーションにはセキュアなリクエスト(https)であることが伝わらないので、クライアントのプロトコルがhttpsであることを伝えるために $http_x_forwarded_proto を指定しました。

使ったnginxの変数

変数名 意味
$uri パラメータなしのリクエストURI
$remote_addr クライアントのIPアドレス
$host 次の優先順位で決まる。リクエストライン -> Hostヘッダフィールド -> リクエストに一致するサーバー名
$proxy_add_x_forwarded_for ALBはEC2に X-Forwarded-For: xxx.xxx.xxx.xxx を送ってくれるので、これを参照する。
$http_x_forwarded_proto ALBはEC2に X-Forwarded-Proto: https を送ってくれるので、これを参照する。

参考にしたページ

https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/X-Forwarded-For
https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/X-Forwarded-Proto
https://github.com/yandex/gixy/blob/master/docs/en/plugins/hostspoofing.md

Discussion