Nginx 反向代理 Node.js 教學:從內部 Port 到正式網域

Node.js 程式只在 127.0.0.1:3000 運作時,本文教你用 systemd 管理程序、設定 Nginx reverse proxy、保留來源 Header,並排查 502、504 與 Redirect loop。

Nginx反向代理Reverse ProxyNode.jssystemdVPSproxy_pass502504正式環境網域部署

Node.js 程式能在 http://127.0.0.1:3000 回應,只代表應用本身啟動了;要讓正式網域穩定提供服務,還需要程序管理、Nginx 反向代理、正確 Header、日誌與 HTTPS。這篇從「內部 port 已經能用」開始,帶你把 Node.js 服務接到正式網域。

這篇會完成什麼

  • 確認 Node.js 應用只監聽本機或指定內部介面。
  • 用 systemd 管理啟動、重啟與日誌。
  • 安裝 Nginx 並設定 proxy_pass
  • 保留 Host、來源 IP 與原始連線協定資訊。
  • nginx -t、curl、systemctl 逐層驗證。
  • 排查 502、504、404 與 Redirect loop。

架構先看懂

使用者瀏覽器
    ↓ HTTPS / 443
Nginx
    ↓ HTTP / 127.0.0.1:3000
Node.js 應用

Node.js 不一定要直接開放 3000 給全網。讓 Nginx 對外處理網域、Header、連線與憑證,應用只監聽本機 port,通常比較容易維護。

動手前先確認

  • Ubuntu / Debian VPS 已可使用 SSH。
  • 網域 DNS 已指向 VPS,或你準備用 curl --resolve 測試。
  • Node.js 應用已經能在主機本機回應。
  • 你知道程式目錄、啟動檔與執行帳號。
  • 防火牆已允許真正需要的 80、443 與 SSH port。

先在 VPS 上確認應用:

curl -i http://127.0.0.1:3000/
sudo ss -lntp | grep :3000

如果這一步就失敗,先修 Node.js 應用,不要先改 Nginx。

用 systemd 管理 Node.js 程序

正式服務不適合靠 SSH 視窗裡的 node server.js 長期執行。SSH 斷線、主機重開機或程式崩潰後,服務都可能消失。

先確認 Node.js 的絕對路徑:

command -v node
readlink -f "$(command -v node)"

如果你使用 NVM,systemd 不會自動讀取互動式 shell 的 .bashrc。請把上面查到的實際路徑填進 ExecStart,並在升級 Node.js 後重新確認。

建立 /etc/systemd/system/myapp.service

[Unit]
Description=My Node.js application
After=network.target

[Service]
Type=simple
User=deploy
WorkingDirectory=/srv/myapp
Environment=NODE_ENV=production
ExecStart=/usr/bin/node /srv/myapp/server.js
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

請把 UserWorkingDirectory、Node 路徑與啟動檔換成你的實際值。不要為了省事直接用 root 執行一般 Web 應用。

套用並啟動:

sudo systemctl daemon-reload
sudo systemctl enable --now myapp
sudo systemctl status myapp --no-pager
sudo journalctl -u myapp -n 100 --no-pager

再次確認內部 port:

curl -i http://127.0.0.1:3000/

安裝並確認 Nginx

sudo apt update
sudo apt install nginx
sudo systemctl enable --now nginx
sudo nginx -t

如果系統使用 UFW,先確認 SSH 規則存在,再開放網站連線:

sudo ufw status verbose
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

不要直接把應用的 3000 port 對外開放,除非你有明確的網路與存取控制需求。

建立 Nginx reverse proxy 設定

建立 /etc/nginx/sites-available/myapp

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

    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;

        proxy_connect_timeout 10s;
        proxy_read_timeout 60s;
    }
}

這幾個 Header 的用途:

設定用途
Host讓應用知道使用者原本要求的網域
X-Real-IP傳遞目前連線來源 IP
X-Forwarded-For保留經過代理鏈的來源 IP 資訊
X-Forwarded-Proto告訴應用原始請求是 HTTP 還是 HTTPS

啟用站台:

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

nginx -t 成功後才 reload。若 symbolic link 已存在,不要重複建立;直接檢查它是否指向正確檔案。

驗證正式網域

curl -I http://app.example.com/
curl -sS -o /dev/null -w "%{http_code}\n" http://app.example.com/

DNS 尚未切換時,可以指定新主機 IP:

curl -I --resolve app.example.com:80:203.0.113.10 http://app.example.com/

完整用法請看 curl HTTP 與 API 排錯教學

HTTPS 要在什麼時候處理

先確認以下項目都正確,再設定 HTTPS:

  1. DNS 指向正確主機。
  2. Nginx HTTP 設定通過 nginx -t
  3. 內部 Node.js port 正常回應。
  4. 正式網域能透過 Nginx 取得預期內容。

不要在憑證與代理 Header 尚未確認時就疊加多層強制 Redirect,否則很容易形成 HTTP / HTTPS 無限循環。若應用框架需要判斷安全連線,必須正確信任反向代理提供的 X-Forwarded-Proto,而不是一律把請求視為 HTTP。

如果你不想自行處理憑證申請、設定與到期管理,可以查看侃瑞的 SSL 憑證全程代管;網站搬遷或切換前,也建議先跑一次 網站上線檢查表

WebSocket 要額外注意什麼

聊天、即時通知或部分開發工具會使用 WebSocket。這類服務需要正確傳遞 Upgrade Header,而且應用、Nginx 與前端 CDN 都要支援。不要在不確定需求時把所有連線一律設定成 upgrade;先確認你的應用框架與實際路徑,再依官方文件增加對應設定。

502 Bad Gateway 怎麼排查

依序檢查:

sudo systemctl status myapp --no-pager
sudo journalctl -u myapp -n 100 --no-pager
sudo ss -lntp | grep :3000
curl -i http://127.0.0.1:3000/
sudo tail -100 /var/log/nginx/error.log

常見原因:

  • Node.js 程式沒啟動或持續崩潰。
  • proxy_pass port 寫錯。
  • 應用只監聽另一個 IP 或 Socket。
  • systemd 的 ExecStart 使用了錯誤 Node.js 路徑。
  • SELinux、容器網路或額外防火牆阻擋內部連線。

504、404 與 Redirect loop

504 Gateway Timeout

Nginx 找得到上游,但上游太久沒有回應。查看應用、資料庫、外部 API 與慢查詢,不要只把 proxy_read_timeout 無限加長。

404 Not Found

先比較內部與外部結果:

curl -i http://127.0.0.1:3000/test
curl -i http://app.example.com/test

內部成功、外部失敗,通常是 Nginx locationproxy_pass 尾端斜線或路徑重寫問題。

Redirect loop

檢查:

curl -IL http://app.example.com/
curl -IL https://app.example.com/

常見是 CDN、Nginx 與 Node.js 三層都在做 HTTPS Redirect,或應用不信任 X-Forwarded-Proto

部署後的固定檢查

sudo nginx -t
systemctl is-active nginx
systemctl is-active myapp
curl -sS -o /dev/null -w "%{http_code}\n" https://app.example.com/

另外要定期確認:

  • Nginx 與應用日誌沒有持續錯誤。
  • 系統與 Node.js 版本仍在維護範圍。
  • 備份包含應用設定、必要資料與還原說明。
  • 監控不是只看「主機有開」,而是會打實際健康檢查 URL。

下一步

參考資料

需要一台可自行部署的 VPS?

適合 Node.js、Docker、API、背景服務與需要 root 權限的工作負載;也要把更新、備份與監控算進去。