やりたかったこと

Node.jsで作ったアプリをVPS上でnginxを使って公開したかった。 ポート3000で動いているアプリを80番・443番ポートで外部公開するためにリバースプロキシの設定が必要だった。

リバースプロキシの仕組み

クライアント → nginx(80/443)→ Node.jsアプリ(3000)

nginxがリクエストを受けてバックエンドのアプリに転送する。 直接アプリのポートを公開しなくて済むのでセキュリティ面でも有利。

基本的なリバースプロキシ設定

/etc/nginx/sites-available/myapp を作成する。

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

シンボリックリンクを作成して有効化する。

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Node.jsアプリ側の設定

アプリはlocalhost(127.0.0.1)でlistenするだけでよい。

const express = require('express');
const app = express();

app.listen(3000, '127.0.0.1', () => {
  console.log('Server running on port 3000');
});

外部に直接ポート3000を公開しない場合はファイアウォールで塞いでおく。

sudo ufw deny 3000
sudo ufw allow 80
sudo ufw allow 443

WebSocketのプロキシ設定

WebSocketを使う場合は追加のヘッダーが必要。

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

UpgradeConnection ヘッダーがないとWebSocketのハンドシェイクが失敗する。

複数アプリを同一サーバーで動かす

ドメインやパスで振り分けることができる。

# ドメインで振り分け
server {
    listen 80;
    server_name app1.example.com;
    location / {
        proxy_pass http://localhost:3000;
    }
}

server {
    listen 80;
    server_name app2.example.com;
    location / {
        proxy_pass http://localhost:4000;
    }
}
# パスで振り分け
server {
    listen 80;
    server_name example.com;

    location /api/ {
        proxy_pass http://localhost:3000/;
    }

    location / {
        proxy_pass http://localhost:4000;
    }
}

SSL(HTTPS)対応

CertbotでSSLを設定すると /etc/nginx/sites-available/myapp が自動で書き換えられる。

sudo certbot --nginx -d example.com

Certbotが proxy_pass の設定はそのままにしてSSLの設定だけ追加してくれる。

ハマったポイント

  • proxy_set_header Host $host; を忘れるとバックエンド側でホスト名が取れない
  • proxy_http_version 1.1; を指定しないとWebSocketが接続できない
  • proxy_pass http://localhost:3000/; の末尾スラッシュの有無でパスの扱いが変わる(末尾スラッシュありだとlocationのパス部分が除去される)
  • Node.jsアプリが127.0.0.1でlistenしていないと 502 Bad Gateway になる
  • nginx -t でテストしてからreloadしないと設定ミスで本番が落ちる

リバースプロキシの設定後にHTTPSも対応させたい場合はnginxにLet’s EncryptのSSL証明書を設定する方法(certbot)で無料のSSL証明書を取得できる。

関連記事

おすすめのVPS/ドメイン

VPSを使って本番環境を構築するなら、以下のサービスがおすすめです。