XServer VPSでNginxリバースプロキシ+TypeScript Node.jsを本番公開する手順【2025年版】
結論:VPS公開は「PM2で常駐」→「Nginxで443を受ける」の2層に分ければ迷わない
XServer VPSでTypeScript製のNode.jsアプリを本番公開するとき、詰まる原因のほとんどは構成の役割分担が曖昧なことにある。整理すると、やることは次の2層だけだ。
- アプリ層:TypeScriptをビルドし、PM2で
localhost:3000に常駐させる(外部には出さない) - 公開層:Nginxが80/443を受け取り、
localhost:3000へリバースプロキシする。SSLはCertbotで自動取得・自動更新
この分離ができていれば、アプリのデプロイとTLS・ドメイン周りを独立して触れる。以下、Ubuntu 24.04 LTSを前提にコマンドまで通しで書く。OSイメージやプラン仕様は変更されることがあるため、契約前に{{A8:xserverVps}}の公式ページで最新のスペックと料金を確認してほしい。
なぜCloudflare WorkersやVercelではなくVPSなのか
正直に書くと、静的サイトや軽いAPIならサーバーレスのほうが楽で安い。VPSを選ぶ理由は次のようなケースに限られる。
| 要件 | VPS | サーバーレス(Workers等) |
|---|---|---|
| 常駐プロセス・WebSocket長時間接続 | 得意 | 制約あり |
| 実行時間の長いバッチ・cron | 自由 | タイムアウト制限あり |
| ffmpeg等のネイティブバイナリ利用 | 自由 | ほぼ不可 |
| OS・ミドルウェアの運用負荷 | 自分持ち | ほぼゼロ |
| コスト予測 | 固定 | 従量(跳ねる可能性) |
つまり「常駐・長時間処理・ネイティブ依存」がある場合にVPSが効く。逆にそれらが不要なら、Cloudflare Workersでできることを先に検討したほうが運用は圧倒的に楽だ。VPSはOSアップデートもセキュリティパッチも自分の責任になる、というデメリットは最初に飲み込んでおきたい。
Step 1:初期セットアップとファイアウォール
XServer VPSはコンソール側にもパケットフィルターがある。OS内のufwとVPS側フィルターの両方で80/443/22を許可しないと疎通しない。ここが初回の定番の詰まりどころだ。
# 一般ユーザー作成(rootで直接運用しない)
adduser deploy
usermod -aG sudo deploy
# ufw設定
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status
SSHは公開鍵認証に切り替え、/etc/ssh/sshd_configでPasswordAuthentication noにしておく。VPSは公開IPを持つため、放置すると総当たり攻撃のログがすぐ溜まる。
Step 2:Node.jsとPM2の導入
ディストリのaptに入るNodeはバージョンが古いことが多い。nvmでLTSを入れるのが無難だ。
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash
source ~/.bashrc
nvm install --lts
node -v
npm install -g pm2
Step 3:TypeScriptアプリをビルドして常駐させる
本番でts-nodeを使うのは避ける。起動が遅く、型チェックの失敗が実行時まで持ち越される。ビルド済みJSを実行するのが原則だ。
{
"scripts": {
"build": "tsc -p tsconfig.json",
"start": "node dist/index.js"
}
}
アプリ側は必ず127.0.0.1にバインドする。0.0.0.0だとNginxを迂回してポート3000に直接アクセスされうる。
import express from "express";
const app = express();
app.get("/health", (_req, res) => res.json({ ok: true }));
app.listen(3000, "127.0.0.1", () => {
console.log("listening on 127.0.0.1:3000");
});
PM2の設定はJSONやecosystemファイルで管理し、環境変数もここに寄せる。
npm run build
pm2 start dist/index.js --name myapp --time
pm2 save
pm2 startup systemd # 出力されたコマンドをsudoで実行
pm2 startupで表示されたコマンドを実行し忘れると、サーバー再起動後にアプリが上がらない。ここは実際にVPSをsudo rebootして復帰を確認しておくべきポイントだ。
Step 4:Nginxのリバースプロキシ設定
/etc/nginx/sites-available/myapp.confを作る。
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
# WebSocket用
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 実クライアントIPをアプリに渡す
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_read_timeout 60s;
}
}
sudo ln -s /etc/nginx/sites-available/myapp.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
X-Forwarded-Protoを渡さないと、Expressでreq.protocolが常にhttpになり、リダイレクトループやCookieのSecure属性の判定を誤る。Express側ではapp.set("trust proxy", 1)を入れておく。この「プロキシ配下でホスト・プロトコルを正しく認識させる」問題は、エッジ環境でも同種の罠がある。認証まわりで踏みやすい例はAuth.js (NextAuth v5) のtrustHost設定の記事も参考になる。
Step 5:Let’s EncryptでSSLを自動取得する
DNSのAレコードをVPSのIPに向け、伝播を確認してからCertbotを実行する。DNSが未反映のまま叩くと認証に失敗し、短時間のリトライ制限に引っかかるので順番は守りたい。
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
# 自動更新のドライラン
sudo certbot renew --dry-run
--nginxプラグインは設定ファイルへの443ブロック追記とHTTPリダイレクトまで自動で書き換えてくれる。更新はsystemd timerで回るため、cron登録は基本的に不要だ。
運用で効く3つの設定
- ログローテーション:
pm2 install pm2-logrotate。入れないと~/.pm2/logsが肥大してディスクを食う - メモリ上限:
pm2 start dist/index.js --max-memory-restart 300Mでリークによる巻き込み停止を防ぐ - ヘルスチェック:
/healthを用意し、外形監視サービスから叩く。プロセスが生きていてもイベントループが詰まる障害は検知できない
ゼロダウンタイム更新が必要ならpm2 reload myappを使う。ただし完全な無停止にはアプリ側でgraceful shutdown(SIGINT受信時に接続を閉じる処理)の実装が要る。
Linuxサーバー運用の土台を体系的に固めたいなら、

Linuxサーバー構築標準教科書 Ubuntu版 Ver.1.0.0: LinuC(リナック)学習にも役立つ (LPI-Japan標準教科書シリーズ)
参考価格¥300(税込)
パーミッション・systemd・ログ運用の基礎を通しで押さえられる
Amazonで最新価格をチェックのような一冊を手元に置いておくと、トラブル時の切り分けが速くなる。
注意点:VPSは「作って終わり」ではない
最後に正直な話をしておく。この構成は動き出せば安定するが、継続的な手入れが必要だ。
sudo apt update && sudo apt upgradeとカーネル更新に伴う再起動- Node.jsのLTSサポート終了に合わせたバージョン移行
- バックアップ(スナップショット機能の有無・料金は公式の最新情報を確認)
- 攻撃トラフィックへの対処(fail2ban導入やNginxのrate limit)
これらを見積もった上で、それでも常駐プロセスやネイティブ依存が必要ならVPSは良い選択になる。まずは検証環境として1台立て、/healthが443越しに200を返すところまでを1本通してみるのが理解の近道だ。