nginx
Nginxリバースプロキシおよびウェブサーバー設定スキル。 使用する場面: - アプリケーションサーバー(Node.js、Python、Ruby、Java)の前段にNginxをリバースプロキシとして設定する - 証明書を使用したHTTPS/TLS終端の設定 - 仮想ホスト、アップストリームロードバランシング、レート制限の設定 - 静的ファイルの配信最適化、gzip圧縮、キャッシュヘッダーの調整 - セキュリティヘッダー(HSTS、CSP、X-Frame-Options、X-Content-Type-Options)の追加 - WebSocketプロキシの設定 - 502、504、413エラーやSSLハンドシェイクエラーの診断 使用しない場面: - アプリケーションロジック(Nginxではなくアプリケーションサーバーで対応) - コンテナオーケストレーション経由のルーティング(Kubernetes IngressまたはServiceメッシュスキルを使用) - 完全なAPIゲートウェイ機能(ユーザー単位のレート制限、認証、JWT認証) - Apache httpd設定
description の原文を見る
Nginx reverse proxy and web server configuration skill. USE WHEN: - Configuring Nginx as a reverse proxy in front of application servers (Node.js, Python, Ruby, Java) - Setting up HTTPS/TLS termination with certificates - Configuring virtual hosts, upstream load balancing, and rate limiting - Optimising static file serving, gzip compression, and cache headers - Adding security headers (HSTS, CSP, X-Frame-Options, X-Content-Type-Options) - Setting up WebSocket proxying - Diagnosing 502, 504, 413, or SSL handshake errors DO NOT USE FOR: - Application-level logic (belongs in the app server, not Nginx) - Container orchestration routing (use Kubernetes Ingress or a service mesh skill instead) - Full API gateway features (rate-limit-by-user, auth, JWT — use api-gateway skill) - Apache httpd configuration
SKILL.md 本文
Nginx — リバースプロキシ & ウェブサーバー
コア概念
Nginx はマスター/ワーカープロセスモデルを使用します。マスターが設定を読み込みワーカーを管理し、ワーカーが接続を処理します。各ワーカーのイベントループはノンブロッキングなため、単一のワーカーで数千の同時接続を処理できます。
主な設定階層:
http { ... } # グローバル HTTP 設定
upstream backend { ... } # アプリサーバープール
server { ... } # 仮想ホスト (vhost)
location / { ... } # URI マッチングブロック
Nginx は server ブロックを listen ポートと server_name で評価します。server 内の location ブロックは順に マッチされます: 完全一致 (=)、プリフィックス最長一致 (^~)、正規表現 (~、~*)、その後暗黙的プリフィックス。
本番環境 HTTPS サーバーブロック (完全テンプレート)
# /etc/nginx/sites-available/myapp.conf
# --- Upstream プール -----------------------------------------------------------
upstream app_backend {
least_conn; # Route to least-busy worker
server 127.0.0.1:3000;
server 127.0.0.1:3001;
keepalive 64; # Persistent connections to upstream
}
# --- Rate limiting zones (defined in http{} context in nginx.conf) -----------
# limit_req_zone $binary_remote_addr zone=api:10m rate=20r/s;
# limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
# --- HTTP → HTTPS リダイレクト ---------------------------------------------------
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# Let's Encrypt ACME チャレンジを許可
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
# --- メイン HTTPS サーバー -------------------------------------------------------
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on; # Nginx ≥ 1.25.1 directive (older: listen 443 ssl http2)
server_name example.com www.example.com;
# --- TLS -----------------------------------------------------------------
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
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:DHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off; # Let TLS 1.3 clients pick
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off; # Disable for forward secrecy
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;
ssl_dhparam /etc/nginx/ssl/dhparam.pem; # openssl dhparam -out dhparam.pem 2048
# --- セキュリティヘッダー ----------------------------------------------------
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self' https://api.example.com; frame-ancestors 'none';" always;
# --- Gzip ----------------------------------------------------------------
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript
application/json application/javascript application/xml+rss
application/atom+xml image/svg+xml;
gzip_min_length 1024;
# --- ログ出力 -------------------------------------------------------------
access_log /var/log/nginx/myapp.access.log combined buffer=4k flush=5s;
error_log /var/log/nginx/myapp.error.log warn;
# --- 長期キャッシュを持つ静的アセット ---------------------------------
location /static/ {
alias /var/www/myapp/static/;
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# --- レート制限付き API エンドポイント ------------------------------------------
location /api/auth/ {
limit_req zone=login burst=10 nodelay;
limit_req_status 429;
proxy_pass http://app_backend;
include /etc/nginx/proxy_params;
}
location /api/ {
limit_req zone=api burst=50 nodelay;
limit_req_status 429;
proxy_pass http://app_backend;
include /etc/nginx/proxy_params;
}
# --- WebSocket エンドポイント --------------------------------------------------
location /ws/ {
proxy_pass http://app_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 3600s; # Keep WS connections open
proxy_send_timeout 3600s;
}
# --- デフォルトプロキシ -------------------------------------------------------
location / {
proxy_pass http://app_backend;
include /etc/nginx/proxy_params;
}
# --- エラーページ ---------------------------------------------------------
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /404.html {
root /var/www/myapp/errors;
internal;
}
location = /50x.html {
root /var/www/myapp/errors;
internal;
}
}
/etc/nginx/proxy_params (共有インクルードファイル)
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_set_header Connection ""; # Required for keepalive upstream
proxy_connect_timeout 10s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_buffering on;
proxy_buffer_size 8k;
proxy_buffers 8 8k;
ワーカー / グローバルチューニング (/etc/nginx/nginx.conf)
user www-data;
worker_processes auto; # One per CPU core
worker_rlimit_nofile 65535; # Match system ulimit -n
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 4096; # Max connections per worker
multi_accept on;
use epoll; # Linux only; Nginx selects automatically
}
http {
sendfile on;
tcp_nopush on; # Batch send headers + start of file
tcp_nodelay on; # Disable Nagle for keepalive connections
keepalive_timeout 75s;
keepalive_requests 1000;
server_tokens off; # Don't reveal Nginx version in headers
client_max_body_size 50m; # Increase if file uploads are needed
client_body_timeout 30s;
client_header_timeout 30s;
# レート制限ゾーン (サーバーブロックから参照)
limit_req_zone $binary_remote_addr zone=api:10m rate=20r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
limit_conn_zone $binary_remote_addr zone=addr:10m;
include /etc/nginx/mime.types;
default_type application/octet-stream; # Not text/html — avoids MIME sniffing
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
ゾーンを使用したレート制限
# ゾーン定義 (http コンテキスト):
limit_req_zone $binary_remote_addr zone=api:10m rate=20r/s;
# ロケーション内での使用:
location /api/ {
limit_req zone=api burst=100 nodelay;
# burst=100 → レート上限を超える最大 100 リクエストをキューイング可能
# nodelay → バーストリクエストを遅延させず、バースト後即座に拒否
limit_req_status 429;
proxy_pass http://app_backend;
}
$binary_remote_addr は 4 バイト (IPv4) または 16 バイト (IPv6) を使用します — $remote_addr 文字列より遥かに小さく、ゾーンメモリの MB あたりより多くのエントリを許可します。
名前付きロケーションと try_files
# SPA をインデックスフォールバック付きで提供
location / {
root /var/www/myapp/dist;
try_files $uri $uri/ @index;
}
location @index {
root /var/www/myapp/dist;
add_header Cache-Control "no-cache";
try_files /index.html =404;
}
# PHP-FPM の例 (参考用)
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
カスタムログフォーマット
# http コンテキスト内
log_format main_ext '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time uct=$upstream_connect_time '
'uht=$upstream_header_time urt=$upstream_response_time '
'cs=$upstream_cache_status';
access_log /var/log/nginx/access.log main_ext buffer=16k flush=10s;
一般的なコマンド
# 適用前に設定構文をテスト
nginx -t
nginx -T # 解析された設定もダンプ
# 接続をドロップせずに再読み込み
nginx -s reload
# グレースフルシャットダウン (接続をドレイン)
nginx -s quit
# 強制停止
nginx -s stop
# Nginx バージョンとコンパイル時モジュールを確認
nginx -V 2>&1 | tr ' ' '\n'
# ログを追跡
journalctl -u nginx -f
tail -f /var/log/nginx/myapp.access.log
# ポート 443 がどのプロセスにバインドされているかを確認
ss -tlnp | grep :443
# Nginx ワーカーのオープンファイルディスクリプタを検査
cat /proc/$(pgrep -o nginx)/limits | grep 'open files'
# systemd ユニットをリロードして再起動
systemctl daemon-reload && systemctl restart nginx
# サイトを有効/無効化
ln -s /etc/nginx/sites-available/myapp.conf /etc/nginx/sites-enabled/
unlink /etc/nginx/sites-enabled/myapp.conf
アンチパターン
| アンチパターン | 有害な理由 | 修正 |
|---|---|---|
http {} 内の default_type text/html | ブラウザが未知のファイルを HTML として実行 — XSS ベクトル | default_type application/octet-stream を設定 |
upstream ブロック内に keepalive がない | バックエンドへのリクエストごとに新しい TCP 接続; 高レイテンシと FD 使用量 | keepalive 64; と proxy_set_header Connection "" を追加 |
worker_rlimit_nofile がない | Nginx ワーカーが OS デフォルト (1024) FD リミットに達する負荷下で — サイレント障害 | worker_rlimit_nofile 65535; を設定し worker_connections を適切に引き上げ |
access_log をバッファなしで /var/log/nginx/access.log へログ出力 | リクエストごとに同期ディスク書き込み; 負荷下でスループット 10–30% 低下 | access_log に buffer=16k flush=10s を追加 |
proxy_read_timeout がない | Nginx デフォルト (60 秒) を使用; 実行時間の長い API 呼び出しがサイレント削除 | 影響を受けるロケーションで proxy_read_timeout 120s; を明示的に設定 |
server_tokens on (デフォルト) | ヘッダーに正確な Nginx バージョンを公開; 対象脆弱性スキャンに有利 | http {} で server_tokens off; を設定 |
ネストされた location 内の add_header に always がない | 4xx/5xx レスポンスでヘッダーが削除 — エラーページでセキュリティヘッダーが欠落 | 常に add_header ... always; を使用 |
アップロードエンドポイントの client_max_body_size 増加なし | デフォルトは 1m; 1 MB を超えるファイルアップロードは 413 を返す | ロケーション単位またはグローバルに適切な制限を引き上げ |
プロキシロジックに if ブロックを使用 | Nginx の if は「悪い」— 微妙なバグを引き起こす可能性; コンテキスト依存の動作 | map、limit_except、または個別の location ブロックを使用 |
ワイルドカード server_name * | 意図しない全ホスト名にマッチ; プライベート API が誤ったvhost に提供される可能性 | 明示的名を使用; キャッチオール デフォルトサーバーを 444 を返すように設定 |
トラブルシューティング
| 症状 | 考えられる原因 | 診断 / 修正 |
|---|---|---|
| 502 Bad Gateway | upstream アプリが実行していないまたは間違ったポートでリッスン | ss -tlnp | grep 3000; アプリログを確認; proxy_pass URL を検証 |
| 504 Gateway Timeout | アプリが実行しているが応答が遅い | proxy_read_timeout を増加; アプリをプロファイル; DB クエリを確認 |
| 413 Request Entity Too Large | client_max_body_size が低すぎる | ロケーション内またはグローバルにリミットを引き上げ; アップロードエンドポイントを確認 |
| SSL ハンドシェイクエラー (クライアント側) | 期限切れ証明書、不正なチェーン、TLS バージョンの不一致 | openssl s_client -connect host:443 -servername host; ssl_trusted_certificate を確認 |
| 混合コンテンツ警告 | バックエンドが HTTPS ターミネーション後も HTTP リンクを返す | proxy_set_header X-Forwarded-Proto $scheme; を設定しアプリでトラスト設定 |
| HSTS がコンフィグロールバック後 HTTP アクセスをブロック | ブラウザが Strict-Transport-Security をキャッシュ | 即座には取り消し不可; ssl-tls スキル HSTS セクションを参照 |
| WebSocket 接続が 60 秒後にドロップ | デフォルト proxy_read_timeout | WebSocket ロケーションで proxy_read_timeout 3600s; を設定 |
| レート制限が正規ユーザーに影響 | ゾーンが小さすぎるまたはレート設定が低すぎる | ゾーンサイズを増加、レートを上げ、burst パラメータを追加; geo で内部 IP をホワイトリスト |
| 静的ファイルが 403 を返す | ディレクトリまたはファイルパーミッションが不正 | ls -la /var/www/myapp/static; www-data が読み込める確認; alias vs root を確認 |
systemctl reload 後 Nginx がリロードしない | 新しい設定に構文エラー | リロード前に常に nginx -t を実行; journalctl -u nginx -n 50 を確認 |
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- claude-dev-suite
- ライセンス
- MIT
- 最終更新
- 2026/5/10
Source: https://github.com/claude-dev-suite/claude-dev-suite / ライセンス: MIT