ESC

Start typing to search across invoices, services, domains, tickets, and more...

Search... Ctrl+K
Linuxサーバー

Nginx リバースプロキシ設定:Node/Python アプリ・WebSocket・実 IP・HTTPS・502 エラー対処

6 ステップ 12 分で読めます 3 回閲覧 0
目次

Node.js、Python(Flask・Django・FastAPI)、Go などのアプリは 127.0.0.1:3000 のようなローカルポートで待ち受けるのが一般的で、そのままインターネットに公開するのは適していません。この記事では Nginx リバースプロキシでドメインへのアクセスをバックエンドへ転送する設定、WebSocket 対応、クライアントの実 IP 取得、certbot による無料 HTTPS、502 Bad Gateway の原因調査を解説します。Debian/Ubuntu と Rocky Linux/AlmaLinux に対応しています。

ステップ1:Nginx のインストールとバックエンドの確認

まずアプリがサーバー内部から応答することを確認します。ここで応答しなければ、プロキシは必ず 502 を返します。アプリは 127.0.0.1 のみで待ち受け、systemd や pm2 で常駐させてクラッシュ時に自動再起動されるようにしておきましょう。

# Debian / Ubuntu
apt update && apt install -y nginx
# Rocky Linux / AlmaLinux
dnf install -y nginx

systemctl enable --now nginx

# Make sure the backend app is listening locally (example: port 3000)
ss -lntp | grep 3000
curl -I http://127.0.0.1:3000

ステップ2:Nginx リバースプロキシ設定(WebSocket 対応)

ドメインの DNS をサーバー IP に向けたら、/etc/nginx/conf.d/app.conf を作成します。map により通常リクエストと WebSocket のアップグレードの両方を処理でき、X-Forwarded-* ヘッダーでクライアント IP と元のプロトコルをアプリへ渡します。proxy_read_timeout を長めにすると長時間接続や遅い API が途中で切れません。大きなファイルを扱う場合は client_max_body_size を増やします。

# /etc/nginx/conf.d/app.conf  (included on Debian/Ubuntu and Rocky/AlmaLinux)
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    listen [::]:80;
    server_name app.example.com;

    client_max_body_size 20m;

    location / {
        proxy_pass http://127.0.0.1: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;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket support
        proxy_set_header Upgrade    $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        proxy_connect_timeout 10s;
        proxy_read_timeout    300s;
    }
}

ステップ3:設定チェック・Nginx リロード・ファイアウォール開放

変更のたびに nginx -t で構文を確認してからリロードします。Rocky/AlmaLinux では SELinux が Nginx からバックエンドへの接続を拒否するため、httpd_can_network_connect を有効にします。ファイアウォールで 80/443 を開放してください(詳細は「Linux ファイアウォール」の記事を参照)。

nginx -t && systemctl reload nginx
curl -I -H "Host: app.example.com" http://127.0.0.1

# Rocky/AlmaLinux (SELinux): allow Nginx to connect to backend ports
setsebool -P httpd_can_network_connect 1

# Open HTTP/HTTPS in the firewall
ufw allow 'Nginx Full'                                   # Debian/Ubuntu with ufw
firewall-cmd --permanent --add-service=http --add-service=https
firewall-cmd --reload                                    # Rocky/AlmaLinux

ステップ4:アプリでクライアントの実 IP を取得する

リバースプロキシ経由では、アプリから見た接続元はすべて 127.0.0.1 になります。Nginx は X-Real-IP と X-Forwarded-For で実 IP を渡しているので、フレームワーク側でローカルのプロキシを信頼する設定を加えます。Nginx の前段に CDN やロードバランサーがある場合は、Nginx の real_ip モジュールで CDN のアドレス範囲だけを信頼して復元し、ヘッダー偽装を防ぎます。

// Node.js / Express: trust the proxy on localhost
app.set('trust proxy', 'loopback');
// req.ip now returns the real client IP

# Python Flask / any WSGI app
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1)

# Gunicorn
gunicorn --bind 127.0.0.1:8000 --forwarded-allow-ips="127.0.0.1" app:app

# Only if Nginx itself sits behind a CDN or load balancer (http or server block):
set_real_ip_from 203.0.113.0/24;     # the CDN / LB address ranges
real_ip_header X-Forwarded-For;
real_ip_recursive on;

ステップ5:certbot でリバースプロキシを HTTPS 化

certbot の nginx プラグインは Let's Encrypt 証明書を取得し、server ブロックに 443 の待ち受けを追加します。--redirect を付けると HTTP から HTTPS へ自動転送されます。事前にドメインがこのサーバーを指し、80 番ポートが外部から到達可能であることを確認してください。証明書は 90 日有効で、自動更新されます。

# Debian / Ubuntu
apt install -y certbot python3-certbot-nginx
# Rocky Linux / AlmaLinux (EPEL)
dnf install -y epel-release && dnf install -y certbot python3-certbot-nginx

certbot --nginx -d app.example.com --redirect -m [email protected] --agree-tos --no-eff-email

# Renewal runs from a systemd timer; test it:
certbot renew --dry-run
systemctl list-timers | grep -i certbot
HTTPS 化後もアプリが http:// のリンクを生成する場合は、X-Forwarded-Proto を読んでいません。ステップ4のプロキシ信頼設定を行ってください。

ステップ6:Nginx 502 Bad Gateway の原因を調べる

502 は Nginx がバックエンドから正常な応答を得られなかったことを意味します。まず Nginx のエラーログを確認し、Connection refused ならアプリ停止かポート違い、Permission denied なら SELinux が主な原因です。また Node 17 以降では localhost が IPv6 の ::1 に解決され、アプリが [::1]:3000 だけで待ち受けて Nginx の 127.0.0.1 から届かないケースもあります。

tail -n 50 /var/log/nginx/error.log

# connect() failed (111: Connection refused)  -> app stopped or wrong port
# connect() ... (13: Permission denied)       -> SELinux: setsebool -P httpd_can_network_connect 1
# upstream prematurely closed connection      -> app crashed / restarted
# upstream timed out (110)                    -> app too slow: raise proxy_read_timeout or fix the app

systemctl status myapp
journalctl -u myapp -n 100 --no-pager
ss -lntp | grep -E ':3000|:8000'      # 127.0.0.1:3000 vs [::1]:3000 ?

よくある質問

WebSocket が 400 になる、またはすぐ切断される

proxy_http_version 1.1 と Upgrade・Connection ヘッダーが設定されているか確認してください。前段に CDN がある場合は CDN 側でも WebSocket を有効にします。アイドル接続は既定で 60 秒で切れるので、proxy_read_timeout を延ばすかアプリからハートビートを送ります。

1 台のサーバーで複数のアプリをプロキシできますか?

できます。ドメインごとに server ブロックを作り、それぞれ別のポートへ転送します。同一ドメイン内で location ごとに振り分けることも可能です。サイトごとに専用 IP が必要な場合は「サイト別 IP(Nginx)」の記事を参照してください。

504 Gateway Timeout になる

504 はバックエンドの応答が遅すぎることを示します。遅い処理を改善するか proxy_read_timeout を延ばし、アプリのログでデータベースや外部 API の待ちが発生していないか確認してください。

上記の手順で解決しない場合は、サポートチケットを送信して IMIDC の 24 時間 365 日対応テクニカルサポートへご連絡ください。サーバー IP、OS バージョン、実行したコマンド、エラー全文を添えていただくと、より迅速に調査できます。

この回答はお役に立ちましたか?

関連チュートリアル