N
NextTech Insights
Next.jsのセキュリティヘッダー|何から入れる?実務チェックリスト(HSTS / CSP Report-Only / COOP/COEP)
securitywebops

Next.jsのセキュリティヘッダー|何から入れる?実務チェックリスト(HSTS / CSP Report-Only / COOP/COEP)

5 min read

Next.jsでセキュリティヘッダーを入れる実務順序。低リスクの基本ヘッダー→HSTS(HTTPSが前提)→CSPはReport-Onlyで観測→COOP/COEP/CORPは必要なルートだけ。壊さず段階投入するためのチェックリスト。

目次

Next.jsでセキュリティヘッダーは何から入れる?順序は?

結論(Conclusion)

壊しにくい順に入れる。

  1. 安全な基本ヘッダー(nosniff / referrer / permissions)
  2. HSTS(HTTPSが全域で保証できる場合のみ)
  3. CSPはReport-Only(観測→締める→強制)
  4. COOP/COEP/CORP は“必要なルートだけ”(クロスオリジン分離が必要な場合)

実務ルール:1デプロイ=1変更くらいで段階投入して戻せるようにする。

背景(Explanation)

セキュリティヘッダーは「入れたら終わり」ではなく、ブラウザとの契約。 壊れやすいのはだいたいここ。

  • authリダイレクト
  • 計測タグ
  • 埋め込み
  • CDN/フォント/画像

だからヘッダー変更はOpsの段階投入として扱う(観測して、戻せるようにする)。

実務手順(Practical Guide)

手順1:外部依存を棚卸しする(CSPの材料)

  • analytics(GA4/GTM)
  • images/CDN(Cloudinary/S3/画像プロキシ)
  • fonts(Google Fonts / self-host)
  • embeds(YouTube/Stripe/maps)
  • auth(リダイレクト先)

この一覧がallowlistの元。

手順2:安全な基本ヘッダーを入れる(低リスク)

  • X-Content-Type-Options: nosniff
  • Referrer-Policy: strict-origin-when-cross-origin
  • Permissions-Policy: ...
  • X-Frame-Options: DENY(埋め込みが不要なら)

Next.js例:

// next.config.ts
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          { key: 'X-Content-Type-Options', value: 'nosniff' },
          { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
          { key: 'X-Frame-Options', value: 'DENY' },
          {
            key: 'Permissions-Policy',
            value: 'camera=(), microphone=(), geolocation=(), payment=()',
          },
        ],
      },
    ];
  },
};

export default nextConfig;

判断基準:

  • 埋め込みが必要なら X-Frame-Options は避けて、CSPの frame-ancestors で制御する。

手順3:HSTSを入れる(HTTPSが絶対条件)

  • Strict-Transport-Security: max-age=...; preload

判断基準:

  • どこかにHTTPが残っているなら入れない(閉じ込め事故)。

手順4:CSPはReport-Onlyから始める

  • まず Content-Security-Policy-Report-Only
  • いきなり Content-Security-Policy(強制)はやらない

最小の叩き台:

default-src 'self';
base-uri 'self';
object-src 'none';
frame-ancestors 'none';
img-src 'self' data: https:;
script-src 'self' 'unsafe-inline' https:;
style-src 'self' 'unsafe-inline' https:;

レポートを見て締める:

  • 'unsafe-inline' を減らす
  • https: を放置せず、既知ホストにpinする
  • scriptはnonce/hashへ

手順5:COOP/COEP/CORPは必要なルートだけ

  • Cross-Origin-Opener-Policy: same-origin
  • Cross-Origin-Embedder-Policy: require-corp
  • Cross-Origin-Resource-Policy: same-site

判断基準:

  • SharedArrayBuffer等でクロスオリジン分離が必要なルートだけに適用する。

手順6:段階投入(Ops)

  • 1デプロイ=1変更
  • ロールバック手段を用意
  • 主要フロー(ログイン/計測/埋め込み)を毎回確認

失敗パターン(Pitfalls)

  • コピペCSPでauth/計測が壊れる
  • HTTPが残っているのにHSTSを入れて閉じ込める
  • COEPを全体に入れて埋め込み/CDNが壊れる
  • 埋め込みが必要なのに X-Frame-Options: DENY

チェックリスト(Checklist)

  • [ ] 外部依存(計測/認証/CDN/フォント/埋め込み)を棚卸しした
  • [ ] 基本ヘッダーを入れた(nosniff/referrer/permissions)
  • [ ] 埋め込み方針を決めた(XFO or frame-ancestors)
  • [ ] HTTPSが全域で保証されていることを確認した(HSTS前提)
  • [ ] HSTSのmax-age戦略を決めた(短→長、preload可否)
  • [ ] CSPはReport-Onlyから開始した
  • [ ] CSPレポートの受け皿がある
  • [ ] allowlistを https: から既知ホストへ締めた
  • [ ] script-srcの方針がある(外部化/nonce/hash)
  • [ ] COOP/COEP/CORPは必要ルートだけに限定した
  • [ ] ロールバック手段がある

FAQ

Q1. helmetを入れれば終わり?

ベースラインにはなるが、埋め込み方針やCSP allowlist、段階投入はアプリ固有。最後は意思決定が必要。

Q2. CSPはなぜReport-Onlyから?

アプリ固有だから。観測してから締めないと壊れる。

Q3. COOP/COEPはいつ必要?

クロスオリジン分離が必要な機能がある時だけ。分からないなら全体適用しない。

参考

人気記事

  1. 1Permit2とは何か?Approveが変わった理由と安全な使い方(チェックリスト)
  2. 2署名(Sign)画面の読み方|Approve/Permit/NFT全権限を30秒で判別するチェックリスト
  3. 3仕様→実装に落とすプロンプトテンプレ(AI開発)|AIに“迷わせない”仕様の書き方
  4. 4EVMのトークン承認(Approve)を見直す方法|Revokeの手順と判断基準(チェックリスト)
  5. 5曖昧な依頼を要件定義に変換する質問リスト(AI開発)|AIに実装させる前に聞くべきこと

関連記事