技術・開発

Nginxでadd_headerが効かない?継承ルールの罠と「always」指定の重要性を解説

2020年8月31日

Nginxでセキュリティヘッダー(HSTSやX-Content-Type-Options)や、特定の「Allow」ヘッダーなどを付与する際、add_header ディレクティブは非常に便利です。

しかし、設定したはずのヘッダーがブラウザ側で確認できない、あるいは特定のページだけ消えてしまうといった現象に遭遇することがあります。これにはNginx特有の「継承ルール」と「ステータスコード」が深く関わっています。

今回は、add_header が効かない時の2大原因とその対策についてまとめます。

1. serverとlocationに記載した場合の「継承の罠」

Nginxの設定で、server ブロックと location ブロックの両方に add_header を記載した場合、「現在の階層(location)で定義されたものだけ」が有効になります。

つまり、上位の server ブロックで定義した設定は、下位の location ブロックに add_header が一つでも書かれた時点で、すべて上書き(無視)されてしまいます。

設定例:継承されないケース

例えば以下の設定では、index.html にアクセスした際、Strict-Transport-Security などのヘッダーは付与されません。

server {
    listen 443 ssl http2 default_server;
    server_name xxx.xx;
    charset UTF-8;

    # server階層の設定
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
    add_header Allow "GET, POST, HEAD" always;
    add_header X-Content-Type-Options nosniff;

    location = /index.html {
        # ここでadd_headerを定義したため、上位の3つは無視される
        add_header Allow "GET, POST" always;
        root /xxxx/html;
    }
}

公式ドキュメントの定義

Nginxの公式ページにも、以下のように明記されています。

There could be several add_header directives. These directives are inherited from the previous level if and only if there are no add_header directives defined on the current level.
(現在の階層に add_header ディレクティブが定義されていない場合に限り、前の階層から継承されます。)

解決策

location 内で個別の add_header を設定しつつ、共通のヘッダーも付与したい場合は以下の対応が必要です。

  1. 設定をコピーする: 各 location ブロックに、server ブロックで共通化したい設定を再度記述します。
  2. サードパーティーモジュールの使用: もし環境が許すなら、headers-more-nginx-moduleを使用することで、階層に関係なくヘッダーを制御できるようになります。

2. 「always」なしは特定のHTTPステータスコードのみ有効

もう一つの落とし穴が、レスポンスのステータスコードです。
公式ページによると、デフォルト(always パラメータなし)の状態では、add_header は以下のステータスコードに対してのみ動作します。(カッコ内はNginxの対応バージョン)

  • 200
  • 201 (1.3.10以降)
  • 204
  • 206
  • 301
  • 302
  • 303
  • 304
  • 307 (1.1.16, 1.0.13以降) or 308 (1.13.0以降)

REST APIやエラーページでの注意点

REST APIなどで上記以外のステータスコードを使用している場合や、4xx系・5xx系のエラーレスポンスで返却された場合は、add_headerが付与されなくなります。

解決策:全てのステータスコードで付与する設定例

エラー発生時にも共通のヘッダーを付与したい場合は、必ず末尾に always パラメータを追加します。

server {
    listen 443 ssl http2 default_server;
    server_name xxx.xx;
    charset UTF-8;

    # 「always」を付与することで、全ステータスコードでヘッダーが返る
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header Allow "GET, POST, HEAD" always;
    add_header X-Content-Type-Options nosniff always;
}

上記の例では、全てのステータスコードに対して指定したヘッダーを追加するように設定しています。

まとめ

add_header についての仕様を正しく理解していないと、設定したはずのセキュリティヘッダーが特定のページやエラー画面で漏れてしまうなど、思わぬところでハマりかねません。

  1. locationブロックに個別のadd_headerを書いていないか?
  2. エラー時にも付与したい設定に「always」を忘れていないか?

設定を変更した際は、この2点を意識して確認しておくことをおすすめします。

-技術・開発
-, , , , , ,