なぜUbuntuサーバーの多くが最終的にNginxを必要とするのか
初めてUbuntu 22.04の4GB RAMのVPSにNode.jsアプリをセットアップしたとき、ポート3000で直接起動してインターネットに公開した。それは動いた——しばらくの間は。HTTPSが必要になった瞬間、同じサーバーで2つ目のアプリをホストしたいとき、あるいは静的ファイルを効率よく配信する必要が出てきたとき、Nodeだけで運用することは一気に負債になった。
根本的な原因はここにある。Node、Python(Gunicorn)、PHP-FPMといったアプリケーションランタイムはコードの実行には優れている。しかし、HTTPのエッジケース——SSLターミネーション、負荷時のコネクションキューイング、gzip圧縮、キャッシュ、複数バックエンドへのトラフィックルーティング——を処理するようには設計されていない。その隙間を埋めるのがNginxだ。
本番サーバーでこの構成に切り替えたところ、負荷時の平均レスポンスタイムが約90msから約22msに低下した。Nodeプロセスは静的アセットのリクエスト処理でCPUを消費しなくなり、アプリケーションロジックだけに集中できるようになった。
インストール
Ubuntu 22.04またはDebianベースのシステムでは、NginxはデフォルトのAPTリポジトリに含まれている:
sudo apt update
sudo apt install nginx -y
インストール後、再起動しても自動起動するように設定する:
sudo systemctl start nginx
sudo systemctl enable nginx
UFWが有効な場合はHTTPとHTTPSを許可する:
sudo ufw allow 'Nginx Full'
ブラウザでサーバーのIPを開くと、Nginxのウェルカムページが表示されるはずだ。これでWebサーバーが動作していることが確認できる。
設定ファイルのレイアウトを理解する
設定を書き始める前に、Nginxがファイルをどこに置いているかを把握しておこう:
- /etc/nginx/nginx.conf — メイン設定ファイル。
conf.d/とsites-enabled/内のすべてをインクルード - /etc/nginx/sites-available/ — サイト設定の保存場所(シンボリックリンクを張るまでは無効)
- /etc/nginx/sites-enabled/ — 有効な設定へのシンボリックリンク
- /var/www/html/ — デフォルトのWebルート
- /var/log/nginx/ — アクセスログとエラーログ
基本の流れは: sites-availableに設定を書き、sites-enabledにシンボリックリンクを張って有効化する。こうすることで、設定を削除せずにサイトを無効化できる。
設定: 静的WebサーバーとしてのNginx
最もシンプルなケースから始めよう——あるドメインに対して静的HTMLファイルを配信する。
Webルートとテストページを作成する:
sudo mkdir -p /var/www/mysite.com/html
echo '<h1>Hello from Nginx</h1>' | sudo tee /var/www/mysite.com/html/index.html
サーバーブロックの設定を書く:
sudo nano /etc/nginx/sites-available/mysite.com
以下の設定を貼り付ける:
server {
listen 80;
server_name mysite.com www.mysite.com;
root /var/www/mysite.com/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
access_log /var/log/nginx/mysite.access.log;
error_log /var/log/nginx/mysite.error.log;
}
有効化してテストする:
sudo ln -s /etc/nginx/sites-available/mysite.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
nginx -tのステップは重要だ——リロードする前に必ず実行すること。サーバーをダウンさせる前に構文エラーを検出してくれる。
設定: リバースプロキシとしてのNginx
リバースプロキシの設定こそ、多くの人がNginxを使う本来の目的だ。ローカルのポート3000でNode.jsアプリが動いているとしよう。http://myapp.comへのすべてのトラフィックをそこにルーティングしたい。
新しい設定ファイルを作成する:
sudo nano /etc/nginx/sites-available/myapp.com
server {
listen 80;
server_name myapp.com www.myapp.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 60s;
proxy_read_timeout 60s;
}
access_log /var/log/nginx/myapp.access.log;
error_log /var/log/nginx/myapp.error.log;
}
proxy_set_headerの行を省略してはいけない。これがないと、バックエンドはすべてのリクエストが実際のクライアントIPではなく127.0.0.1から来ているように見えてしまい、レート制限・ログ・ジオロケーションが一度に壊れる。
有効化する:
sudo ln -s /etc/nginx/sites-available/myapp.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
複数バックエンドへのプロキシ
同じサーバーで2つのサービスを動かしている場合——たとえばAPIがポート3000、フロントエンドがポート5000——パスによって1つのドメイン配下でルーティングできる:
server {
listen 80;
server_name platform.com;
location /api/ {
proxy_pass http://127.0.0.1:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Nginxはlocationを上から順に、最長プレフィックス優先でマッチする。/api/usersへのリクエストはポート3000へ、それ以外はすべてポート5000へ転送される。
Let’s EncryptでSSLを追加する
平文のHTTPは本番環境に居場所はない。ブラウザは「保護されていない通信」と警告を表示し、GoogleはHTTPS非対応サイトの検索順位を実際に下げる。解決策は無料だ——Certbotをインストールして証明書を取得する:
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d myapp.com -d www.myapp.com
CertbotはNginxの設定を自動的に変更し、listen 443 sslブロックを追加してHTTPをHTTPSにリダイレクトする。証明書の更新はsystemdタイマーが担当する——以下で確認できる:
sudo certbot renew --dry-run
動作確認とモニタリング
設定変更後に毎回実施するチェック項目を紹介する。
構文チェックとリロード
sudo nginx -t
sudo systemctl reload nginx
必要な場合を除き、本番環境ではrestartを使わないこと——reloadは既存の接続を正常に終了させながら新しい設定をゼロダウンタイムで適用する。
サービスの状態確認
sudo systemctl status nginx
active (running)と表示されていることを確認する。failedと表示された場合、エラーメッセージに原因となった設定行が正確に示されていることが多い。
リアルタイムリクエスト監視
特定サイトへのトラフィックをリアルタイムで確認する:
sudo tail -f /var/log/nginx/myapp.access.log
エラーのみをフィルタリングする場合:
sudo tail -f /var/log/nginx/myapp.error.log | grep -v 'favicon'
プロキシヘッダーのテスト
curlを使ってリバースプロキシが正しく転送し、適切なステータスコードを返しているか確認する:
curl -I http://myapp.com
curl -v http://myapp.com/api/health
アプリのログでX-Forwarded-Forヘッダーを確認し、実際のクライアントIPが届いていることを検証しよう。
Nginxステータスページ(オプション)
基本的な接続メトリクスを確認するには、stub statusモジュールを有効にする:
location /nginx_status {
stub_status;
allow 127.0.0.1;
deny all;
}
ローカルからクエリを実行する:
curl http://127.0.0.1/nginx_status
アクティブな接続数、1秒あたりのリクエスト数、読み取り/書き込み/待機中のカウントが出力される——フルのモニタリングスタックを導入せずに素早いキャパシティ確認に役立つ。
よくある落とし穴
- 502 Bad Gateway — バックエンドが起動していないか、
proxy_passで指定したポートと異なるポートで動いている。ss -tlnp | grep 3000で確認しよう。 - 413 Request Entity Too Large — ユーザーがファイルをアップロードする場合は、サーバーブロックに
client_max_body_size 50M;を追加する。 - SSLでリダイレクトループが発生する — バックエンドが
X-Forwarded-ProtoでHTTPSを検出してリダイレクトする場合、このヘッダーを必ず設定すること。設定がないと無限リダイレクトループに陥る。 - デフォルトサイトの無効化を忘れる — デフォルト設定はマッチしないすべてのトラフィックをキャッチする。
sudo rm /etc/nginx/sites-enabled/defaultで削除しておこう。

