Nginx でセキュリティヘッダを設定する|Web 担当者向け実践手順
目次
この記事でわかること
- Nginx でセキュリティヘッダを書く 3 つの場所と使い分け
add_headerのalwaysキーワードを必ず付ける理由locationブロック内add_headerの罠(上位のadd_headerが無効化される)- Web 担当者向けの推奨設定(コピペ用)
Nginx でヘッダを書く 3 つの場所
1. http {} ブロック(全サイト一括)
/etc/nginx/nginx.conf の http {} ブロックに書くと、サーバー上の全サイトに適用されます。1 サーバー 1 サイトの構成なら楽ですが、サイト別に上書きしたい場合は適しません。
2. server {} ブロック(推奨)
/etc/nginx/sites-available/<site>.conf の server {} ブロックに書く。最も標準的な場所で、サイト別に独立して設定できます。
3. location {} ブロック(パス別)
特定のパス(例: /wp-admin/)だけ別のヘッダを付けたい場合に使います。ただし罠があるので注意(後述)。
推奨設定(コピペ用)
server {
listen 443 ssl http2;
server_name example.co.jp;
# SSL 証明書 / 鍵の設定 ...
# セキュリティヘッダ(必ず always キーワードを付ける)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always;
# CSP は最初 Report-Only で開始(観察目的なので unsafe-inline は入れない)
add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; report-uri /csp-report" always;
# ... 他の設定 ...
}
設定後の反映:
sudo nginx -t # 構文チェック
sudo nginx -s reload # リロード(無停止)
always キーワードを必ず付ける理由
# 悪い例(always なし)
add_header X-Frame-Options "SAMEORIGIN";
# 良い例(always 付き)
add_header X-Frame-Options "SAMEORIGIN" always;
always キーワードがない場合、Nginx は限られた応答コード(200 / 201 / 204 / 206 / 301 / 302 / 303 / 304 / 307 / 308)にしかヘッダを付けません(公式 ngx_http_headers_module の仕様)。
問題:
- 4xx / 5xx エラーページにセキュリティヘッダが付かない
- 攻撃者が意図的にエラーを発生させて、ヘッダなしの応答を引き出せる
- HSTS が効かないエラーページから http に降格させられる可能性
always を付けると、全ステータスコードに対してヘッダ付与されます。セキュリティヘッダは必ず always を付けてください。
location ブロックの罠
セキュリティヘッダ設定で最も間違いやすいポイントです。
# 親 server {} ブロック
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
location /api/ {
# 子 location ブロック内で add_header を使うと…
add_header X-API-Version "v1";
# ↑ ここで上の 2 つの add_header が「全部消える」!!
}
Nginx の仕様で「子ブロックで add_header を 1 つでも書くと、親ブロックの add_header が継承されない」という罠があります(追加ではなく上書きの動作)。
正しい書き方
# 親 server {} ブロック
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
location /api/ {
# 親で設定した全ヘッダを再記述する
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-API-Version "v1" always;
}
または more_headers(ngx_headers_more モジュール)を使うと継承挙動が改善されますが、追加モジュールが必要です。
/wp-admin/ 等で CSP を分けたい場合
WordPress のように管理画面と公開ページで CSP を変えたい場合:
server {
# 公開ページ用の厳しい CSP
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'" always;
add_header X-Frame-Options "SAMEORIGIN" always;
# ... 他の always 系ヘッダ
location /wp-admin/ {
# 管理画面は緩める。ただし他のヘッダも全部書き直す必要あり
add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval'" always;
add_header X-Frame-Options "SAMEORIGIN" always;
# ... 他の always 系ヘッダを再記述
}
}
CSP に nonce を動的に埋める場合
Nginx 単体では nonce のランダム生成ができないため、バックエンド(PHP / Node.js / Rails 等)で nonce を生成 → CSP ヘッダを返す設計が必要です。Nginx は proxy_pass で渡し、バックエンドの返したヘッダをそのまま使います。
location / {
proxy_pass http://backend;
proxy_pass_header Content-Security-Policy; # バックエンドの CSP を尊重
}
このパターンは CSP の unsafe-inline 撤去手順 で詳しく解説しています。
設定後の確認
構文チェック
sudo nginx -t
エラーが出たら直してから reload。
コマンドラインでヘッダ確認
curl -I https://example.co.jp/
ブラウザの開発者ツール
F12 → Network → 任意のページ → Response Headers
ドメイン番人の単発チェック
Web セキュリティヘッダ 単発チェック で 30 秒で全ヘッダの状態がスコアリングされます。
まとめ
- 推奨は
server {}ブロックにヘッダを書く - すべての
add_headerにalwaysを付ける(4xx/5xx でもヘッダ付与) location内でadd_headerを使うと親の add_header が継承されない罠に注意- 設定後は必ず
nginx -tで構文確認 →nginx -s reload
まずは現状を把握しましょう
ドメイン番人の Web セキュリティヘッダ 単発チェック で 30 秒で確認できます。設定支援が必要な場合は Web セキュリティヘッダ診断+設定支援(3 万円〜)でご相談ください。
サーバー別の設定: WordPress でセキュリティヘッダを設定する / Cloudflare でセキュリティヘッダを設定する も参照してください。
各ヘッダの個別解説:
- CSP(Content-Security-Policy)とは
- HSTS の設定方法
- クリックジャッキング対策
- Referrer-Policy のおすすめ設定値
- Permissions-Policy とは
総合点検は 無料のドメイン診断 を、SSL 単独は SSL 単発チェック をご利用ください。