Skip to content
Published on

Nginx設定完全ガイド:アーキテクチャからプロダクション最適化まで15の重要テーマ

Authors
  • Name
    Twitter

1. Nginxアーキテクチャと設定構造

1.1 イベント駆動アーキテクチャ:Master-Workerモデル

Nginxは、Apache httpdのプロセス/スレッドベースモデルとは根本的に異なるイベント駆動(Event-Driven)アーキテクチャを採用した。この設計哲学こそが、Nginxが単一サーバーで数十万の同時接続を処理できる核心的な理由である。

┌─────────────────────────────────────────────────────────┐
Master Process- 設定ファイルの読み込みと検証                          │
- Workerプロセスの生成/管理 (fork)- ポートバインディング (80, 443)- シグナル処理 (reload, stop, reopen)└─────────┬───────────┬───────────┬───────────┬───────────┘
          │           │           │           │
    ┌─────▼─────┐ ┌───▼───┐ ┌───▼───┐ ┌───▼───┐
Worker 0  │ │Worker 1│ │Worker 2│ │Worker 3Event Loop │ │  ...   │ │  ...   │ │  ...    │ epoll/kq   │ │        │ │        │ │        │
    │ 数千 conn  │ │        │ │        │ │        │
    └───────────┘ └────────┘ └────────┘ └────────┘

Master Processはroot権限で実行され、設定ファイルのパース、ポートバインディング、Workerプロセスの管理を担当する。fork()システムコールでWorkerを生成し、設定リロード時には既存の接続を切断せずに新しいWorkerを起動した後、既存のWorkerをgraceful shutdownする。

Worker Processは実際のクライアントリクエストを処理する核心的なユニットである。各Workerは独立したイベントループを運営し、OSのI/O多重化メカニズム(Linuxではepoll、FreeBSD/macOSではkqueue)を活用してブロッキングなしに数千の接続を同時に処理する。コネクションごとにスレッドを割り当てる方式と比較して、コンテキストスイッチとメモリオーバーヘッドが劇的に削減される。

1.2 nginx.conf 設定コンテキスト構造

Nginxの設定は階層的コンテキスト(Context)構造に従う。子コンテキストは親の設定を継承し、同じディレクティブを子で再宣言するとオーバーライドされる。

# ============================================
# Main Context(グローバル設定)
# ============================================
user nginx;                          # Workerプロセス実行ユーザー
worker_processes auto;               # CPUコア数に合わせてWorkerを生成
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;

# ============================================
# Events Context(接続処理設定)
# ============================================
events {
    worker_connections 1024;         # Worker当たりの最大同時接続数
    multi_accept on;                 # 一度に複数の接続を受け入れる
    use epoll;                       # Linuxでepollを使用(デフォルト)
}

# ============================================
# HTTP Context(HTTPプロトコル設定)
# ============================================
http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # ========================================
    # Server Context(仮想ホスト)
    # ========================================
    server {
        listen 80;
        server_name example.com;

        # ====================================
        # Location Context(URLパスマッチング)
        # ====================================
        location / {
            root /var/www/html;
            index index.html;
        }

        location /api/ {
            proxy_pass http://backend;
        }
    }
}

# ============================================
# Stream Context(TCP/UDPプロキシ、L4)
# ============================================
stream {
    server {
        listen 3306;
        proxy_pass mysql_backend;
    }
}

コンテキスト階層のまとめ:

コンテキスト位置役割
Main最上位グローバル設定 (user, worker, pid, error_log)
EventsMain内部接続処理メカニズム (worker_connections)
HTTPMain内部HTTPプロトコル関連の全体設定
ServerHTTP内部仮想ホスト(ドメイン別設定)
LocationServer内部URLパス別リクエスト処理ルール
UpstreamHTTP内部バックエンドサーバーグループ(ロードバランシング)
StreamMain内部TCP/UDP L4プロキシ

1.3 設定ファイル構造化のベストプラクティス

プロダクション環境では、単一のnginx.confにすべての設定を入れるのではなく、モジュール化して管理する。

/etc/nginx/
├── nginx.conf                    # メイン設定(includeで分離)
├── conf.d/                       # 共通設定スニペット
│   ├── ssl-params.conf           # SSL/TLS共通パラメータ
│   ├── proxy-params.conf         # リバースプロキシ共通ヘッダー
│   ├── security-headers.conf     # セキュリティヘッダー
│   └── gzip.conf                 # 圧縮設定
├── sites-available/              # サイト別設定ファイル
│   ├── example.com.conf
│   ├── api.example.com.conf
│   └── admin.example.com.conf
├── sites-enabled/                # 有効化されたサイト(シンボリックリンク)
│   ├── example.com.conf -> ../sites-available/example.com.conf
│   └── api.example.com.conf -> ../sites-available/api.example.com.conf
└── snippets/                     # 再利用可能な設定フラグメント
    ├── letsencrypt.conf
    └── fastcgi-php.conf
# nginx.conf メインファイル
http {
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*.conf;
}

2. Virtual Host / Server Block設定

NginxのServer BlockはApacheのVirtual Hostに相当し、一つのサーバーで複数のドメインをホスティングできるようにする。

2.1 基本Server Block設定

# /etc/nginx/sites-available/example.com.conf

# ── プライマリドメイン ──
server {
    listen 80;
    listen [::]:80;                          # IPv6対応
    server_name example.com www.example.com;

    root /var/www/example.com/html;
    index index.html index.htm;

    # ドメイン別にアクセスログを分離
    access_log /var/log/nginx/example.com.access.log;
    error_log /var/log/nginx/example.com.error.log;

    location / {
        try_files $uri $uri/ =404;
    }
}

# ── セカンドドメイン ──
server {
    listen 80;
    listen [::]:80;
    server_name blog.example.com;

    root /var/www/blog.example.com/html;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

2.2 Default Server(catch-all)

定義されていないドメインへのリクエストを処理するデフォルトサーバーブロックである。セキュリティのため、444(接続切断)で応答することが推奨される。

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;                           # すべての未マッチドメイン

    # 定義されていないホストリクエストは即座に接続を終了
    return 444;
}

2.3 Server Nameマッチング優先順位

Nginxはserver_nameマッチング時に以下の優先順位に従う:

  1. 正確な名前: server_name example.com
  2. 前方ワイルドカード: server_name *.example.com
  3. 後方ワイルドカード: server_name example.*
  4. 正規表現: server_name ~^(?<subdomain>.+)\.example\.com$
  5. default_server: 上記すべてが未マッチの場合
# 正規表現でサブドメインをキャプチャ
server {
    listen 80;
    server_name ~^(?<subdomain>.+)\.example\.com$;

    location / {
        root /var/www/$subdomain;
    }
}

2.4 サイトの有効化/無効化

# サイト有効化
sudo ln -s /etc/nginx/sites-available/example.com.conf \
           /etc/nginx/sites-enabled/example.com.conf

# 設定検証
sudo nginx -t

# リロード(無停止)
sudo systemctl reload nginx

3. リバースプロキシ設定

3.1 基本リバースプロキシ

server {
    listen 80;
    server_name app.example.com;

    location / {
        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;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host  $host;
        proxy_set_header X-Forwarded-Port  $server_port;
    }
}

各ヘッダーの役割:

ヘッダー目的
Host元のリクエストのHostヘッダーを転送
X-Real-IP実際のクライアントIP(プロキシ経由で元のIPを識別)
X-Forwarded-Forプロキシチェーンを経由して蓄積されるクライアントIPリスト
X-Forwarded-Proto元のプロトコル(http/https)―バックエンドでリダイレクト判断
X-Forwarded-Host元のHostヘッダー
X-Forwarded-Port元のポート

3.2 再利用可能なプロキシパラメータスニペット

# /etc/nginx/conf.d/proxy-params.conf
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_set_header X-Forwarded-Host  $host;
proxy_set_header X-Forwarded-Port  $server_port;

proxy_http_version 1.1;
proxy_connect_timeout 60s;
proxy_send_timeout    60s;
proxy_read_timeout    60s;
proxy_buffering on;
# Server Blockでincludeして再利用
location / {
    proxy_pass http://backend;
    include /etc/nginx/conf.d/proxy-params.conf;
}

3.3 WebSocketプロキシ

WebSocketはHTTP Upgradeメカニズムを使用するため、UpgradeConnectionのホップバイホップヘッダーを明示的に転送する必要がある。Nginxはデフォルトでこれらのヘッダーをフォワーディングしない。

# ── mapでConnectionヘッダーを動的設定 ──
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    server_name ws.example.com;

    location /ws/ {
        proxy_pass http://127.0.0.1:8080;

        # WebSocket必須設定
        proxy_http_version 1.1;                          # HTTP/1.1必須(Upgrade対応)
        proxy_set_header Upgrade    $http_upgrade;       # クライアントのUpgradeヘッダーを転送
        proxy_set_header Connection $connection_upgrade; # 動的Connectionヘッダー

        # 一般プロキシヘッダー
        proxy_set_header Host            $host;
        proxy_set_header X-Real-IP       $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # WebSocketは長時間接続のためタイムアウトを延長
        proxy_read_timeout  86400s;    # 24時間(デフォルト60秒ではidle接続が切断される)
        proxy_send_timeout  86400s;
    }
}

3.4 パスベースルーティング(マイクロサービス)

server {
    listen 80;
    server_name api.example.com;

    # ユーザーサービス
    location /api/users/ {
        proxy_pass http://user-service:3001/;     # 末尾の/に注意:/api/users/を除去して転送
        include /etc/nginx/conf.d/proxy-params.conf;
    }

    # 注文サービス
    location /api/orders/ {
        proxy_pass http://order-service:3002/;
        include /etc/nginx/conf.d/proxy-params.conf;
    }

    # 決済サービス
    location /api/payments/ {
        proxy_pass http://payment-service:3003/;
        include /etc/nginx/conf.d/proxy-params.conf;
        proxy_read_timeout 120s;                  # 決済はタイムアウトを延長
    }
}

注意: proxy_passのURL末尾に/があると、locationにマッチした部分が除去される。/api/users/123リクエストがhttp://user-service:3001/123として転送される。/がない場合は全URIがそのまま転送される。


4. ロードバランシング

4.1 Upstreamブロックとアルゴリズム

# ============================================
# 1. Round Robin(デフォルト)
# リクエストを順次分配
# ============================================
upstream backend_roundrobin {
    server 10.0.0.1:8080;           # デフォルトweight 1
    server 10.0.0.2:8080;
    server 10.0.0.3:8080;
}

# ============================================
# 2. Weighted Round Robin(重み付きラウンドロビン)
# サーバー性能に応じた比率分配
# ============================================
upstream backend_weighted {
    server 10.0.0.1:8080 weight=5;  # リクエストの5/8
    server 10.0.0.2:8080 weight=2;  # リクエストの2/8
    server 10.0.0.3:8080 weight=1;  # リクエストの1/8
}

# ============================================
# 3. Least Connections(最小接続数)
# アクティブ接続数が最も少ないサーバーへ転送
# ============================================
upstream backend_leastconn {
    least_conn;
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
    server 10.0.0.3:8080;
}

# ============================================
# 4. IP Hash(セッション固定)
# 同一クライアントIP → 同一サーバー
# ============================================
upstream backend_iphash {
    ip_hash;
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
    server 10.0.0.3:8080;
}

# ============================================
# 5. Generic Hash(カスタムハッシュ)
# 任意の変数に基づくハッシュ
# ============================================
upstream backend_hash {
    hash $request_uri consistent;   # URIベース + 一貫性ハッシュ
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
    server 10.0.0.3:8080;
}

アルゴリズム選択ガイド:

アルゴリズム適した場面注意点
Round Robinステートレスサービス、同一スペックサーバーサーバースペック差がある場合は不均衡
Weightedサーバースペックが異なる場合ウェイトの手動管理が必要
Least Connectionsリクエスト処理時間のばらつきが大きい場合短いリクエスト中心ならRound Robinと類似
IP Hashセッション固定が必要な場合(レガシーアプリ)サーバー追加/削除時に再分配が発生
Generic Hashキャッシュ最適化(同一URI → 同一サーバー)consistentハッシュを推奨

4.2 サーバーステータスとバックアップ

upstream backend {
    least_conn;

    server 10.0.0.1:8080;                         # 正常サーバー
    server 10.0.0.2:8080;                         # 正常サーバー
    server 10.0.0.3:8080 backup;                  # バックアップ:上記サーバーがすべてダウンした時に有効化
    server 10.0.0.4:8080 down;                    # 無効化(メンテナンス)

    server 10.0.0.5:8080 max_fails=3 fail_timeout=30s;
    # max_fails=3: 30秒以内に3回失敗すると異常判定
    # fail_timeout=30s: 異常判定後30秒間リクエスト除外
}

4.3 Keepaliveコネクションプール

バックエンドサーバーとのTCP接続を再利用して接続の確立/解除オーバーヘッドを削減する。

upstream backend {
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;

    keepalive 32;                # 各Workerが維持するアイドルコネクション数
    keepalive_requests 1000;     # 1つのコネクションで処理する最大リクエスト数
    keepalive_timeout 60s;       # アイドルコネクション維持時間
}

server {
    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;                    # keepaliveにはHTTP/1.1が必須
        proxy_set_header Connection "";             # "close"の代わりに空値でkeepaliveを有効化
    }
}

5. SSL/TLS設定

5.1 基本HTTPS設定

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com www.example.com;

    # ── 証明書 ──
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # ── プロトコル ──
    ssl_protocols TLSv1.2 TLSv1.3;              # TLS 1.0、1.1を無効化

    # ── Cipher Suites(TLS 1.2用) ──
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
    ssl_prefer_server_ciphers off;               # TLS 1.3ではoff推奨

    # ── 楕円曲線 ──
    ssl_ecdh_curve X25519:secp384r1:secp256r1;

    # ── セッション再利用 ──
    ssl_session_cache shared:SSL:10m;            # 10MB = 約40,000セッション
    ssl_session_timeout 1d;                      # セッション有効期間
    ssl_session_tickets off;                     # Forward Secrecyを保証

    root /var/www/example.com/html;
    index index.html;
}

5.2 HTTP → HTTPSリダイレクト

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

    # すべてのHTTPリクエストをHTTPSに301リダイレクト
    return 301 https://$host$request_uri;
}

5.3 HSTS (HTTP Strict Transport Security)

# HTTPS server block内に追加
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
  • max-age=63072000: 2年間HTTPSのみ使用(最低1年推奨)
  • includeSubDomains: すべてのサブドメインにも適用
  • preload: ブラウザのHSTS Preloadリスト登録資格
  • always: エラーレスポンス(4xx、5xx)でもヘッダーを送信

5.4 OCSP Stapling

OCSP Staplingはサーバーが証明書の有効性検証を代行し、クライアントのCA直接照会を不要にする。初期接続速度が改善され、プライバシーが保護される。

ssl_stapling on;
ssl_stapling_verify on;

# OCSPレスポンス検証用の信頼チェーン(Let's Encrypt中間証明書を含む)
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

# OCSPレスポンダー照会用DNSリゾルバ
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;

5.5 プロダクションSSL統合スニペット

# /etc/nginx/conf.d/ssl-params.conf
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:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
ssl_ecdh_curve X25519:secp384r1:secp256r1;

ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;

ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;

add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# Server Blockでinclude
server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    include /etc/nginx/conf.d/ssl-params.conf;

    # ... 残りの設定
}

5.6 Let's Encrypt自動更新(Certbot)

# 証明書発行
sudo certbot --nginx -d example.com -d www.example.com

# 自動更新テスト
sudo certbot renew --dry-run

# Cron自動更新(certbotが既に設定するが、明示的に記載)
echo "0 0,12 * * * root certbot renew --quiet --deploy-hook 'systemctl reload nginx'" \
  | sudo tee /etc/cron.d/certbot-renew

6. キャッシング設定

6.1 Proxy Cache(リバースプロキシキャッシュ)

# ── HTTP Contextでキャッシュパスを定義 ──
proxy_cache_path /var/cache/nginx/proxy
    levels=1:2                       # 2段階ディレクトリ構造(ファイル分散)
    keys_zone=proxy_cache:10m        # キャッシュキー格納用共有メモリ(1MB ≈ 8,000キー)
    max_size=1g                      # ディスクキャッシュ最大サイズ
    inactive=60m                     # 60分間未使用のキャッシュを削除
    use_temp_path=off;               # 一時ファイルパスを使用しない(直接書き込み → 性能向上)

server {
    listen 80;
    server_name app.example.com;

    location / {
        proxy_pass http://backend;
        proxy_cache proxy_cache;                   # キャッシュゾーンを指定
        proxy_cache_valid 200 302 10m;             # 200、302レスポンスを10分間キャッシュ
        proxy_cache_valid 404     1m;              # 404レスポンスを1分間キャッシュ
        proxy_cache_use_stale error timeout
                              updating
                              http_500 http_502
                              http_503 http_504;   # バックエンドエラー時にstaleキャッシュを提供
        proxy_cache_lock on;                       # 同時リクエスト時に1つだけバックエンドへ
        proxy_cache_min_uses 2;                    # 2回以上リクエストされたURLのみキャッシュ

        # デバッグ用キャッシュステータスヘッダー
        add_header X-Cache-Status $upstream_cache_status;
    }

    # キャッシュバイパス(管理者用)
    location /api/ {
        proxy_pass http://backend;
        proxy_cache proxy_cache;

        # Cookieにnocacheがある場合、またはヘッダーにCache-Control: no-cacheがある場合にバイパス
        proxy_cache_bypass $http_cache_control;
        proxy_no_cache $cookie_nocache;
    }
}

X-Cache-Statusの値:

ステータス意味
HITキャッシュから直接提供
MISSキャッシュなし → バックエンドにリクエスト
EXPIRED期限切れキャッシュ → バックエンドに再リクエスト
STALE期限切れだがstaleポリシーで提供
UPDATING更新中にstaleキャッシュを提供
BYPASSキャッシュバイパスされた

6.2 FastCGI Cache(PHPなど)

fastcgi_cache_path /var/cache/nginx/fastcgi
    levels=1:2
    keys_zone=fastcgi_cache:10m
    max_size=512m
    inactive=30m;

server {
    listen 80;
    server_name wordpress.example.com;

    # キャッシュバイパス条件の定義
    set $skip_cache 0;

    # POSTリクエストはキャッシュしない
    if ($request_method = POST) {
        set $skip_cache 1;
    }

    # クエリストリングがある場合はキャッシュしない
    if ($query_string != "") {
        set $skip_cache 1;
    }

    # WordPress管理者ページのキャッシュ除外
    if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php") {
        set $skip_cache 1;
    }

    # ログインユーザーのキャッシュ除外
    if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
        set $skip_cache 1;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;

        fastcgi_cache fastcgi_cache;
        fastcgi_cache_valid 200 30m;
        fastcgi_cache_bypass $skip_cache;
        fastcgi_no_cache $skip_cache;
        fastcgi_cache_use_stale error timeout updating http_500;

        add_header X-FastCGI-Cache $upstream_cache_status;
    }
}

6.3 ブラウザキャッシング(静的リソース)

# ── 静的ファイルの長期ブラウザキャッシュ ──
location ~* \.(jpg|jpeg|png|gif|ico|webp|avif|svg)$ {
    expires 30d;                                   # Expiresヘッダーを設定
    add_header Cache-Control "public, immutable";  # ブラウザが再検証なしにキャッシュを使用
    access_log off;                                # 静的ファイルのログを無効化(I/O削減)
}

location ~* \.(css|js)$ {
    expires 7d;
    add_header Cache-Control "public";
}

location ~* \.(woff|woff2|ttf|eot)$ {
    expires 365d;
    add_header Cache-Control "public, immutable";
    add_header Access-Control-Allow-Origin "*";    # フォントCORS
}

# ── HTMLは短いキャッシュまたはno-cache ──
location ~* \.html$ {
    expires -1;                                    # no-cache
    add_header Cache-Control "no-store, no-cache, must-revalidate";
}

7. Rate LimitingとConnection Limiting

7.1 Rate Limiting(リクエストレート制限)

NginxのRate LimitingはLeaky Bucketアルゴリズムを使用する。リクエストはバケット(zone)に入り、設定されたレート(rate)で処理される。

# ── HTTP ContextでZoneを定義 ──
# キー:クライアントIP
# 共有メモリ:10MB(約160,000 IPアドレス格納)
# レート:秒あたり10リクエスト
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;

# ログインエンドポイント:秒あたり1(ブルートフォース防止)
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;

# APIエンドポイント:秒あたり50
limit_req_zone $binary_remote_addr zone=api:10m rate=50r/s;

# Rate Limit超過時のログレベル
limit_req_status 429;                  # デフォルト503の代わりに429 Too Many Requests
limit_req_log_level warn;

server {
    listen 80;
    server_name example.com;

    # ── 一般ページ ──
    location / {
        limit_req zone=general burst=20 nodelay;
        # burst=20: 瞬間的に20個まで超過を許容
        # nodelay: burst範囲内のリクエストを遅延なしに即座に処理
        proxy_pass http://backend;
    }

    # ── ログインページ ──
    location /login {
        limit_req zone=login burst=5 nodelay;
        proxy_pass http://backend;
    }

    # ── API ──
    location /api/ {
        limit_req zone=api burst=100 nodelay;
        proxy_pass http://backend;
    }
}

burstとnodelayの動作原理:

Rate: 10r/s, Burst: 20

時刻030リクエストが到着:
├── 10: 即座に処理(rate範囲内)
├── 20: burstキューに格納
│         nodelayなし: 100ms間隔で処理(2秒間)
│         nodelayあり: 即座に処理(キュースロットのみ占有)
└── 残り: 429で拒否

7.2 Connection Limiting(同時接続制限)

# クライアントIPあたりの同時接続数制限
limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m;

# サーバー全体の同時接続数制限
limit_conn_zone $server_name zone=conn_per_server:10m;

limit_conn_status 429;
limit_conn_log_level warn;

server {
    listen 80;
    server_name example.com;

    # IPあたり最大100同時接続
    limit_conn conn_per_ip 100;

    # サーバーあたり最大10,000同時接続
    limit_conn conn_per_server 10000;

    # ダウンロード帯域幅制限(オプション)
    location /downloads/ {
        limit_conn conn_per_ip 5;        # ダウンロードはIPあたり5に制限
        limit_rate 500k;                 # 接続あたり500KB/s速度制限
        limit_rate_after 10m;            # 最初の10MBは制限なし、以降制限
    }
}

7.3 IPホワイトリストとRate Limitingの組み合わせ

# 内部ネットワークはRate Limitingを免除
geo $limit {
    default        1;
    10.0.0.0/8     0;    # 内部ネットワーク
    192.168.0.0/16 0;    # 内部ネットワーク
    172.16.0.0/12  0;    # 内部ネットワーク
}

map $limit $limit_key {
    0 "";                  # 空キー → Rate Limiting適用されない
    1 $binary_remote_addr; # 外部IP → Rate Limiting適用
}

limit_req_zone $limit_key zone=api:10m rate=10r/s;

8. Gzip/Brotli圧縮

8.1 Gzip圧縮設定

# /etc/nginx/conf.d/gzip.conf

gzip on;
gzip_vary on;                         # Vary: Accept-Encodingヘッダーを追加
gzip_proxied any;                     # プロキシレスポンスも圧縮
gzip_comp_level 6;                    # 圧縮レベル(1-9、6がパフォーマンス/圧縮のバランスポイント)
gzip_min_length 1000;                 # 1KB未満のファイルは圧縮効果なし → 除外
gzip_buffers 16 8k;                   # 圧縮バッファ

gzip_types
    text/plain
    text/css
    text/javascript
    text/xml
    application/javascript
    application/json
    application/xml
    application/rss+xml
    application/atom+xml
    application/vnd.ms-fontobject
    font/opentype
    font/ttf
    image/svg+xml
    image/x-icon;

# 既に圧縮されたファイルを除外(画像、動画などはgzip_typesに未含)
gzip_disable "msie6";                 # IE6を無効化(レガシー)

8.2 Brotli圧縮設定

BrotliはGzip比で15-25%高い圧縮率を提供する。ほとんどのモダンブラウザが対応しており、Nginxではngx_brotliモジュールが必要である。

# Brotli動的圧縮
brotli on;
brotli_comp_level 6;                  # 動的圧縮はレベル6推奨(11はCPU過負荷)
brotli_min_length 1000;

brotli_types
    text/plain
    text/css
    text/javascript
    text/xml
    application/javascript
    application/json
    application/xml
    application/rss+xml
    font/opentype
    font/ttf
    image/svg+xml;

# Brotli静的圧縮(事前圧縮された.brファイルを提供)
brotli_static on;

8.3 デュアル圧縮戦略

Brotli対応ブラウザにはBrotliで、非対応ブラウザにはGzipでフォールバックする。

# ビルド時に静的ファイルを事前圧縮(CI/CDパイプライン)
# gzip -k -9 dist/**/*.{js,css,html,json,svg}
# brotli -k -q 11 dist/**/*.{js,css,html,json,svg}

# Nginx設定
brotli_static on;             # .brファイルがあれば優先提供
gzip_static on;               # .gzファイルがあれば提供(Brotli非対応時)
gzip on;                      # 事前圧縮ファイルがなければ動的gzip

圧縮性能比較:

アルゴリズム圧縮率(一般JS)CPU負荷(動的)ブラウザ対応
Gzip L670-75%低い99%以上
Brotli L675-80%中程度96%以上
Brotli L1180-85%高い(静的専用)96%以上

9. セキュリティヘッダー

9.1 総合セキュリティヘッダー設定

# /etc/nginx/conf.d/security-headers.conf

# ── クリックジャッキング防止 ──
add_header X-Frame-Options "DENY" always;
# DENY: いかなるサイトもiframeで埋め込み不可
# SAMEORIGIN: 同じドメインのみiframe許可
# 参考: CSP frame-ancestorsがより現代的な代替手段

# ── MIMEタイプスニッフィング防止 ──
add_header X-Content-Type-Options "nosniff" always;
# ブラウザがContent-Typeを無視して独自判断することを防止

# ── XSSフィルター(レガシー、現代ブラウザはCSPを使用) ──
add_header X-XSS-Protection "0" always;
# 最新の推奨: "0"(無効化)― CSPがより安全で正確

# ── Referrer情報制御 ──
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 同じドメイン: 完全なURLを送信
# 異なるドメイン: origin(ドメイン)のみ送信
# HTTP→HTTPSダウングレード: 送信しない

# ── パーミッションポリシー(カメラ、マイク、位置情報など制御) ──
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always;
# すべての機能を無効化(必要なもののみ許可)

# ── HSTS ──
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

# ── Cross-Originポリシー ──
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;
add_header Cross-Origin-Resource-Policy "same-origin" always;

9.2 Content Security Policy (CSP)

CSPは最も強力なセキュリティヘッダーだが、設定が複雑である。Report-Onlyモードで開始して違反事項をモニタリングした後、段階的に適用することが推奨される。

# ── 第1段階:Report-Onlyモード(ブロックせずに違反のみ報告) ──
add_header Content-Security-Policy-Report-Only
    "default-src 'self';
     script-src 'self' https://cdn.example.com;
     style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
     img-src 'self' data: https:;
     font-src 'self' https://fonts.gstatic.com;
     connect-src 'self' https://api.example.com;
     frame-ancestors 'none';
     base-uri 'self';
     form-action 'self';
     report-uri /csp-report;" always;

# ── 第2段階:実際の適用(違反時にブロック) ──
add_header Content-Security-Policy
    "default-src 'self';
     script-src 'self' https://cdn.example.com;
     style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
     img-src 'self' data: https:;
     font-src 'self' https://fonts.gstatic.com;
     connect-src 'self' https://api.example.com;
     frame-ancestors 'none';
     base-uri 'self';
     form-action 'self';" always;

9.3 Server Blockでの統合適用

server {
    listen 443 ssl http2;
    server_name example.com;

    include /etc/nginx/conf.d/ssl-params.conf;
    include /etc/nginx/conf.d/security-headers.conf;

    # セキュリティ関連の追加設定
    server_tokens off;                   # Nginxバージョン情報を非表示
    more_clear_headers Server;           # Serverヘッダーを削除(headers-moreモジュール)

    # ...
}

10. アクセス制御(Access Control)

10.1 IPベースアクセス制御

# ── 管理者ページ:特定IPのみ許可 ──
location /admin/ {
    allow 10.0.0.0/8;            # 内部ネットワーク
    allow 203.0.113.50;          # 特定の管理者IP
    deny all;                    # 残りはすべて拒否

    proxy_pass http://backend;
}

# ── 特定IP遮断 ──
location / {
    deny 192.168.1.100;          # 特定IP遮断
    deny 10.0.0.0/24;            # サブネット遮断
    allow all;                   # 残りを許可
    # 注意:allow/denyの順序が重要!先にマッチしたルールが適用される

    proxy_pass http://backend;
}

10.2 HTTP Basic Authentication

# htpasswdファイル作成
sudo apt install apache2-utils          # Debian/Ubuntu
# sudo yum install httpd-tools          # RHEL/CentOS

# ユーザー作成(-c: ファイル新規作成、-B: bcryptハッシュ)
sudo htpasswd -cB /etc/nginx/.htpasswd admin

# ユーザー追加
sudo htpasswd -B /etc/nginx/.htpasswd developer
# ── 特定パスにBasic Authを適用 ──
location /admin/ {
    auth_basic "Administrator Area";               # 認証プロンプトメッセージ
    auth_basic_user_file /etc/nginx/.htpasswd;     # パスワードファイル

    proxy_pass http://backend;
}

# ── 特定パスのみ認証免除 ──
location /admin/health {
    auth_basic off;                                # ヘルスチェックは認証免除
    proxy_pass http://backend;
}

10.3 IP + Auth組み合わせ(satisfyディレクティブ)

location /admin/ {
    # satisfy any → IP許可 OR 認証成功のいずれかを満たせばアクセス許可
    # satisfy all → IP許可 AND 認証成功の両方を満たす必要がある
    satisfy any;

    # IPホワイトリスト
    allow 10.0.0.0/8;
    deny all;

    # Basic Auth(IPホワイトリスト外からのアクセス時)
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/.htpasswd;

    proxy_pass http://backend;
}

10.4 GeoIPベースアクセス制御

# GeoIP2モジュールが必要(ngx_http_geoip2_module)
geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb {
    auto_reload 60m;
    $geoip2_data_country_code country iso_code;
}

# 特定の国を遮断
map $geoip2_data_country_code $allowed_country {
    default yes;
    CN      no;    # 中国
    RU      no;    # ロシア
}

server {
    if ($allowed_country = no) {
        return 403;
    }
}

11. ログ設定

11.1 基本ログ設定

# ── Access Log ──
# デフォルトlog_format: combined
log_format combined '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent"';

access_log /var/log/nginx/access.log combined;

# ── Error Log ──
# レベル: debug, info, notice, warn, error, crit, alert, emerg
error_log /var/log/nginx/error.log warn;

11.2 JSONログフォーマット(ログ分析ツール連携)

JSON形式の構造化ロギングは、Elasticsearch、Datadog、Splunkなどの分析ツールとの連携に必須である。

log_format json_combined escape=json
    '{'
        '"time":"$time_iso8601",'
        '"remote_addr":"$remote_addr",'
        '"remote_user":"$remote_user",'
        '"request_method":"$request_method",'
        '"request_uri":"$request_uri",'
        '"server_protocol":"$server_protocol",'
        '"status":$status,'
        '"body_bytes_sent":$body_bytes_sent,'
        '"request_time":$request_time,'
        '"http_referer":"$http_referer",'
        '"http_user_agent":"$http_user_agent",'
        '"http_x_forwarded_for":"$http_x_forwarded_for",'
        '"upstream_addr":"$upstream_addr",'
        '"upstream_status":"$upstream_status",'
        '"upstream_response_time":"$upstream_response_time",'
        '"ssl_protocol":"$ssl_protocol",'
        '"ssl_cipher":"$ssl_cipher",'
        '"request_id":"$request_id"'
    '}';

access_log /var/log/nginx/access.json.log json_combined;

11.3 条件付きロギング

# ── ヘルスチェックリクエストのログ除外 ──
map $request_uri $loggable {
    ~*^/health   0;
    ~*^/ready    0;
    ~*^/metrics  0;
    default      1;
}

access_log /var/log/nginx/access.log combined if=$loggable;

# ── エラーリクエストのみ個別ロギング ──
map $status $is_error {
    ~^[45]  1;
    default 0;
}

access_log /var/log/nginx/error_requests.log combined if=$is_error;

# ── 低速リクエストのロギング(1秒以上) ──
map $request_time $is_slow {
    ~^[1-9]    1;    # 1秒以上
    ~^[0-9]{2} 1;    # 10秒以上
    default    0;
}

access_log /var/log/nginx/slow_requests.log json_combined if=$is_slow;

11.4 ドメイン別ログ分離

server {
    server_name example.com;
    access_log /var/log/nginx/example.com.access.log json_combined;
    error_log /var/log/nginx/example.com.error.log warn;
}

server {
    server_name api.example.com;
    access_log /var/log/nginx/api.example.com.access.log json_combined;
    error_log /var/log/nginx/api.example.com.error.log warn;
}

11.5 ログローテーション(logrotate)

# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
    daily                    # 毎日ローテーション
    missingok                # ログファイルがなくてもエラーにしない
    rotate 14                # 14日分保持
    compress                 # gzip圧縮
    delaycompress            # 直前のファイルは圧縮しない
    notifempty               # 空のファイルはローテーションしない
    create 0640 nginx adm    # 新規ファイルの権限
    sharedscripts
    postrotate
        # Nginxにログファイルの再オープンシグナルを送信
        if [ -f /run/nginx.pid ]; then
            kill -USR1 $(cat /run/nginx.pid)
        fi
    endscript
}

12. パフォーマンスチューニング

12.1 Workerプロセスと接続

# ── Main Context ──
worker_processes auto;             # CPUコア数に合わせる(手動設定: 4, 8など)
worker_rlimit_nofile 65535;        # Worker当たりの最大ファイルディスクリプタ数

events {
    worker_connections 4096;       # Worker当たりの最大同時接続数
    multi_accept on;               # 1つのイベントループで複数接続を受け入れる
    use epoll;                     # Linux: epoll(デフォルト)
}

最大同時接続数の計算式:

最大接続数 = worker_processes x worker_connections
: 4 workers x 4096 connections = 16,384同時接続

リバースプロキシ時(クライアント + バックエンドの2接続):
実際の同時クライアント数 = 16,384 / 2 = 8,192

12.2 Keepalive設定

http {
    # ── クライアントKeepalive ──
    keepalive_timeout 65;          # クライアントkeepalive維持時間(秒)
    keepalive_requests 1000;       # 1つのkeepalive接続で処理する最大リクエスト数

    # ── タイムアウト ──
    client_body_timeout 12;        # クライアントリクエストボディ受信タイムアウト
    client_header_timeout 12;      # クライアントリクエストヘッダー受信タイムアウト
    send_timeout 10;               # クライアントへのレスポンス送信タイムアウト
    reset_timedout_connection on;  # タイムアウト接続を即座にリセット(メモリ解放)
}

12.3 バッファ設定

http {
    # ── クライアントリクエストバッファ ──
    client_body_buffer_size 16k;   # リクエストボディバッファ(超過時にディスク書き込み)
    client_header_buffer_size 1k;  # リクエストヘッダーバッファ
    client_max_body_size 100m;     # 最大アップロードサイズ(デフォルト1MB)
    large_client_header_buffers 4 16k;  # 大きなヘッダー用バッファ

    # ── プロキシバッファ ──
    proxy_buffers 16 32k;          # バックエンドレスポンス格納バッファ
    proxy_buffer_size 16k;         # 最初のレスポンス(ヘッダー)バッファ
    proxy_busy_buffers_size 64k;   # クライアントへ送信中のバッファサイズ
    proxy_temp_file_write_size 64k; # ディスクに書き込む一時ファイルサイズ
}

12.4 プロダクションパフォーマンスチューニング総合

# /etc/nginx/nginx.conf ― プロダクション最適化

user nginx;
worker_processes auto;
worker_rlimit_nofile 65535;
pid /run/nginx.pid;

events {
    worker_connections 4096;
    multi_accept on;
    use epoll;
}

http {
    # ── MIME & 基本設定 ──
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    charset utf-8;
    server_tokens off;              # バージョン情報を非表示

    # ── ファイル転送最適化 ──
    sendfile on;                    # カーネルレベルのファイル転送
    tcp_nopush on;                  # sendfileと併用:パケット最適化
    tcp_nodelay on;                 # Nagleアルゴリズムを無効化
    aio on;                         # 非同期I/O

    # ── Keepalive ──
    keepalive_timeout 65;
    keepalive_requests 1000;

    # ── タイムアウト ──
    client_body_timeout 12;
    client_header_timeout 12;
    send_timeout 10;
    reset_timedout_connection on;

    # ── バッファ ──
    client_body_buffer_size 16k;
    client_header_buffer_size 1k;
    client_max_body_size 100m;
    large_client_header_buffers 4 16k;

    # ── Open File Cache ──
    open_file_cache max=10000 inactive=30s;
    open_file_cache_valid 60s;
    open_file_cache_min_uses 2;
    open_file_cache_errors on;

    # ── 圧縮、SSL、ログなどinclude ──
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*.conf;
}

13. URLリライティングとリダイレクション

13.1 returnディレクティブ(推奨)

returnrewriteよりシンプルで効率的である。URL変更が必要なほとんどの場合、まずreturnを検討すべきである。

# ── HTTP → HTTPSリダイレクト ──
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

# ── www → non-www正規化 ──
server {
    listen 443 ssl http2;
    server_name www.example.com;
    return 301 https://example.com$request_uri;
}

# ── ドメイン変更リダイレクト ──
server {
    listen 80;
    listen 443 ssl http2;
    server_name old-domain.com www.old-domain.com;
    return 301 https://new-domain.com$request_uri;
}

# ── 特定パスリダイレクト ──
location /old-page {
    return 301 /new-page;
}

# ── メンテナンスモード ──
location / {
    return 503;                  # Service Unavailable
}

# error_pageと組み合わせ
error_page 503 /maintenance.html;
location = /maintenance.html {
    root /var/www/html;
    internal;
}

13.2 rewriteディレクティブ

rewriteは正規表現ベースのURL変換が必要な場合に使用する。

# ── 基本構文 ──
# rewrite regex replacement [flag];
# flag: last | break | redirect (302) | permanent (301)

# ── バージョンなしのAPIパスに変換 ──
rewrite ^/api/v1/(.*)$ /api/$1 last;
# /api/v1/users → /api/users に内部リライト
# last: 新しいlocationマッチングを開始

# ── 拡張子除去(Clean URL) ──
rewrite ^/(.*)\.html$ /$1 permanent;
# /about.html → /about(301リダイレクト)

# ── クエリストリング付きリライト ──
rewrite ^/search/(.*)$ /search?q=$1? last;
# 末尾に?を付けると元のクエリストリングが除去される

# ── 多言語URL ──
rewrite ^/ko/(.*)$ /$1?lang=ko last;
rewrite ^/en/(.*)$ /$1?lang=en last;
rewrite ^/ja/(.*)$ /$1?lang=ja last;

rewriteフラグ比較:

フラグ動作用途
lastリライト後に新しいlocationマッチング内部ルーティング変更
breakリライト後に現在のlocation内で処理現在のブロック内変換
redirect302一時リダイレクト一時的な移動
permanent301永久リダイレクト永久的な移動

13.3 try_filesディレクティブ

try_filesはファイル/ディレクトリの存在を順次確認し、SPAやフレームワークで必須である。

# ── SPA(React、Vue、Angularなど) ──
location / {
    root /var/www/spa;
    try_files $uri $uri/ /index.html;
    # 1. $uri: リクエストされたファイルがあるか確認
    # 2. $uri/: ディレクトリがあるか確認
    # 3. /index.html: 上記すべてない場合index.html(SPAルーティング)
}

# ── Next.js / Nuxt.js ──
location / {
    try_files $uri $uri/ @proxy;
}
location @proxy {
    proxy_pass http://127.0.0.1:3000;
    include /etc/nginx/conf.d/proxy-params.conf;
}

# ── PHP(WordPress、Laravel) ──
location / {
    try_files $uri $uri/ /index.php?$args;
}

# ── 静的ファイル優先 → バックエンドフォールバック ──
location / {
    root /var/www/static;
    try_files $uri @backend;
}
location @backend {
    proxy_pass http://app_server;
}

13.4 条件付きリダイレクト

# ── モバイルデバイスリダイレクト ──
if ($http_user_agent ~* "(Android|iPhone|iPad)") {
    return 302 https://m.example.com$request_uri;
}

# ── 特定クエリパラメータベース ──
if ($arg_redirect) {
    return 302 $arg_redirect;
}

# ── mapで複雑なリダイレクト管理 ──
map $request_uri $redirect_uri {
    /old-blog/post-1    /blog/new-post-1;
    /old-blog/post-2    /blog/new-post-2;
    /products/legacy    /shop/all;
    default             "";
}

server {
    if ($redirect_uri) {
        return 301 $redirect_uri;
    }
}

14. 静的ファイル配信最適化

14.1 コアファイル転送ディレクティブ

http {
    # ── sendfile ──
    # カーネルのsendfile()システムコールを使用してファイルを直接ソケットに転送
    # ユーザースペースのメモリコピーを排除し、CPU使用量とコンテキストスイッチを削減
    sendfile on;

    # ── tcp_nopush ──
    # sendfileと連動:HTTPヘッダーとファイルの先頭部分を1つのパケットで送信
    # ネットワークパケット数を削減して帯域幅効率を向上
    tcp_nopush on;

    # ── tcp_nodelay ──
    # Nagleアルゴリズムを無効化:小さなパケットを即座に送信
    # keepalive接続でのレイテンシを削減(tcp_nopushと併用可能)
    tcp_nodelay on;
}

転送方式の比較:

sendfile off(デフォルト):
ディスク → カーネルバッファ → ユーザーメモリ(Nginx) → カーネルソケットバッファ → ネットワーク
            [読み込み]       [コピー]                     [書き込み]

sendfile on:
ディスク → カーネルバッファ ─────────────────────────→ カーネルソケットバッファ → ネットワーク
            [読み込み]     [ゼロコピー:CPU介入なし]       [転送]

14.2 Open File Cache

繰り返しリクエストされるファイルのディスクリプタ、サイズ、更新時刻をキャッシュして、ファイルシステム照会を最小化する。

http {
    # 最大10,000件のファイル情報をキャッシュ、30秒間未使用で削除
    open_file_cache max=10000 inactive=30s;

    # キャッシュされた情報を60秒ごとに再検証
    open_file_cache_valid 60s;

    # 最低2回以上リクエストされたファイルのみキャッシュ(1回限りのリクエストをフィルタ)
    open_file_cache_min_uses 2;

    # ファイル未検出(ENOENT)エラーもキャッシュ(存在しないファイルの繰り返し照会を防止)
    open_file_cache_errors on;
}

14.3 静的ファイル配信総合設定

server {
    listen 443 ssl http2;
    server_name static.example.com;

    root /var/www/static;

    # ── 画像 ──
    location ~* \.(jpg|jpeg|png|gif|ico|webp|avif|svg)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
        add_header Vary "Accept-Encoding";
        access_log off;
        log_not_found off;                 # 404ログも無効化

        # 画像専用制限
        limit_rate 2m;                     # 接続あたり2MB/s
    }

    # ── CSS/JS(キャッシュバスティング戦略と併用) ──
    location ~* \.(css|js)$ {
        expires 365d;                      # ハッシュベースファイル名なら長期キャッシュ
        add_header Cache-Control "public, immutable";
        gzip_static on;
        brotli_static on;
    }

    # ── フォント ──
    location ~* \.(woff|woff2|ttf|eot|otf)$ {
        expires 365d;
        add_header Cache-Control "public, immutable";
        add_header Access-Control-Allow-Origin "*";
    }

    # ── メディアファイル ──
    location ~* \.(mp4|webm|ogg|mp3|wav)$ {
        expires 30d;
        add_header Cache-Control "public";

        # Rangeリクエスト対応(動画シーク)
        add_header Accept-Ranges bytes;
    }

    # ── ディレクトリリスティング防止 ──
    location / {
        autoindex off;
    }

    # ── 隠しファイルへのアクセス遮断 ──
    location ~ /\. {
        deny all;
        access_log off;
        log_not_found off;
    }
}

14.4 HTTP/2 Server Push(オプション)

# 主要リソースをクライアントリクエスト前に事前送信
location = /index.html {
    http2_push /css/main.css;
    http2_push /js/app.js;
    http2_push /images/logo.webp;
}

参考: HTTP/2 Server Pushは一部のブラウザでサポートが終了しており、103 Early Hintsが代替として台頭している。


15. ヘルスチェックとモニタリング

15.1 Stub Status(基本モニタリング)

Nginx OSSに含まれるstub_statusモジュールでリアルタイムの接続状態を確認できる。

server {
    listen 8080;                          # 別ポートで分離
    server_name localhost;

    # 内部ネットワークからのアクセスのみ許可
    allow 10.0.0.0/8;
    allow 127.0.0.1;
    deny all;

    location /nginx_status {
        stub_status;
    }

    location /health {
        access_log off;
        return 200 "OK\n";
        add_header Content-Type text/plain;
    }
}

stub_status出力例:

Active connections: 291
server accepts handled requests
 16630948 16630948 31070465
Reading: 6 Writing: 179 Waiting: 106
項目意味
Active connections現在のアクティブ接続数(Reading + Writing + Waiting)
accepts総受け入れ接続数
handled総処理接続数(= acceptsなら正常)
requests総処理リクエスト数
Readingクライアントリクエストヘッダーを読み込み中の接続数
Writingクライアントにレスポンスを送信中の接続数
Waitingkeepaliveアイドル接続数

15.2 パッシブヘルスチェック(Upstreamモニタリング)

Nginx OSSは実際のトラフィックベースのパッシブヘルスチェックのみ対応している。

upstream backend {
    server 10.0.0.1:8080 max_fails=3 fail_timeout=30s;
    server 10.0.0.2:8080 max_fails=3 fail_timeout=30s;
    server 10.0.0.3:8080 max_fails=3 fail_timeout=30s backup;

    # max_fails=3: 30秒以内に3回連続失敗でサーバーを異常と判定
    # fail_timeout=30s: 異常判定後30秒間そのサーバーへリクエスト送信しない
    #                   30秒後に再度リクエストを送り復旧を確認
}

server {
    location / {
        proxy_pass http://backend;

        # どのレスポンスを「失敗」と判定するかを設定
        proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
        proxy_next_upstream_timeout 10s;   # 次のサーバーを試行する最大時間
        proxy_next_upstream_tries 3;       # 最大リトライ回数
    }
}

15.3 アクティブヘルスチェック(NGINX Plusまたは外部ソリューション)

# ── NGINX Plusでのアクティブヘルスチェック ──
upstream backend {
    zone backend_zone 64k;           # 共有メモリゾーン必須

    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
}

server {
    location / {
        proxy_pass http://backend;
        health_check interval=5s      # 5秒ごとにチェック
                     fails=3           # 3回失敗で異常
                     passes=2          # 2回成功で復旧
                     uri=/health;      # ヘルスチェックエンドポイント
    }
}

15.4 Prometheus連携(nginx-prometheus-exporter)

# docker-compose.yml
services:
  nginx-exporter:
    image: nginx/nginx-prometheus-exporter:1.3
    command:
      - --nginx.scrape-uri=http://nginx:8080/nginx_status
    ports:
      - '9113:9113'
    depends_on:
      - nginx
# prometheus.yml
scrape_configs:
  - job_name: 'nginx'
    static_configs:
      - targets: ['nginx-exporter:9113']

15.5 カスタムヘルスチェックエンドポイント

# ── Liveness Probe(Nginx自体の動作確認) ──
location = /healthz {
    access_log off;
    return 200 "alive\n";
    add_header Content-Type text/plain;
}

# ── Readiness Probe(バックエンド接続を含む確認) ──
location = /readyz {
    access_log off;
    proxy_pass http://backend/health;
    proxy_connect_timeout 2s;
    proxy_read_timeout 2s;

    # バックエンドレスポンス失敗時503
    error_page 502 503 504 = @not_ready;
}

location @not_ready {
    return 503 "not ready\n";
    add_header Content-Type text/plain;
}
# Kubernetesでの活用
apiVersion: v1
kind: Pod
spec:
  containers:
    - name: nginx
      livenessProbe:
        httpGet:
          path: /healthz
          port: 8080
        initialDelaySeconds: 5
        periodSeconds: 10
      readinessProbe:
        httpGet:
          path: /readyz
          port: 8080
        initialDelaySeconds: 5
        periodSeconds: 5

総合プロダクション設定チェックリスト

プロダクション環境にNginxをデプロイする際に確認すべき重要項目を整理する。

カテゴリ項目ステータス
アーキテクチャworker_processes auto設定[ ]
アーキテクチャworker_connections適切な値に設定[ ]
SSL/TLSTLSv1.2 + TLSv1.3のみ有効化[ ]
SSL/TLS強力なCipher Suitesの設定[ ]
SSL/TLSOCSP Stapling有効化[ ]
SSL/TLSHSTSヘッダー設定[ ]
SSL/TLSHTTP → HTTPSリダイレクト[ ]
セキュリティserver_tokens off[ ]
セキュリティセキュリティヘッダー(CSP、X-Frame-Optionsなど)設定[ ]
セキュリティRate Limiting設定[ ]
セキュリティ管理者ページアクセス制御[ ]
パフォーマンスsendfile、tcp_nopush、tcp_nodelay有効化[ ]
パフォーマンスGzip/Brotli圧縮設定[ ]
パフォーマンスopen_file_cache設定[ ]
パフォーマンス静的ファイルブラウザキャッシング設定[ ]
パフォーマンスUpstream keepalive設定[ ]
キャッシングproxy_cacheまたはfastcgi_cache設定[ ]
キャッシングproxy_cache_use_stale設定[ ]
モニタリングstub_status有効化(内部専用)[ ]
モニタリングヘルスチェックエンドポイント構成[ ]
ロギングJSONログフォーマット設定[ ]
ロギングログローテーション設定[ ]
ロギングヘルスチェック/静的ファイルログ除外[ ]
プロキシ必須プロキシヘッダー設定[ ]
プロキシWebSocketプロキシ設定(必要時)[ ]
ロードバランシング適切なアルゴリズム選択[ ]
ロードバランシングバックアップサーバー構成[ ]

参考資料