• 診断項目 No.78/127
  • HTTP応答・エラー処理
  • 重大度 中(medium)

エラー応答のヘッダー整合性とは?404・500にセキュリティヘッダーが付かない原因・診断基準・設定方法

エラー応答のヘッダー整合性とは、404・403・500などのエラー応答にもnosniffやCSPを付け、正常応答とキャッシュ方針をそろえることです。Apache・nginxで付かない原因(always)、診断基準、設定例を解説します。

結論(エラー応答のヘッダー整合性)

エラー応答のヘッダー整合性とは、正常なページだけでなく404・403・500などのエラー応答にも防御ヘッダーを付けることです。X-Content-Type-Options: nosniffやContent-Security-Policyなどを付け、Cache-Controlの方針も正常応答とそろえます。Apacheの Header set やnginxの add_header は既定ではエラー応答に付かないため、always を指定するのが基本の対策です。

この記事のポイント

  • Apacheの Header set は既定で成功応答(2xx)にしか付かず、always を付けるとエラー応答やErrorDocumentにも付きます。
  • nginxの add_header は200・3xxなど特定の応答コードだけに付き、always(1.7.5以降)で全応答に付きます。下位階層に add_header があると上位は継承されません。
  • エラーページは要求URLを本文に表示するなど入力を反映しやすく、CSPやnosniffの欠落がXSS等の足掛かりになります。
  • セキュリティAIチェッカーは、存在しないURLの応答でnosniff・CSP(HTML時)・no-store(正常応答がno-storeの場合)の不足があれば△と判定します。
  • no-storeは一律には求めず、正常応答がno-storeなのにエラー応答だけ付いていない不整合だけを指摘します。
診断項目名エラー応答のヘッダー整合性
項目IDresponse.header-consistency
分類HTTP応答・エラー処理
重大度・配点の重み中(medium) 重み 2
関連する標準・ガイドラインOWASP ASVS 5.0.0 V3.4.4(すべてのHTTP応答に X-Content-Type-Options: nosniff)、V3.4.3(Content-Security-Policy 応答ヘッダー)、V14.3.2(機微データがブラウザにキャッシュされないよう Cache-Control: no-store 等を設定)/OWASP WSTG-ERRH-01、WSTG-CONF-14(Other HTTP Security Header Misconfigurations。安定版 v4.2 には無く、最新の開発版に収録)/CWE-693(Protection Mechanism Failure)/Apache mod_headers always 条件、nginx add_header … always

エラー応答のヘッダー整合性とは

エラー応答のヘッダー整合性とは、セキュリティヘッダー(X-Content-Type-Options: nosniff、Content-Security-Policy など)を、正常なページだけでなく 404・403・500 などのエラー応答にも付け、キャッシュ方針(Cache-Control)も正常応答と食い違わないようにそろえることです。

HTTP/1.1 404 Not Found
Content-Type: text/html; charset=UTF-8
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'

Apache や nginx の標準的な書き方では、ヘッダーが成功応答(2xx や一部の3xx)にしか付かず、エラーページだけ無防備になることがよくあります。エラーページは、要求されたURLを本文に表示するなど入力値を反映しやすく、XSS や MIME スニッフィングの足掛かりになり得ます。

セキュリティAIチェッカーは、存在しないURLへのエラー応答に主要な防御ヘッダーが付いているか、正常応答とキャッシュ方針が食い違っていないかを確認します。

エラー応答で各ヘッダーが必要な理由

ヘッダーエラー応答で必要な理由
X-Content-Type-Options: nosniffエラーページの本文が別の MIME 型(スクリプトなど)として解釈されるのを防ぐ。
Content-Security-Policy(HTML の場合)要求URLやパラメータを反映するエラーページでの XSS 被害を軽減する。
Cache-Control: no-store正常応答で機微情報のキャッシュを禁止しているサイトでは、エラー応答も同じ方針でないと、共有キャッシュに意図しない応答が残る。

OWASP ASVS 5.0.0 の V3.4.4 は「すべての HTTP 応答」に nosniff を含めることを求めており、エラー応答も例外ではありません。

エラー応答にだけセキュリティヘッダーが付かない原因

Apache:Header set は既定で成功応答のみ

Header set は既定で onsuccess テーブルを操作し、2xx 応答にしか付きません。mod_headers のドキュメントは、always テーブルのヘッダーはエラー時にも付き、ErrorDocument のような内部リダイレクトをまたいで保持されると説明しています。また、ローカルで生成された非2xx応答(リダイレクト等)では always テーブルだけが使われます。さらに always は既存ヘッダーに関して onsuccess の上位集合ではないため、両方を使い分ける場面があるとも注記されています。

nginx:add_header は特定の応答コードのみ、しかも継承されない

add_header は、応答コードが 200、201、204、206、301、302、303、304、307、308 の場合のみ付与し、always パラメータ(1.7.5以降)を指定すると応答コードに関係なく付与します。

加えて、現在の階層に add_header が1つでもあると、上位階層の add_header は継承されません(1.29.3 以降は add_header_inherit で変更可能)。location に1行追加しただけで他のヘッダーが消える事故が典型例です。

CDN・WAF:独自のエラーページ

オリジンの応答ではなく、CDN や WAF が独自に生成するエラーページ(ブロック画面、メンテナンス画面)には、ヘッダーが付かないことがあります。

no-store を一律には求めない理由

no-store について、ASVS V14.3.2 は「機微データがブラウザにキャッシュされないよう」十分なキャッシュ抑止ヘッダーを求めるもので、一般公開ページや公開サイトの404すべてに no-store を義務付けるものではありません。RFC 9110 でも 404 はヒューリスティックにキャッシュ可能とされています。

そのためこの項目は no-store を一律には要求せず、「正常応答が no-store を付けているのにエラー応答だけ付いていない」という不整合だけを指摘します。

よくある誤解

  • 「トップページの診断で A 評価だからエラーページも同じ」:設定方法によっては、エラー応答だけヘッダーが欠落します。実際に存在しないURLを取得して確認する必要があります。
  • 「エラーページは静的HTMLだから CSP は不要」:エラーページにも共通テンプレートのスクリプトや、URL を表示する処理が組み込まれていることが多くあります。

正常応答側のヘッダーそのものは「MIMEスニッフィング対策」や「Content Security Policy」、巡回した全応答への適用は「nosniffを全応答へ適用」で確認します。この項目は「エラー応答」に絞った確認です。

エラー応答のヘッダー整合性の診断基準(セキュリティAIチェッカー)

取得方法

  • 存在しないURL /miniparse-ver2-not-found-<タイムスタンプ> を GET で1回取得した応答(リダイレクト追従後の最終応答)のヘッダーを調べます。
  • 比較対象として、診断開始時に取得済みのトップページ応答の Cache-Control を参照します(追加通信なし)。

確認する3つのヘッダー

次の3点を確認し、不足しているものを列挙します。

チェック不足とみなす条件
nosniffX-Content-Type-Options の値(複数ある場合はカンマ区切りの先頭の値)が nosniff(大文字小文字不問)でない、または無い
CSPエラー応答の Content-Type が text/html で、Content-Security-Policy ヘッダーが無い(Report-Only や meta 要素は数えない)
no-storeトップページ応答の Cache-Control に no-store があるのに、エラー応答の Cache-Control に no-store が無い

判定条件

判定条件根拠表示(例)
○ 問題なし(pass)不足なしエラー応答にもnosniff・CSP(HTML時)があり、正常応答のno-store方針とも一致しています。
△ 要確認(warn)1つ以上不足エラー応答で不足: nosniff、CSP
× 要改善(fail)この項目では使用しない―
対象外(na)エラー応答を取得できないエラー応答を取得できませんでした。

×(fail)は使用しません。ヘッダーの欠落は直接の侵害ではなく、多層防御の穴であるためです。

得点への影響

重み2。○は2点、△は0.9点(2×0.45)。対象外は分母から除外します。

自治体サイトでの判定例

設定・挙動判定
.htaccess に Header set X-Content-Type-Options "nosniff"(always なし)と書かれ、404 は ErrorDocument の静的ページ△(エラー応答に nosniff・CSP が付かない)
Header always set … に修正済み、トップページは Cache-Control: max-age=600no-store は比較対象外。nosniff・CSP があれば ○
トップページが no-store、404 は CDN の既定エラーページで Cache-Control: max-age=…△(no-store 不整合)

同じ存在しないURLの応答は、「存在しないURLのステータス」と「エラー応答のデバッグ情報」の判定にも使います。

エラー応答のヘッダーが欠けるとどんなリスクがあるか

攻撃シナリオ(エラーページ経由の反射型XSS)

  1. 攻撃者が https://www.example.lg.jp/<script>…</script> のような URL を用意する。
  2. エラーページが要求パスをエスケープ不十分なまま表示し、かつエラー応答には CSP が無いため、スクリプトが実行される(反射型 XSS)。
  3. 正常ページの CSP で防げていた攻撃が、エラーページ経由で成立する。

nosniff が無い場合、攻撃者が制御できる文字列を含むエラー応答が <script src> 等で読み込まれた際に、ブラウザの MIME 判定によってスクリプトとして解釈される余地が生じます。

想定される被害

  • エラーページ経由の XSS によるセッション窃取、偽フォーム表示
  • 防御ヘッダーの適用範囲の思い込みによる、診断・監査での指摘漏れ
  • キャッシュ方針の不整合による、意図しない応答の共有キャッシュ残留

エラー応答にもセキュリティヘッダーを付ける設定方法(Apache・nginx・IIS・Next.js)

Apache(.htaccess/httpd.conf)

<IfModule mod_headers.c>
  # always を付けて 4xx/5xx・ErrorDocument にも付与する
  Header always set X-Content-Type-Options "nosniff"
  Header always set Content-Security-Policy "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'"
  Header always set Referrer-Policy "strict-origin-when-cross-origin"
</IfModule>
ErrorDocument 404 /errors/404.html

CMS やアプリ(PHP 等)が同じヘッダーを出している場合、always と onsuccess の両テーブルに入って二重送信になることがあります。重複が問題になる場合は、mod_headers のドキュメントの例のように Header onsuccess unset <名前> と Header always set <名前> … を組み合わせます。

CSP は既存サイトのインラインスクリプトを壊さないよう、Content-Security-Policy-Report-Only で影響を確認してから強制に切り替えます。

nginx

server {
    add_header X-Content-Type-Options "nosniff" always;
    add_header Content-Security-Policy "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'" always;
    error_page 404 /errors/404.html;

    location /errors/ {
        internal;
        # この階層で add_header を書くと server の add_header は継承されない
        # 書く場合は必要なヘッダーをすべて再記述する
    }
}

共通ヘッダーを include /etc/nginx/snippets/security-headers.conf; にまとめ、add_header を持つ各 location で必ず include する運用にすると、継承漏れを防げます。

IIS(web.config)

<system.webServer>
  <httpProtocol>
    <customHeaders>
      <add name="X-Content-Type-Options" value="nosniff" />
      <add name="Content-Security-Policy" value="default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'" />
    </customHeaders>
  </httpProtocol>
</system.webServer>

customHeaders は通常エラー応答にも付与されますが、httpErrors の設定やモジュール構成によって異なる場合があるため、curl で404応答を確認してください。

Next.js(Vercel)

// next.config.ts
const securityHeaders = [
  { key: "X-Content-Type-Options", value: "nosniff" },
  { key: "Content-Security-Policy", value: "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'" },
];
export default {
  async headers() {
    return [{ source: "/:path*", headers: securityHeaders }];
  },
};

存在しないURL(not-found の描画)にも付与されているかを、デプロイ後に curl -I で確認します。

キャッシュ方針(no-store)の揃え方

ログイン後の画面など no-store が必要な領域では、同じ領域のエラー応答にも no-store を付けます。

<IfModule mod_headers.c>
  <If "%{REQUEST_URI} =~ m#^/member/#">
    Header always set Cache-Control "no-store"
  </If>
</IfModule>

一般公開ページ全体に no-store を付けると、CDN・ブラウザキャッシュが効かなくなり、表示速度やサーバー負荷に影響します。必要な範囲に限定してください。

サイトを壊さないためのガードレール

  • Header always set へ変更すると、リダイレクト応答(301/302)にもヘッダーが付きます。通常は問題ありませんが、CSP の frame-ancestors などで外部サイトへの埋め込みを許可している場合は、値を確認します。
  • 共有ホスティングで <If> が使えない、mod_headers が無い場合に備え、<IfModule> で囲み、500エラーを避けます。

設定後の確認方法

# 存在しないURLの応答ヘッダーを確認
curl -sI https://www.example.lg.jp/no-such-page-$(date +%s) | grep -Ei "^(HTTP/|x-content-type-options|content-security-policy|cache-control|content-type)"
# 正常応答と比較
curl -sI https://www.example.lg.jp/ | grep -Ei "^(x-content-type-options|content-security-policy|cache-control)"

よくある質問(FAQ)

トップページにはセキュリティヘッダーが付いているのに、404ページに付かないのはなぜですか?

Apacheの Header set は既定で成功応答にしか付かず、nginxの add_header も always を付けないと200・3xxなど特定の応答コードにしか付かないためです。CDNやWAFが独自に生成するエラーページにもヘッダーが付かないことがあります。

Apacheでエラー応答にもヘッダーを付けるにはどうすればよいですか?

Header always set X-Content-Type-Options "nosniff" のように always を付けます。mod_headersのドキュメントによれば、always テーブルのヘッダーはエラー時にも付き、ErrorDocumentのような内部リダイレクトをまたいで保持されます。

nginxで location に add_header を1行追加したら、他のヘッダーが消えました。なぜですか?

nginxでは、現在の階層に add_header が1つでもあると、上位階層の add_header は継承されないためです。必要なヘッダーをすべて再記述するか、共通ヘッダーをinclude用のファイルにまとめて各locationで読み込みます(1.29.3以降は add_header_inherit で変更可能)。

公開サイトの404ページにもno-storeを付ける必要がありますか?

一律には必要ありません。ASVS V14.3.2は機微データのキャッシュ防止を求めるもので、RFC 9110でも404はヒューリスティックにキャッシュ可能です。セキュリティAIチェッカーは、正常応答がno-storeなのにエラー応答だけ付いていない不整合だけを△にします。

エラーページは静的HTMLなのにCSPが必要ですか?

必要です。エラーページにも共通テンプレートのスクリプトや、要求URLを表示する処理が組み込まれていることが多く、エスケープ不十分な表示があると反射型XSSの経路になります。CSPはその被害を軽減します。

meta要素やReport-OnlyのCSPでも合格になりますか?

なりません。この項目では、エラー応答の Content-Security-Policy レスポンスヘッダーだけを数え、Content-Security-Policy-Report-Only やmeta要素は数えません。

セキュリティAIチェッカーを利用する

この項目を含む127項目を、URLを入れるだけで無料診断します。

参考資料