Web セキュリティNginxWeb 担当者How-to

Nginx でセキュリティヘッダを設定する|Web 担当者向け実践手順

ドメイン番人6 分で読めます
目次

この記事でわかること

  • Nginx でセキュリティヘッダを書く 3 つの場所と使い分け
  • add_headeralways キーワードを必ず付ける理由
  • location ブロック内 add_header の罠(上位の add_header が無効化される)
  • Web 担当者向けの推奨設定(コピペ用)

Nginx でヘッダを書く 3 つの場所

Nginx でヘッダを書く 3 つの場所

1. http {} ブロック(全サイト一括)

/etc/nginx/nginx.confhttp {} ブロックに書くと、サーバー上の全サイトに適用されます。1 サーバー 1 サイトの構成なら楽ですが、サイト別に上書きしたい場合は適しません。

2. server {} ブロック(推奨)

/etc/nginx/sites-available/<site>.confserver {} ブロックに書く。最も標準的な場所で、サイト別に独立して設定できます。

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;

    # ... 他の設定 ...
}

Nginx server {} ブロックの推奨設定

設定後の反映:

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_headeralways を付ける(4xx/5xx でもヘッダ付与)
  • location 内で add_header を使うと親の add_header が継承されない罠に注意
  • 設定後は必ず nginx -t で構文確認 → nginx -s reload

まずは現状を把握しましょう

ドメイン番人の Web セキュリティヘッダ 単発チェック で 30 秒で確認できます。設定支援が必要な場合は Web セキュリティヘッダ診断+設定支援(3 万円〜)でご相談ください。

サーバー別の設定: WordPress でセキュリティヘッダを設定する / Cloudflare でセキュリティヘッダを設定する も参照してください。

各ヘッダの個別解説:

総合点検は 無料のドメイン診断 を、SSL 単独は SSL 単発チェック をご利用ください。

次の一歩は無料診断から。