nginxリバースプロキシ設定ガイド
Webアプリを安全に公開する
nginxをリバースプロキシとして使い、Webアプリケーションを安全に公開する方法を解説します。SSL設定やロードバランシングまでカバーします。
こんな人向けの記事です
- Webアプリをインターネットに公開したい
- nginxの基本的な設定を学びたい
- SSL証明書の設定方法を知りたい
Step 1リバースプロキシとは
リバースプロキシとは、クライアント(ブラウザ)とバックエンドサーバー(Django, Rails等)の間に立ち、クライアントからのリクエストを代理で受け取ってバックエンドに転送する仕組みです。
| 項目 | フォワードプロキシ | リバースプロキシ |
|---|---|---|
| 配置場所 | クライアント側 | サーバー側 |
| 目的 | クライアントの匿名化・アクセス制御 | サーバーの保護・負荷分散 |
| 設定者 | クライアント管理者 | サーバー管理者 |
| 代表例 | Squid, 企業プロキシ | nginx, Apache, HAProxy |
リバースプロキシのメリット
- セキュリティ向上: バックエンドサーバーを直接公開しないため、攻撃対象を限定できる
- SSL終端: SSL/TLSの処理をnginxに任せ、バックエンドの負荷を軽減
- 負荷分散: 複数のバックエンドに処理を振り分けられる
- キャッシュ: 静的コンテンツをキャッシュして応答速度を向上
Step 2nginxのインストールと基本設定
インストール
sudo apt update
sudo apt install nginx -y
# バージョン確認
nginx -v
# サービスの起動・有効化
sudo systemctl start nginx
sudo systemctl enable nginx
sudo yum install epel-release -y
sudo yum install nginx -y
# サービスの起動・有効化
sudo systemctl start nginx
sudo systemctl enable nginx
ディレクトリ構成
| パス | 役割 |
|---|---|
/etc/nginx/nginx.conf | メイン設定ファイル |
/etc/nginx/conf.d/ | サイト別設定ファイル(推奨) |
/etc/nginx/sites-available/ | 利用可能なサイト設定(Debian系) |
/etc/nginx/sites-enabled/ | 有効なサイト設定(シンボリックリンク) |
/var/log/nginx/ | アクセスログ・エラーログ |
基本的なnginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# ログフォーマット
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
gzip on;
# サイト別設定を読み込み
include /etc/nginx/conf.d/*.conf;
}
設定変更後のテスト
設定を変更したら、必ず sudo nginx -t で構文チェックを行ってからリロードしましょう。構文エラーがあるままリロードすると、nginxが停止する可能性があります。
# 構文チェック
sudo nginx -t
# 設定のリロード(ダウンタイムなし)
sudo systemctl reload nginx
Step 3リバースプロキシの設定方法
ここでは、Djangoアプリ(Gunicorn)をバックエンドとして、nginxでリバースプロキシを設定する例を紹介します。
基本的なリバースプロキシ設定
server {
listen 80;
server_name example.com www.example.com;
# 静的ファイル(Djangoのcollectstatic)
location /static/ {
alias /var/www/myapp/static/;
expires 30d;
add_header Cache-Control "public, immutable";
}
# メディアファイル
location /media/ {
alias /var/www/myapp/media/;
expires 7d;
}
# それ以外はGunicornに転送
location / {
proxy_pass http://127.0.0.1:8000;
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_send_timeout 60s;
proxy_read_timeout 60s;
}
}
重要なプロキシヘッダー
| ヘッダー | 役割 |
|---|---|
Host | 元のリクエストのホスト名をバックエンドに伝える |
X-Real-IP | クライアントの実IPアドレスを伝える |
X-Forwarded-For | プロキシチェーン全体のIPリストを伝える |
X-Forwarded-Proto | 元のプロトコル(http/https)を伝える |
Django側の設定
Djangoで X-Forwarded-Proto を認識させるには、settings.py に以下を追加します。
# リバースプロキシ配下でHTTPSを正しく認識
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
# 許可するホスト
ALLOWED_HOSTS = ["example.com", "www.example.com"]
# 信頼するプロキシ(Django 4.0+)
CSRF_TRUSTED_ORIGINS = ["https://example.com"]
UNIXソケット接続(推奨)
TCP接続の代わりにUNIXソケットを使うと、オーバーヘッドが減りパフォーマンスが向上します。
upstream django_app {
server unix:/run/gunicorn/myapp.sock;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://django_app;
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;
}
}
Step 4SSL/TLS終端(Let's Encrypt)
Let's Encryptを使って無料のSSL証明書を取得し、nginxでHTTPS通信を実現します。
Certbotのインストール
# Certbotのインストール
sudo apt install certbot python3-certbot-nginx -y
# 証明書の取得(nginxプラグイン使用)
sudo certbot --nginx -d example.com -d www.example.com
# 自動更新のテスト
sudo certbot renew --dry-run
自動更新について
Certbotはインストール時に自動更新用のタイマー(systemdまたはcron)を設定します。証明書は90日間有効で、期限の30日前に自動更新されます。
SSL対応の完全な設定
# HTTPからHTTPSへリダイレクト
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
# HTTPS設定
server {
listen 443 ssl;
http2 on;
server_name example.com www.example.com;
# SSL証明書(Let's Encryptが自動配置)
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# SSL設定の最適化
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# OCSPステープリング
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
# セッションキャッシュ
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# 静的ファイル
location /static/ {
alias /var/www/myapp/static/;
expires 30d;
}
# リバースプロキシ
location / {
proxy_pass http://127.0.0.1:8000;
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;
}
}
TLS 1.0/1.1は無効にする
TLS 1.0と1.1には既知の脆弱性があります。ssl_protocols には TLSv1.2 TLSv1.3 のみを指定してください。古いブラウザとの互換性が必要な場合でも、TLS 1.2以上を推奨します。
Step 5ロードバランシング
複数のバックエンドサーバーにリクエストを分散することで、可用性とパフォーマンスを向上させます。
基本的なロードバランシング
# バックエンドサーバーグループの定義
upstream django_backends {
server 127.0.0.1:8001;
server 127.0.0.1:8002;
server 127.0.0.1:8003;
}
server {
listen 443 ssl;
http2 on;
server_name example.com;
# SSL設定(省略)
location / {
proxy_pass http://django_backends;
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;
}
}
負荷分散アルゴリズム
| アルゴリズム | 設定 | 動作 |
|---|---|---|
| ラウンドロビン | (デフォルト) | 順番にリクエストを振り分ける |
| 重み付け | weight=N | サーバーの性能に応じて振り分け比率を設定 |
| 最少接続 | least_conn | 接続数が最も少ないサーバーに振り分ける |
| IPハッシュ | ip_hash | 同じクライアントIPを同じサーバーに固定 |
upstream django_backends {
least_conn;
# 高性能サーバーに多くのリクエストを割り当て
server 192.168.1.10:8000 weight=3;
server 192.168.1.11:8000 weight=2;
server 192.168.1.12:8000 weight=1;
# 障害時のバックアップサーバー
server 192.168.1.20:8000 backup;
# ヘルスチェック: 3回失敗で30秒間除外
server 192.168.1.10:8000 max_fails=3 fail_timeout=30s;
}
セッションの永続化
Djangoでセッションを使っている場合、ip_hash を指定するか、セッションストレージをRedis等の共有ストレージに変更することで、どのサーバーに接続しても同じセッションを参照できます。
Step 6セキュリティヘッダーの設定
HTTPレスポンスヘッダーを追加して、ブラウザ側のセキュリティ機能を有効にします。
# XSS対策
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
# HTTPS強制(HSTS)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# リファラーポリシー
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# コンテンツセキュリティポリシー(CSP)
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self';" always;
# Permissions Policy
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
各ヘッダーの役割
| ヘッダー | 役割 |
|---|---|
X-Content-Type-Options | MIMEタイプのスニッフィングを防止 |
X-Frame-Options | iframe埋め込み(クリックジャッキング)を防止 |
Strict-Transport-Security | ブラウザにHTTPS接続を強制 |
Referrer-Policy | リファラー情報の送信範囲を制御 |
Content-Security-Policy | 読み込み可能なリソースの提供元を制限 |
Permissions-Policy | ブラウザAPI(カメラ・位置情報等)の使用を制限 |
CSPは段階的に導入する
Content-Security-Policyを厳しく設定しすぎると、サイトが正常に動作しなくなる場合があります。まず Content-Security-Policy-Report-Only ヘッダーで違反を検出し、問題がないことを確認してから本番適用しましょう。
追加のセキュリティ設定
# サーバーバージョンの非表示
server_tokens off;
# リクエストサイズの制限(ファイルアップロード対応)
client_max_body_size 10m;
# レート制限(DDoS対策)
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://127.0.0.1:8000;
}
# 不要なHTTPメソッドを拒否
if ($request_method !~ ^(GET|HEAD|POST|PUT|PATCH|DELETE)$) {
return 405;
}
設定完了チェックリスト
- nginxのインストールと起動を確認した
- リバースプロキシの基本設定を行った
- プロキシヘッダー(Host, X-Real-IP等)を設定した
- SSL証明書を取得し、HTTPS通信を有効にした
- HTTPからHTTPSへのリダイレクトを設定した
- TLS 1.2/1.3のみを許可した
- セキュリティヘッダーを追加した
- server_tokensをoffにした
sudo nginx -tで設定の構文チェックを行った- ロードバランシングの必要性を検討した