セキュリティヘッダーとは?一覧と付け方、クリックジャッキングを防ぐ frame-ancestors

PR
セキュリティヘッダーとは?一覧と付け方、クリックジャッキングを防ぐ frame-ancestors
この記事は約35分で読めます。

セキュリティヘッダーは、サーバーがブラウザーに返す応答に付ける、セキュリティのためのヘッダー(応答に添える付け足しの情報)のことです。OWASP(Web アプリのセキュリティの資料を出している団体)は、HTTP ヘッダーは手軽に入れられて、Web のセキュリティを大きく上げると書いています。正しい応答ヘッダーが、XSS・クリックジャッキング・情報の漏れなどを防ぐ助けになる、という説明です。

この記事がいちばん細かく扱うのは、クリックジャッキング(見えない iframe を重ねて、利用者に別のサイトの操作をさせる攻撃)の防ぎ方です。MDN(Mozilla が運営する Web 技術の資料)によると、守りの道具は CSP の frame-ancestors と X-Frame-Options の二つで、frame-ancestors は X-Frame-Options の後継です。

この記事は、Web サイトやアプリを公開する初心者〜若手の人に向けて、主なセキュリティヘッダーを一覧で引ける形にまとめ、クリックジャッキングの防ぎ方、Cloudflare Pages での付け方、手元のサーバーで付けて点検する方法を説明します。版や日付が動く話には「2026年10月21日時点」と、出どころを添えています。セキュリティ全体の点検は、Web 開発のセキュリティの基本にまとめています。

この記事でわかること
  • 主なセキュリティヘッダー(HSTS・CSP・X-Frame-Options・X-Content-Type-Options・Referrer-Policy・Permissions-Policy ほか)が、それぞれ何のためのものか。付けないほうがよいヘッダー(X-XSS-Protection ほか)
  • クリックジャッキングの仕組みと、IPA・MDN・OWASP の防ぎ方。ヘッダーでは防げない型もあること
  • frame-ancestors と X-Frame-Options の書き方と違い、ALLOW-FROM を使わない理由
  • Cloudflare Pages の _headers の書き方と、Pages Functions の応答には効かないこと
  • 手元のサーバーでヘッダーを付けて点検する方法(ブラウザーでの効き目は、この記事では見ていません)

セキュリティヘッダーとは? 主なヘッダーの一覧(2026年10月時点)

まず言葉を整理します。ヘッダーは、ブラウザーとサーバーがやり取りするとき、HTML などの中身とは別に、名前: 値 の形で添える情報です。ブラウザーが頼むときに付けるものを「リクエストヘッダー」、サーバーが返すときに付けるものを「応答ヘッダー」といいます。セキュリティヘッダーは、そのうちセキュリティのために、サーバーが応答に付けるものです。

次の表は、OWASP の「HTTP Security Response Headers Cheat Sheet」が推す値の例を、ヘッダーごとに並べたものです(2026年10月21日に開いた版。版や日付の表示は見つかりませんでした)。

ヘッダー一言で言うとOWASP の例の値くわしく
Strict-Transport-Security(HSTS)このホストは HTTPS だけで開く、とブラウザーに伝える。HTTPS の応答で送ったときだけ効くmax-age=63072000; includeSubDomains; preload(そのまま写さない。表の下の注意)HTTPS と証明書の基本
Content-Security-Policy(CSP)ページが読み込んでよい資源を、サイトの管理者が決める。XSS への守り。frame-ancestors もこの一部OWASP は別のチートシートで説明しているXSS と CSRF
X-Frame-Optionsページを <frame>・<iframe> などに描いてよいかを示す。クリックジャッキング対策DENY(できれば CSP の frame-ancestors)この記事の下の節
X-Content-Type-OptionsContent-Type で示したファイルの種類を守らせ、ブラウザーに中身から推測させないnosniffこの記事の下の節
Referrer-Policyリクエストに付ける参照元(Referer ヘッダー)の情報を、どこまで含めるか決めるstrict-origin-when-cross-originこの記事の下の節
Permissions-Policyページとその中の iframe で、ブラウザーの機能を使わせる・使わせないを決めるgeolocation=(), camera=(), microphone=()この記事の下の節
Cross-Origin-Opener-Policy(COOP)Window.open() や移動で開いた新しい最上位の文書を、開いた側と同じグループに入れるか、分けるかsame-originこの記事の下の節
Cross-Origin-Embedder-Policy(COEP)COOP・CORP と組で働く(OWASP)require-corp(正しく設定していない別オリジンの資源は読めなくなる)──
Cross-Origin-Resource-Policy(CORP)no-cors の別オリジン・別サイトからのリクエストを、ブラウザーに止めさせるsame-siteこの記事の下の節
Cache-Controlキャッシュ(一時的な保存)の指示。機微な応答(個人の情報など)は no-storeno-store(機微な応答)この記事の下の節

表を読むときの注意が3つあります。

  • 値は OWASP の例です。 資料によって勧める値が違うものがあります。HSTS の max-age は、OWASP が 2 年(63072000 秒)を推す一方、preload(ブラウザーに最初から入れておくリスト)に載せる条件は 1 年(31536000 秒)以上と includeSubDomains です(MDN)。Referrer-Policy は、OWASP が strict-origin-when-cross-origin、Cloudflare の文書の例が no-referrer です。どちらかが誤り、という話ではなく、資料の言い方の違いです
  • HSTS は「よく読んでから」です。 OWASP は、長い期間にしたまま証明書が切れたり失効したりすると、その期間が過ぎるまで正しい利用者が開けなくなりうる、と注意しています。includeSubDomains を付けると、いまと将来のサブドメインもすべて HTTPS だけになり、HTTP でしか開けないページやサブドメインは開けなくなります(MDN)。preload について、OWASP の HSTS のチートシートは、取り返しのつかない結果になりうると書いています。preload のリストを扱う hstspreload.org は、リストから外すのに数か月かかるとしています。hstspreload.org は、max-age を 300 秒から始めて、段階的に延ばすよう勧めています。表の OWASP の値を、そのまま写さないでください。HSTS の基本はHTTPS と証明書の基本の HSTS の節です
  • 応答が何を返すかで、要るものが変わります。 OWASP は、CSP は描かれない内容を返す REST API の応答では意味が無いかもしれない、X-Frame-Options もリダイレクトや JSON を返す API の応答では守りにならない、と書いています。付ける前に、その応答がページなのか API なのかを見ます

付けないもの・消すもの:X-XSS-Protection ほか

古い手引きや記事に出てくる名前の中には、いまは付けないほうがよいものがあります。

名前資料の言い方
X-XSS-ProtectionMDN: 非推奨(Deprecated)で非標準(Non-standard)。安全なサイトに XSS の穴を作ることもあるので、XSS のフィルターではなく CSP を使う。OWASP: 付けないか、はっきり切る(X-XSS-Protection: 0)。IPA の手引き(2021 年の刷り)は 1; mode=block を挙げていますが、いまの MDN・OWASP とは言い方が違います
Expect-CTOWASP: 使わない。Mozilla も避けるよう勧めている
Public-Key-Pins(HPKP)OWASP: Chromium から 2018 年に外され、いまのブラウザーはどれも対応していない。本番から消し、Certificate Transparency と CAA に頼る
ServerOWASP: 消すか、中身の無い値にする。ただし攻撃者はほかの方法でもサーバーの技術を調べられる
X-Powered-ByOWASP: すべて消す。この情報があると、攻撃者が脆弱性を見つけやすくなる

X-XSS-Protection と CSP の関係は、XSS と CSRFの「XSS を重ねて守る:CSP・HttpOnly(X-XSS-Protection は使わない)」の節にも書いています。

クリックジャッキングとは? 仕組みと防ぎ方

IPA(情報処理推進機構)の「安全なウェブサイトの作り方」(改訂第7版、2021年3月 第4刷)の 1.9 が、クリックジャッキングです。IPA は、ログインした人だけが使える機能がマウス操作だけで使える場合、細工された外部のサイトを見て操作すると、利用者が誤操作して意図しない機能を実行させられる可能性がある、と説明しています。この問題を「クリックジャッキングの脆弱性」、悪用する攻撃を「クリックジャッキング攻撃」と呼びます。

仕組み:見えない iframe を重ねる

iframe は、ページの中に別のページをはめ込む HTML の部品です。IPA の図の説明では、罠ページの仕組みは次のとおりです。

  1. 罠ページの上に、サイトAを iframe で重ね合わせ、見た目を透明にする
  2. ブラウザーで見ると、罠ページだけが表示されているように見える
  3. 罠ページのコンテンツをクリックしたつもりが、本当はサイトAのコンテンツをクリックしてしまう

MDN も同じ筋で説明しています。攻撃者は、おとりのサイトに目当てのサイトを <iframe> で埋めて隠し、大事な操作のボタンと同じ位置に、おとりの要素を置きます。利用者がそのサイトにログインしたままなら、操作のリクエストには本物の資格情報が付くので、リクエストは通ってしまいます。

動く罠ページの HTML や CSS は、この記事には載せません。仕組みは、上の IPA と MDN の説明で足ります。

何が起きる? 特に注意が要るサイト

IPA が挙げる「発生しうる脅威」は、マウス操作だけでできる処理に限られる点のほかは、CSRF(ログイン中の利用者のブラウザーから、別のサイトがリクエストを出させる攻撃。XSS と CSRFで説明しています)と同じです。例は次のとおりです。

  • ログイン後のサービスの悪用(意図しない情報発信、意図しない退会処理など)
  • ログイン後の設定の変更(利用者情報の公開範囲の意図しない変更など)

IPA が「注意が必要」とするのは、ログイン後の機能をマウス操作だけで実行できるサイトです。公開範囲の変更のような処理なら、被害が大きくなるので、特に注意が要ります。

IPA は、対策を実施した場合の「副作用」にも触れ、情報セキュリティポリシーや副作用を踏まえて、対策をするかどうかを検討するよう書いています。ただし、その副作用の中身は、1.9 の中に見つけられませんでした。この記事では、副作用の中身は書きません。

防ぎ方:IPA・MDN・OWASP の言い方

3つの資料が挙げる守りを並べます。

資料挙げている守り
IPA 9-(i)-a(根本的解決)応答ヘッダーに X-Frame-Options を出し、他ドメインのサイトからの frame 要素・iframe 要素による読み込みを制限する
IPA 9-(i)-b(根本的解決)処理を実行する直前のページで、もう一度パスワードを入れさせ、正しいときだけ実行する。画面の仕様の変更が要る対策なので、実装の変更だけで直すなら 9-(i)-a
IPA 9-(ii)(保険的対策)重要な処理は、一連の操作をマウスだけで実行できないようにする。キーボード操作を挟むと、攻撃の成功率を下げられる
MDN主な守りは、埋め込みを禁じる(または絞る)こと。道具は CSP の frame-ancestors と X-Frame-Options。追加の一部の守りとして、セッションの Cookie の SameSite を Lax か Strict にする
OWASP守りは3つ。(1) X-Frame-Options か CSP の frame-ancestors で、フレームでの読み込みを止める。(2) SameSite で、別のサイトの iframe にセッションの Cookie を付けない。(3) ページの JavaScript で、フレームに入るのを防ぐ(frame-buster と呼ばれる)

OWASP は、この3つは互いに独立なので、できれば重ねて入れる、と書いています。MDN の点検表も、frame-ancestors と X-Frame-Options を付けることと、セッションの Cookie を SameSite=Lax か Strict にすることを並べています。SameSite の値の説明は、XSS と CSRFの「SameSite は、代わりにならない」の所にあります。frame-buster のスクリプトは、この記事には載せません。

SameSite だけに頼れない理由も、OWASP が書いています。

  • SameSite は、フレームに入れること自体は止めません。同じサイトからのリクエストには Cookie が付きます。同じサイトでありながら別のオリジンになりうる例が、攻撃者が握った兄弟のサブドメインです。だから frame-ancestors で、埋め込んでよいオリジンを絞ります
  • ログインが要らない種類のクリックジャッキングには、SameSite は効きません

ヘッダーでは防げない型:DoubleClickjacking

OWASP は、ヘッダーでは防げない型として、DoubleClickjacking を挙げています。別の窓で、利用者のダブルクリックの間に、大事なボタンを押させる攻撃です。目当てのページは最上位の窓なので、X-Frame-Options や frame-ancestors のような、フレームに入れることの制限では防げません。

OWASP の対策は、リスクの高い操作では、操作の内容を利用者に確かめさせ、取引の認可を完了させることです。認可はサーバー側で行います。確認のボタンを足すだけでは、完全な守りにならない、とも書いています。この型は、手元では確かめていません。

frame-ancestors と X-Frame-Options は何が違う? 書き方と付け方

結論から書きます。MDN は、frame-ancestors は X-Frame-Options の後継だと説明しています。OWASP も、できれば frame-ancestors を使い、X-Frame-Options を付けるなら DENY にする、と書きます。MDN は、両方を付ければ frame-ancestors に対応しないブラウザーでも止められるが、frame-ancestors の対応はとても広いので、大きな心配ではない、とも書いています。

どちらも、MDN では Baseline の "Widely available"(広く使える)です。frame-ancestors は 2018 年 1 月から、X-Frame-Options は 2015 年 7 月からです。

frame-ancestors の書き方

frame-ancestors は CSP の指示(ディレクティブ)の1つです。MDN によると、そのページを <frame>・<iframe>・<object>・<embed> で埋め込んでよい「親」を決めます。CSP の中では、移動先やフォームの送り先を決める「ナビゲーションの指示」の仲間です。

オリジン(https://example.com のように、https とホスト名、ポートがあればそれも合わせた組)で見たとき、どこから埋め込ませるかを書きます。

書き方意味
Content-Security-Policy: frame-ancestors 'none';どこにも埋め込ませない。単一引用符は必須。MDN は X-Frame-Options: deny に近いと書く。OWASP は、埋め込みの必要が見つかっていなければ、この設定を勧める
Content-Security-Policy: frame-ancestors 'self';自分のサイトだけが埋め込める(OWASP)
Content-Security-Policy: frame-ancestors 'self' https://www.example.org;自分と https://www.example.org が埋め込める(MDN の例)。値は空白で区切って並べる
https://*.somesite.comワイルドカード。サブドメインに合い、somesite.com そのものには合わない。必要ならそのホストも書き足す(OWASP)

引用符の決まりも、OWASP が書いています。'self' と 'none' には単一引用符が要り、ほかの値には付けません。

frame-ancestors で間違えやすいこと3つ

  • default-src に頼れません。 MDN によると、frame-ancestors は default-src にフォールバック(代わりに使われること)しません。default-src 'none' と書いた方針でも、frame-ancestors を書かなければ、だれでも埋め込めます
  • <meta> では効きません。 MDN は、この指示は <meta> 要素では使えないと書き、OWASP も、応答ヘッダーで付けるものだと書いています
  • frame-src とは向きが逆です。 frame-ancestors は「だれが自分を埋め込めるか」、frame-src は「自分のページの iframe に何を読み込めるか」を決めます。入れ子のフレームでは、祖先を1つずつ確かめ、1つでも合わなければ読み込みを取り消します(MDN)

X-Frame-Options の書き方と限界

MDN によると、X-Frame-Options は、ページを <frame>・<iframe>・<embed>・<object> に描いてよいかをブラウザーに示す応答ヘッダーです。値は2つです。

値意味(MDN)
DENYオリジンを問わず、どのフレームにも入れない。同じオリジンからの埋め込みも止まる
SAMEORIGIN祖先のフレームがすべて、ページと同じオリジンのときだけ入れる
X-Frame-Options: DENY

このヘッダーを送らず、frame-ancestors などの別の仕組みもなければ、ブラウザーは他のサイトの埋め込みを許します(MDN)。<meta http-equiv="X-Frame-Options"> で書いても効きません。HTTP ヘッダーでだけ効きます(MDN)。

OWASP は、X-Frame-Options の限界を次のように挙げています。

  • 許すドメインを並べられません。ブラウザーはヘッダーをひとつ、値もひとつしか見ないので、自分のサイトとほかのサイト1つを同時に許すことはできません
  • frame-ancestors に置き換えられました
  • ウェブプロキシ(途中で通信を中継するもの)がヘッダーを消すと、守りが無くなります
  • ページごとに付ける必要があります。付ける先は、HTML を返すすべての応答です。リダイレクトや JSON を返す API の応答では、守りになりません

ALLOW-FROM は使わない:IPA と MDN の言い方の違い

X-Frame-Options には、かつて ALLOW-FROM(指定したオリジンだけ許す)という値がありました。資料の言い方は割れています。

  • IPA の手引き(2021 年の刷り)。 値の表に ALLOW-FROM を載せ、「ブラウザによって適切に動作しない場合があります」と添えています
  • いまの MDN。 ALLOW-FROM は obsolete(廃止された)で、いまのブラウザーは、この値を見るとヘッダーごと無視する。代わりに frame-ancestors を使う、と書いています
  • OWASP。 いまのブラウザーでは動かない。これに頼って、ブラウザーが対応していなければ、クリックジャッキングの守りがまったく無くなる、と注意しています

見た時期も違います。IPA の手引きは 2021 年の刷り、MDN と OWASP は 2026年10月21日に開いた版です。この記事では、ALLOW-FROM は使わず、特定のサイトにだけ許したいときは frame-ancestors で書く、と整理します。

両方付けるなら、同じ意味にそろえる

資料によって、根本の対策に挙げるヘッダーが違います。

資料挙げているヘッダー
IPA 9-(i)-aX-Frame-Options だけ(frame-ancestors という名前は 1.9 に出てこない)
MDNframe-ancestors を後継とし、点検表では両方を付けるよう書く
OWASPできれば frame-ancestors。X-Frame-Options なら DENY

両方付けたとき、MDN は、frame-ancestors に対応するブラウザーは X-Frame-Options を無視する、と書きます。OWASP の frame-ancestors の限界の所は、"X-Frame-Options takes priority"(X-Frame-Options が優先)という書き出しで、CSP の仕様は X-Frame-Options を無視するよう書いているが、古いブラウザー(Chrome 40・Firefox 35 など)は X-Frame-Options に従った、という内容です。いまのブラウザーについては同じことを言っていて、OWASP は古いブラウザーの話を足している形です。

どちらが勝つかで迷わないように、両方付けるときは、同じ意味の値にそろえておくのが安全です(この記事の整理です)。たとえば frame-ancestors 'none' と X-Frame-Options: DENY で、MDN は 'none' を X-Frame-Options: deny に近いと書いています。

nosniff・Referrer-Policy・Permissions-Policy・Cache-Control は何をする?

一覧に出てきた、ほかのヘッダーの要点です(MDN と OWASP の言い方に沿っています)。

X-Content-Type-Options: nosniff

MIME タイプは、ファイルの種類を表す名前です(text/html など)。X-Content-Type-Options: nosniff は、Content-Type で示した MIME タイプを守り、ブラウザーに中身から推測(sniffing)させないためのヘッダーです。値は nosniff だけです(MDN)。効き目は2つあります。

  • 行き先が script か style のリクエストは、MIME タイプが合わなければ、ブラウザーが止めます
  • ほかの応答は、Content-Type をそのまま使います。Content-Type: text/plain に nosniff が付いていれば、中に HTML があっても HTML として解釈しません。利用者が上げた内容が HTML として動く XSS を、防げます

OWASP は、nosniff と一緒に、サイト全体で Content-Type を正しく付けるよう書いています。HTML なら Content-Type: text/html; charset=UTF-8 で、charset は XSS を防ぐのに要ります。利用者が上げたファイルは、Content-Disposition: attachment(表示せずダウンロードさせる)で返し、種類が分からないものは application/octet-stream にします。アップロードの扱いは、ファイルのアップロードとパストラバーサルの記事でも説明します。

Referrer-Policy

MDN によると、Referrer-Policy は、リクエストに付ける参照元の情報(Referer ヘッダー)を、どこまで含めるかを決めます。Referer は "referrer" の綴りの誤りですが、Referrer-Policy の綴りは誤っていません。値は8つあり、指定が無いとき、また値が無効なときの既定は strict-origin-when-cross-origin です(2020年11月の仕様の改訂から)。同じオリジンへはオリジン・パス・クエリ文字列を送り、別のオリジンへは、通信の安全さが同じとき(MDN の例は HTTPS→HTTPS)に、オリジンだけを送ります。HTTPS から HTTP へは送りません。unsafe-url は、人に見せたくない情報が漏れうる、と MDN は注意しています。

OWASP は、既定に頼らず、ヘッダーで明示するよう勧めています。Cloudflare の文書の例は no-referrer です。

Permissions-Policy

MDN によると、Permissions-Policy は、文書とその中の <iframe> で、ブラウザーの機能を使わせる・使わせないを決めます。MDN は、このヘッダーを "Limited availability"(Baseline ではない)、"Experimental"(実験的な技術)と表示しています。 使う前に、ブラウザーの対応表を見る必要があります(この記事では、対応の中身は読んでいません)。

書き方は Permissions-Policy: <指示>=<許す相手> で、複数はカンマで区切ります。許す相手の () は空で、最上位でも iframe でも無効になります。MDN の例は、マイクと位置情報を止めるものです。

Permissions-Policy: microphone=(), geolocation=()

OWASP は、使わない機能はすべて止めるか、許すドメインだけに絞るよう書いています。XSS のような注入で、カメラやマイクを動かされないようにするためです。

COOP・CORP:短く

  • COOP。 新しく開いた最上位の文書を、開いた側と別のグループに分けて、互いの参照を切ります。XS-Leaks と呼ばれる一群の攻撃を防げます(MDN)。外部の OAuth や決済のように、開いた窓への参照を保つ必要があるときは、same-origin-allow-popups を使います
  • CORP。 値は same-site・same-origin・cross-origin です(MDN)
  • OWASP は、COOP・COEP・CORP は組で働き、Spectre のような攻撃を防ぐと書きます。COEP を require-corp にすると、正しく設定されていない別オリジンの資源は読み込めなくなります

Cache-Control:機微な応答には no-store

セキュリティで見るのは、ログイン後の画面のような、機微な応答です。

  • no-store。 どの種類のキャッシュ(利用者側の private も、プロキシや CDN の shared も)にも、保存させません(MDN)。OWASP も、機微な応答には no-store を勧めます
  • no-cache は「キャッシュするな」ではありません。 保存してよいが、使う前にサーバーに確かめる、という意味です。保存させたくないなら no-store です(MDN)
  • private。 利用者ごとの内容(ログイン後の応答、Cookie で管理するセッション)には付けます。忘れると、shared キャッシュに保存され、別の利用者に使い回されて、個人情報が漏れうる、と MDN は注意しています

Cloudflare Pages では、どう付ける? _headers ファイルの書き方

Cloudflare Pages では、_headers というファイルで、静的な資源の応答のヘッダーを足せます。この節は、Cloudflare の公式の文書「Headers」(2026年8月25日更新)に沿っています。上限の数字は「Limits」(2026年9月5日更新)です。Pages と Workers の違いは、Cloudflare Pages と Workers の違いにまとめています。

書き方

拡張子の無いテキストファイル _headers を、静的な資源の置き場(フレームワークを使わないなら、ビルドの出力のフォルダー)に置きます。そのフォルダーの静的な資源の応答に、ヘッダーを足す・上書きする・消すことができます。_headers のファイルそのものは、公開されません。_headers に書いたヘッダーは、Cloudflare が普段送るものより優先されます。

書き方は、複数行のブロックです。1行目に、ヘッダーを付ける URL か URL のパターンを書きます。次の行から、字下げして 名前: 値 を並べます。# で始まる行は注釈です。絶対 URL を書くなら https で始め、ポートは書けません。

次は、公式の文書の例「アプリのセキュリティを固める」そのままです。このブログの案ではありません。

/app/*
  X-Frame-Options: DENY
  X-Content-Type-Options: nosniff
  Referrer-Policy: no-referrer
  Permissions-Policy: document-domain=()
  Content-Security-Policy: script-src 'self'; frame-ancestors 'none';

/app/* に合う応答にだけ付きます。Permissions-Policy: document-domain=() は、公式の例のまま載せたもので、この記事では説明しません。CSP の行は、そのまま自分のサイトに入れず、先に止めずに報告だけする Content-Security-Policy-Report-Only で試します(XSS と CSRF)。

パスの合わせ方は _redirects と同じです。* は1つだけ使え、:splat や :name も使えます。

ヘッダーを消すこともできます。名前の前に ! (感嘆符と空白)を付けます。公式の例は、/* に CSP を付け、/*.jpg ではそれを外すものです。

/*
  Content-Security-Policy: default-src 'self';

/*.jpg
  ! Content-Security-Policy

効かない所・上限・順番

  • Pages Functions が作る応答には、_headers は効きません。 Cloudflare の文書は、Pages Functions の応答には、URL が _headers の規則に合っていても、ヘッダーが付かないと書いています。SSR(サーバー側で HTML を作る)のフレームワーク、functions/ のフォルダー、_worker.js を使っているなら、付けたいヘッダーは、Functions のコードの中で付けます
  • 複数の規則に合うと、すべての規則のヘッダーを受け継ぎます。 同じヘッダーが2回当たると、値はカンマでつながります(Cloudflare)。W3C の CSP Level 3 では、カンマで区切った CSP の値は複数の方針の並びです。MDN によると、方針を足すほど制限が強くなるだけで、ゆるめることはできません。この動きは、手元では確かめていません
  • 上限は、規則が100まで、1行が2,000文字までです。 Headers の文書は、行全体(空白・名前・値)を数えると書いています。Limits の文書は、言い方が少し違って、1つのヘッダーが2,000文字までです。数えるときは、行全体で数えると安全側です。大きなヘッダーには、Pages Functions が勧められています(Limits の文書)
  • 転送が先に効きます。 リクエストが転送(_redirects)とヘッダーの両方に合うと、転送が優先されます

付けたあとは、自分のサイトの URL に curl -sI を送って、ヘッダーが付いたかを見ます。_headers を使ったとき、404 や転送の応答にヘッダーが付くかは、この記事では確かめていません。

手元のサーバーでヘッダーを付けて、点検するには?

ここから先は、手元で流したものです。環境は、macOS 14.4.1(arm64)・Python 3.12.3・curl 8.4.0 で、2026年10月21日に流しました。Windows では流していません。サーバーは 127.0.0.1(自分のパソコンの中だけ)で待ち受けます。

Python の文書は、http.server は本番には勧められない、基本のセキュリティの確かめしかしていない、と書いています。この例は、手元でヘッダーを確かめるためだけに使います。

ヘッダーを付けて返す(Python の標準ライブラリ)

次のような置き場を作ります。index.html の中身は何でもかまいません。

server.py
site/
  index.html
  account/
    index.html

server.py は次のとおりです。end_headers() を上書きして、応答の最後にヘッダーを足します。/account で始まるパスにだけ、Cache-Control: no-store も足します。

from http.server import HTTPServer, SimpleHTTPRequestHandler

SECURITY_HEADERS = {
    "Content-Security-Policy": "frame-ancestors 'none'",
    "X-Frame-Options": "DENY",
    "X-Content-Type-Options": "nosniff",
    "Referrer-Policy": "strict-origin-when-cross-origin",
    "Permissions-Policy": "geolocation=(), camera=(), microphone=()",
}

class Handler(SimpleHTTPRequestHandler):
    def end_headers(self):
        for name, value in SECURITY_HEADERS.items():
            self.send_header(name, value)
        if self.path.startswith("/account"):
            self.send_header("Cache-Control", "no-store")
        super().end_headers()

HTTPServer(("127.0.0.1", 8000), Handler).serve_forever()

Python の文書によると、send_header() は、ヘッダーを内部のバッファに足し、end_headers() のときに書き出します。end_headers() は、ヘッダーの終わりの空行を足して書き出します。同じ文書は、send_header() は改行(CRLF)を含む入力を断らない、とも注意しています。この例の値は固定の文字列だけです。外から来た値を、そのままヘッダーの値に入れないでください。

site/ に移ってから起動します。

cd site
python3 ../server.py

curl で見る

別のターミナルで、curl -sI(ヘッダーだけを見る)を送ります。手元で流したときは、別のプロセスがポート 8765 を使っていたので、別の番号にしました。出力で変わるのは URL の行だけなので、ここでは 8000 に書き換えて載せています。Date・Content-Length・Last-Modified は、置いたファイルや流した時刻で変わります。

$ curl -sI http://127.0.0.1:8000/
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.12.3
Date: Wed, 21 Oct 2026 10:41:15 GMT
Content-type: text/html
Content-Length: 13
Last-Modified: Wed, 21 Oct 2026 10:40:56 GMT
Content-Security-Policy: frame-ancestors 'none'
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), camera=(), microphone=()

$ curl -sI http://127.0.0.1:8000/account/
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.12.3
Date: Wed, 21 Oct 2026 10:41:15 GMT
Content-type: text/html
Content-Length: 15
Last-Modified: Wed, 21 Oct 2026 10:40:56 GMT
Content-Security-Policy: frame-ancestors 'none'
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), camera=(), microphone=()
Cache-Control: no-store

$ curl -sI http://127.0.0.1:8000/nope | grep -i -E '^HTTP|frame|nosniff'
HTTP/1.0 404 File not found
Content-Security-Policy: frame-ancestors 'none'
X-Frame-Options: DENY
X-Content-Type-Options: nosniff

読み取れることは4つです。

  • 5つのヘッダーが、応答に付きました
  • Cache-Control: no-store は、/account/ の応答にだけ付き、/ には付きませんでした
  • 存在しないパス(404)の応答にも、同じヘッダーが付きました。send_error も end_headers() を通るため、と見ていますが、Python のソースは読んでいません
  • Server: SimpleHTTP/0.6 Python/3.12.3 の行は、OWASP が「消すか、中身の無い値に」と言うヘッダーです。この例では消していません

macOS では、ポートが使われているときは、起動で OSError: [Errno 48] Address already in use が出ます。番号を変えます。別のプロセスが答えているかどうかは、Server: の行が SimpleHTTP で始まるかで見分けられます。手元では、別のプロセス(Server: BaseHTTP/0.6)が答えて、HEAD に 501 Unsupported method ('HEAD') を返しました。

点検するスクリプト

付けたいヘッダーが付いているかを、標準ライブラリだけで一覧にする例です。

import sys, urllib.request

WANT = ["Content-Security-Policy", "X-Frame-Options", "X-Content-Type-Options",
        "Referrer-Policy", "Permissions-Policy", "Strict-Transport-Security", "Cache-Control"]

url = sys.argv[1]
req = urllib.request.Request(url, method="HEAD")
with urllib.request.urlopen(req) as res:
    for name in WANT:
        value = res.headers.get(name)
        print(f"{name}: {value if value is not None else '(なし)'}")

check.py として保存して流します。

$ python3 check.py http://127.0.0.1:8000/account/
Content-Security-Policy: frame-ancestors 'none'
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), camera=(), microphone=()
Strict-Transport-Security: (なし)
Cache-Control: no-store
$ python3 check.py http://127.0.0.1:8000/
Content-Security-Policy: frame-ancestors 'none'
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), camera=(), microphone=()
Strict-Transport-Security: (なし)
Cache-Control: (なし)

手元のサーバーは http なので、HSTS は付けていません。HSTS は、HTTP で送ってもブラウザーが無視するヘッダーです(HTTPS と証明書の基本)。だから (なし) と出ています。HEAD を断るサーバーでは、urllib.error.HTTPError で止まります。手元でも、別のプロセスに流したとき HTTP Error 501: Unsupported method ('HEAD') で止まりました。ほかのサーバーに使うなら、GET にするか、例外を受ける形にします。

ここで確かめたのは、「ヘッダーが応答に付く」ことまでです。 ブラウザーで、frame-ancestors や X-Frame-Options が本当に iframe を止める様子、nosniff でスクリプトが止まる様子、Permissions-Policy で位置情報が断られる様子は、この記事では見ていません。

OWASP は、ヘッダーを確かめる道具として、Mozilla Observatory(オンラインの道具)と SmartScanner を挙げています。OWASP によると、オンラインの道具はたいていトップページだけを見て、SmartScanner はサイト全体を調べます。この記事では、どちらも使っていません。

よくある質問

X-Frame-Options と frame-ancestors は、どちらを付ければいい?

MDN は frame-ancestors を後継とし、両方付ければ対応しないブラウザーでも止められると書きます。OWASP は、できれば frame-ancestors、X-Frame-Options なら DENY です。IPA の手引き(2021 年の刷り)が挙げるのは X-Frame-Options だけです。両方付けるときは、同じ意味の値にそろえます(「両方付けるなら、同じ意味にそろえる」の所)。

meta タグでも、ヘッダーは付けられる?

ヘッダーによります。X-Frame-Options は <meta http-equiv> では効きません(MDN)。frame-ancestors も <meta> では使えず、応答ヘッダーで付けます(MDN・OWASP)。一方、Referrer-Policy は、MDN によると HTML の中でも決められます。

ヘッダーを付ければ、クリックジャッキングは完全に防げる?

OWASP によると、DoubleClickjacking のように目当てのページが最上位の窓になる型は、X-Frame-Options や frame-ancestors では防げません。大事な操作は、サーバー側で認可します。ヘッダー・SameSite・frame-buster は独立なので、できれば重ねます。この記事では、ブラウザーで iframe が止まる様子までは見ていません。

自分のアプリで確かめること(チェックリスト)
  • includeSubDomains・preload を付ける前に、すべてのサブドメインが HTTPS で開けるかを確かめ、max-age は短く始めたか(→「セキュリティヘッダーとは? 主なヘッダーの一覧(2026年10月時点)」)
  • HTML を返す応答に、frame-ancestors(埋め込みが要らなければ 'none')を、応答ヘッダーで付けているか。default-src や <meta> に頼っていないか(→「frame-ancestors の書き方」)
  • X-Frame-Options も付け、frame-ancestors と同じ意味の値(DENY と 'none' など)にそろえているか。ALLOW-FROM を書いていないか(→「両方付けるなら、同じ意味にそろえる」)
  • ログイン後の機能(公開範囲の変更・退会など)が、マウス操作だけで実行できないか。重要な処理に再入力などを挟んでいるか、セッションの Cookie に SameSite を付けているか。ヘッダーで防げない型もあるので、大事な操作はサーバー側で認可しているか(→「ヘッダーでは防げない型:DoubleClickjacking」)
  • X-Content-Type-Options: nosniff を付け、Content-Type を正しく付けているか。利用者が上げたファイルは Content-Disposition: attachment で返しているか(→「X-Content-Type-Options: nosniff」)
  • ログイン後のような機微な応答に Cache-Control: no-store を付けているか。no-cache を「保存しない」と読んでいないか(→「Cache-Control:機微な応答には no-store」)
  • X-XSS-Protection を頼りにしていないか(付けるなら 0)。Server・X-Powered-By を消す、または減らしているか(→「付けないもの・消すもの:X-XSS-Protection ほか」)
  • Cloudflare Pages の _headers が、Pages Functions の応答には効かないことを知っているか。規則は100まで、1行は2,000文字までに収まっているか(→「効かない所・上限・順番」)
  • 付けたあと、自分の URL に curl -sI を送って、ヘッダーが応答に出ているか。オンラインの点検の道具はたいていトップページだけを見る(→「curl で見る」「点検するスクリプト」)

まとめ

  • セキュリティヘッダーは、サーバーが応答に付けて、ブラウザーにページの扱い方を伝えるヘッダー。主なものは、HSTS・CSP・X-Frame-Options・nosniff・Referrer-Policy・Permissions-Policy・COOP/COEP/CORP・Cache-Control。HSTS と CSP は、HTTPS と証明書の基本とXSS と CSRFで詳しく説明している
  • X-XSS-Protection は付けない(付けるなら 0)。Expect-CT・Public-Key-Pins は使わない。Server・X-Powered-By は消すか減らす
  • クリックジャッキングは、見えない iframe を重ねて、ログイン中の利用者に意図しない操作をさせる攻撃(IPA・MDN)。守りは、埋め込みを禁じる・絞ること、SameSite、大事な処理への再入力など。DoubleClickjacking のようにヘッダーでは防げない型もある(OWASP)
  • frame-ancestors は X-Frame-Options の後継(MDN)。default-src に落ちず、<meta> では効かない。ALLOW-FROM は使わない。両方付けるなら同じ意味にそろえる
  • Cloudflare Pages では _headers で静的な資源の応答に付けられる。Pages Functions が作る応答には効かない
  • 手元の Python のサーバーで、ヘッダーが応答に付くことまでは確かめた。ブラウザーでの効き目は、この記事では見ていない

参考資料

※この記事は、公式の資料をもとに AI(Claude)で下書きし、2026年10月21日時点の公式の資料と照らして確かめてから公開しています。誤りに気づいたらお問い合わせからお知らせください。

PR
PR
タイトルとURLをコピーしました