16
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Webサイトに最低限入れるべきHTTPセキュリティヘッダ7種と設定例(nginx / Apache / WordPress対応・コピペOK)

16
Last updated at Posted at 2026-06-05

はじめに

「とりあえずHTTPS化はしたけれど、セキュリティヘッダは手つかず」というサイトは少なくありません。HTTPレスポンスヘッダを数行足すだけで、クリックジャッキング・MIMEスニッフィング・中間者攻撃・情報漏えいといった典型的なリスクをまとめて下げられます。

この記事では、Webサイトに最低限入れておきたいセキュリティヘッダ7種を、役割・推奨値・設定するとどこが壊れやすいかまで含めて整理します。nginx / Apache(.htaccess) / WordPress のコピペ用設定と、設定後の確認方法までワンセットでまとめました。対象は自社サイトやホームページを運用・制作する初〜中級エンジニア/Web担当者です。

値はあくまで「多くのサイトで安全側に倒した推奨値」です。サイトの構成(外部CDN・埋め込み・サブドメイン構成など)によって最適値は変わります。本番反映前に必ず検証環境やReport-Onlyで確認してください。


まず全体像:今回設定する7つのヘッダ

ヘッダ 役割(何を防ぐか) 本記事の推奨値(出発点)
Strict-Transport-Security 常にHTTPS接続を強制(中間者攻撃・ダウングレード対策) max-age=31536000; includeSubDomains
Content-Security-Policy XSS・リソース読み込み制御(最重要かつ最も繊細) まず Report-Only で検証
X-Content-Type-Options MIMEタイプの推測実行を禁止 nosniff
X-Frame-Options iframe埋め込みによるクリックジャッキング対策 SAMEORIGIN
Referrer-Policy リファラからの情報漏えいを抑制 strict-origin-when-cross-origin
Permissions-Policy カメラ・位置情報など強力なAPIの利用制限 geolocation=(), camera=(), microphone=()
Cross-Origin-Resource-Policy 自サイトのリソースを他オリジンから読ませない same-origin

以降、1つずつ意味と注意点を見ていきます。設定例だけ欲しい方は「環境別 設定例」へどうぞ。


各ヘッダの詳細と推奨値

1. Strict-Transport-Security(HSTS)

一度アクセスしたブラウザに「次回以降このドメインは必ずHTTPSで接続せよ」と覚えさせるヘッダです。HTTPへのダウングレードや、初回以降の中間者攻撃を防ぎます。

Strict-Transport-Security: max-age=31536000; includeSubDomains
  • max-age は秒数。31536000 は1年。
  • includeSubDomains全サブドメインも完全にHTTPS化している場合のみ付けます。HTTPでしか動かないサブドメインがあると、それらにアクセスできなくなります。
  • preload(ブラウザに事前組み込みさせるオプション)は強力ですが、一度有効化すると解除に時間がかかる片道切符です。サイトが恒久的にHTTPSで運用できる確信が持てるまで付けないのが無難です。
  • 注意: サイトが完全にHTTPS化される前にHSTSを付けると、HTTPでしか配信していないページに自分自身がアクセスできなくなります。HTTPS化が完了してから設定してください。

2. Content-Security-Policy(CSP)

読み込めるスクリプト・スタイル・画像などの「出どころ」を制限し、XSSの被害を大幅に抑える最重要ヘッダです。一方で、最も既存サイトを壊しやすいヘッダでもあります。

いきなり本番に強いポリシーを入れると、インラインスクリプト・外部CDN・広告・解析タグなどが軒並みブロックされて表示が崩れます。そこでまず Content-Security-Policy-Report-Only で「ブロックせず違反だけ記録」して実態を把握してから、本番ヘッダに切り替えるのが定石です。

# ステップ1:まずはレポートのみ(サイトは壊れない)
Content-Security-Policy-Report-Only: default-src 'self'

# ステップ2:違反を潰しきってから本番ポリシーへ(例)
Content-Security-Policy: default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'
  • object-src 'none'base-uri 'self' はほぼ全サイトで安全に付けられる効果の高い指定です。
  • frame-ancestors 'self' は X-Frame-Options の上位互換(後述)。
  • 'unsafe-inline' を style-src に付けているのは、多くのテーマ/プラグインがインラインCSSを使うための妥協です。本来は外したいので、可能ならnonce/hash方式への移行を検討してください。
  • WordPressでの現実: テーマやプラグインがインラインスクリプトを多用するため、script-src 'self' だけで動くサイトは多くありません。WPで厳格なCSPを完成させるのは難易度が高く、Report-Onlyでの地道な調整が前提になります。

3. X-Content-Type-Options

ブラウザがContent-Typeを無視してMIMEタイプを「推測」する挙動を止めます。これにより、画像やテキストに偽装したスクリプトの実行などを防げます。値は1つだけで副作用もほぼありません。全サイト即入れて問題ないヘッダです。

X-Content-Type-Options: nosniff

4. X-Frame-Options

自サイトを他サイトの<iframe>に埋め込ませないことで、透明なiframeを重ねてクリックを盗む「クリックジャッキング」を防ぎます。

X-Frame-Options: SAMEORIGIN
  • DENY(どこにも埋め込ませない)/ SAMEORIGIN(同一オリジンのみ許可)が有効値です。
  • かつての ALLOW-FROM は非推奨で、主要ブラウザでサポートされていません。特定の許可先を指定したい場合は CSP の frame-ancestors を使います。
  • 自サイトを意図的に他社サイトへ埋め込ませている(決済ウィジェット等)場合は、埋め込みが壊れないか確認してください。

5. Referrer-Policy

別サイトへ遷移した際に送られるRefererヘッダの中身を制御し、URLに含まれるパスやクエリ(セッションIDや内部パス等)の漏えいを抑えます。

Referrer-Policy: strict-origin-when-cross-origin
  • strict-origin-when-cross-origin は近年の主要ブラウザの既定値でもあり、実用とプライバシーのバランスが良い設定です。
  • もっと厳しくするなら same-originno-referrer。ただしリファラを使った解析や、外部サービスのリファラ判定が効かなくなる場合があるので影響を確認してください。

6. Permissions-Policy

位置情報・カメラ・マイク・決済APIなど、強力なブラウザ機能を「どのオリジンに許可するか」を宣言します。使っていない機能を明示的に無効化しておくと、万一スクリプトが混入しても悪用範囲を狭められます。

Permissions-Policy: geolocation=(), camera=(), microphone=()
  • 空のかっこ () は「どのオリジンにも許可しない(=完全無効)」の意味です。
  • Feature-Policy の後継で構文が異なります。古い記事の Feature-Policy 構文をそのまま流用しないよう注意してください。
  • 自サイトで位置情報やカメラを使う機能がある場合は、その機能だけ geolocation=(self) のように許可します。
  • ディレクティブごとにブラウザ対応状況が異なるため、未対応の指定は単に無視されます(壊れはしません)。

7. Cross-Origin-Resource-Policy(CORP)

自サイトのリソース(画像・スクリプト等)を、他オリジンのページから読み込ませないようにします。リソースの不正な巻き込み(Spectre系のサイドチャネル等)対策の一つです。

Cross-Origin-Resource-Policy: same-origin
  • same-origin / same-site / cross-origin があります。
  • 注意: 画像や静的ファイルを他サイト・別サブドメインから正規に読み込ませている場合、same-origin だと読めなくなります。配信用CDNや別ドメインから取得している構成では same-sitecross-origin を検討してください。

補足(発展): Cross-Origin-Opener-Policy(COOP)と Cross-Origin-Embedder-Policy(COEP)を組み合わせると「クロスオリジン分離」が有効になり、SharedArrayBuffer 等が使えます。ただし外部埋め込みが広範に壊れやすいため、必要になってから慎重に導入する上級者向けの設定です。最低限の7種には含めていません。


環境別 設定例(コピペ用)

いずれも反映前に検証環境で動作確認を。CSPだけは本番でもまず Report-Only から始めるのを強く推奨します。

nginx

server { ... } ブロック内に記述します。

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Content-Security-Policy "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), camera=(), microphone=()" always;
add_header Cross-Origin-Resource-Policy "same-origin" always;

反映:

nginx -t && systemctl reload nginx
  • 末尾の always を付けると、4xx/5xxなどのエラー応答にもヘッダが付きます(付けないと200系などにしか付きません)。
  • 最大のハマりどころ: nginxの add_header は、location ブロック内に別の add_header が1つでもあると、その location では上位(server)の add_header がすべて無効化されます(継承されない)。特定パスでヘッダが消える場合はこれが原因のことが多いです。共通ヘッダを include ファイルにまとめて各 location で読み込む、などの対策を検討してください。

Apache(.htaccess)

mod_headers が必要です。

<IfModule mod_headers.c>
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
    Header always set Content-Security-Policy "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'"
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
    Header always set Permissions-Policy "geolocation=(), camera=(), microphone=()"
    Header always set Cross-Origin-Resource-Policy "same-origin"
</IfModule>

事前にモジュール有効化(Debian/Ubuntu系の例):

a2enmod headers && systemctl restart apache2
  • .htaccess で効かせるにはサーバ側で AllowOverride が許可されている必要があります。許可されていない共有レンタルサーバでは、コントロールパネルからの設定になる場合があります。
  • Header setHeader always set の違いは、後者がエラー応答にもヘッダを付ける点です。基本は always を推奨します。

WordPress(テーマの functions.php)

サーバ設定に触れない場合の手段です。send_headers フックで付与します。子テーマfunctions.php に書くのが安全です。

add_action('send_headers', function () {
    // 管理画面はCSP等で壊れやすいのでフロント側のみに付与する例
    if (is_admin()) {
        return;
    }
    header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
    header('X-Content-Type-Options: nosniff');
    header('X-Frame-Options: SAMEORIGIN');
    header('Referrer-Policy: strict-origin-when-cross-origin');
    header('Permissions-Policy: geolocation=(), camera=(), microphone=()');
    header('Cross-Origin-Resource-Policy: same-origin');

    // CSPはまずReport-Onlyで検証することを推奨
    header("Content-Security-Policy-Report-Only: default-src 'self'");
});
  • 重要な制約: この方法はPHPを通るリクエスト(主にHTML)にしか効きません。Webサーバが直接返す静的ファイル(画像・CSS・JS等)にはヘッダが付きません。可能であればサーバ(nginx/Apache)側で設定するほうが網羅的で確実です。
  • キャッシュ系プラグインに注意: ページキャッシュが効いていると、PHPを通らずキャッシュHTMLが返ってPHPの header() が実行されない場合があります。
  • プラグインで管理したい場合は、レスポンスヘッダ設定に対応したセキュリティ系プラグイン(例: 各種セキュリティ強化プラグインやヘッダ専用プラグイン)も選択肢です。導入後は後述の方法で実際に付与されているか必ず確認してください。

もう使わないほうがよい「レガシーヘッダ」

古い記事を参考にすると、今は非推奨/廃止のヘッダを足してしまいがちです。次のものは新規に追加しないのが現在のセオリーです。

ヘッダ 現状 対応
X-XSS-Protection 主要ブラウザのXSSオーディター機能が廃止済み。むしろ脆弱性を生む挙動が知られている 付けないか、付けるなら X-XSS-Protection: 0。XSS対策はCSPで行う
Expect-CT Chromeで廃止。役割を終えた 付けない
Public-Key-Pins(HPKP) 運用事故が多く廃止 付けない
Feature-Policy Permissions-Policy に置き換わった Permissions-Policy を使う(構文が違う点に注意)

設定後の確認方法

設定したつもりでも、キャッシュやnginxの継承問題で「実は付いていない」ことはよくあります。反映後は必ず外形から確認しましょう。

まずは手元で curl で生のヘッダを見るのが確実です。

curl -sI https://example.com | grep -iE 'strict-transport|content-security|x-content-type|x-frame|referrer-policy|permissions-policy|cross-origin'

ブラウザのDevTools(Network → 対象リクエスト → Headers)でも同様に確認できます。

スコア化・抜け漏れチェックには無料ツールが便利です。それぞれ評価軸が違うので、複数併用すると把握しやすくなります。

  • securityheaders.com:URLを入れるとヘッダの有無をグレード表示。手軽さNo.1。
  • Mozilla Observatory:ヘッダに加え総合的なWeb設定を採点。改善提案が具体的。
  • Google Lighthouse(Chrome DevTools内蔵 / PageSpeed Insights):「Best Practices」にセキュリティ関連の指摘が出る。
  • OWASP ZAP:ローカルで動かせるOSSスキャナ。ヘッダだけでなく受動的な脆弱性検査まで踏み込みたいとき。
  • サイトドック(sitedock.jp):URLを入れると登録不要で、ヘッダ・Cookie属性・SPF/DMARCなどをパッシブに健診してスコア化する無料サービス(対象に負荷をかけるポートスキャンや能動的な脆弱性検査は、自ドメインの所有者確認を済ませた場合のみ)。ヘッダの抜け漏れを第三者視点でまとめて確認したいときの手段の一つとして。

ツールごとに減点基準が微妙に異なるため、「あるツールで満点でない=危険」とは限りません。指摘内容を見て、自サイトの構成に照らして取捨選択するのが現実的です。


まとめ

  • セキュリティヘッダは数行のコピペで効果が出る、費用対効果の高い対策。
  • 副作用がほぼないもの(X-Content-Type-Options: nosniffX-Frame-Options: SAMEORIGINReferrer-PolicyPermissions-Policy)はまず即入れてよい。
  • CSPは別格。いきなり本番に入れず、Content-Security-Policy-Report-Only で違反を観測してから段階導入する。WordPressでは特に時間がかかる前提で。
  • HSTS は完全HTTPS化後に。includeSubDomainspreload は影響範囲を理解してから。
  • nginxは add_header継承されない仕様、WordPressの send_headers静的ファイルに効かない/キャッシュで実行されないという落とし穴に注意。
  • 古い X-XSS-Protection / Expect-CT / HPKP / Feature-Policy は新規追加しない。
  • 反映後は curl -I と securityheaders.com / Mozilla Observatory / Lighthouse / OWASP ZAP などで実際に付いているかを必ず確認する。

想定タグ: Security nginx Apache WordPress CSP

想定読了時間: 約8〜10分

難易度: 初〜中級(CSPの実運用のみ中級+)


関連記事


お知らせ — バーチャルセキュリティコンシェルジュ「一ノ瀬あかり」がデビューしました🎉

JIISセキュリティラボから、セキュリティ用語をやさしく動画で解説する案内役「一ノ瀬あかり」が登場しました。よければデビューのご挨拶もご覧ください。

▶ デビュー動画: https://www.youtube.com/watch?v=DUfmTCrnjUY

16
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?