DELOGs
[自サーバでNext.jsアプリを動かす#4]セキュリティヘッダと静的アセットのキャッシュ期間の設定

自サーバでNext.jsアプリを動かす#4
セキュリティヘッダと静的アセットのキャッシュ期間の設定

Next.jsで作成したWebサイト・アプリケーションを自サーバで運用するためのNginxのセキュリティヘッダと静的アセットのキャッシュ期間設定を実施

初回公開日

最終更新日

NginxにNext.jsでWebサイト・アプリケーション向けにセキュリティヘッダとキャッシュの設定をします。 セキュリティヘッダにはブラウザに対して「こういう動作はやめてね」「ここは安全じゃないかもよ」とセキュリティ面のルールを指示する役割があります。これはHTMLやCSSのようにユーザーに見えるものではなく、ブラウザが通信時に受け取って内部で処理してくれる隠れた盾のようなものです。
特に、Next.jsはフロントとバックが統合された強力なWebアプリで、構築をしやすい反面、攻撃対象になりやすいポイントも作り出してしまうという弱点もあります。この弱点を補完するためにもセキュリティヘッダはマストで設定しておく必要があります。Next.js自体にもnext.config.jsでのヘッダ追加方法があるが、Nginxレベルで制御するほうが強力で柔軟です。
キャッシュについてはついでです。
というわけで、早速設定していきます。

1.セキュリティヘッダの設定を追加

私の環境では、サイト毎の設定は、/etc/nginx/conf.d/example.com.confなど、/etc/nginx/conf.d/配下に記載しています。
bash
1sudo vi /etc/nginx/conf.d/example.com.conf
セキュリティヘッダを追記するのですが、記述位置に注意する必要があります。同じブロック内(下層ブロックを含む)で複数箇所に記述すると上書きされる可能性があるためです。なので、サイト共通のセキュリティヘッダはserverブロックの末尾にまとめて記載するようにします。
bash
1server { 2 ・・・・ 3 location / { 4 ・・・・ 5 } 6 # -------- 最低限のセキュリティ&HTTP/3通知 -------- 7 add_header X-Content-Type-Options "nosniff" always; 8 add_header X-Frame-Options "SAMEORIGIN" always; 9 add_header X-XSS-Protection "1; mode=block" always; 10 add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; style-src 'self'; object-src 'none';"; 11 add_header Referrer-Policy "strict-origin-when-cross-origin" always; 12 add_header Permissions-Policy "geolocation=(), microphone=()" always; 13 ## ここはhttp3対応時に記述したもの、末尾にまとめるために記述位置のみ変更 14 add_header Alt-Svc 'h3=":443"; ma=86400' always; 15 # ---------------------------------------------------- 16}

add_header X-Content-Type-Options "nosniff" always;

このヘッダをつけると、ブラウザがContent-Typeを勝手に推測(スニッフィング)しないようになります。
効果:
  • サーバーが Content-Type: text/plain を返しても、ブラウザが中身を見て勝手にHTMLやJSと判断するのを防止
  • MIMEスニッフィング攻撃の防止になる
  • 特にJSファイルやCSS、画像などのリソースで不正な解釈による実行を防ぐ

add_header X-Frame-Options "SAMEORIGIN" always;

このヘッダは、ページがiframeで読み込まれることを制限するものです。
効果:
  • SAMEORIGIN を指定すると、同一ドメインのページだけがiframeで埋め込める
  • クリックジャッキング攻撃(透明なiframeを重ねて誤クリックを誘導)を防止できる

add_header X-XSS-Protection "1; mode=block" always;

これは、古いブラウザ(特にIE系)におけるXSS保護機能を有効にする設定です。
効果:
  • 1; mode=block によって、XSSの兆候があるとブラウザがページの読み込みをブロックする
  • ただし、現代のブラウザ(Chrome/Edge/Firefoxなど)はこのヘッダを無視するため、現在は非推奨になりつつある
  • でも**「とりあえず入れておいて損はない系」**の対策

add_header Content-Security-Policy-Report-Only

これが一番重要な部分ですが、一番ややこしい設定になります。現代の主要ブラウザ(Chrome、Firefox、Edgeなど)は 前述のX-XSS-Protection ヘッダを無視するようになっており、代替として推奨されているのが Content-Security-Policy (CSP) ヘッダです。
ただ、CSPヘッダを強化しすぎると表示に影響が出ることがあります。JSやCSS、各種計測ダグが動かないということがあります。 そこで、導入する場合は 今回のようにReport-Only モードでまず様子を見るのがベストとなります。
bash
1add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; style-src 'self'; object-src 'none';";
ブラウザに表示されるワーニングを参照して、Next.jsとの互換性や機能制限とのバランスを考えながら慎重に設定する必要があります。以前設定したことのあるCSPヘッダは下記の通りです。
例):
bash
1add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://googleads.g.doubleclick.net https://www.google.co.jp https://s.yimg.jp; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://www.googletagmanager.com https://www.google.co.jp https://s.yimg.jp; font-src 'self'; media-src 'self' https://res.cloudinary.com; frame-ancestors 'self'; frame-src 'self' https://td.doubleclick.net https://www.googletagmanager.com/; connect-src 'self' https://www.google-analytics.com https://analytics.google.com https://www.google.co.jp https://stats.g.doubleclick.net https://s.yimg.jp https://www.google.com;";
ポイント
  • default-src 'self'; ->すべてのリソースはデフォルトで自身のドメインのみに制限
  • script-src ->JavaScriptの取得元を指定。Googleタグなど外部スクリプトを使う場合はここに許可が必要
  • style-src ->CSSの取得元を指定。インラインCSSを許可するには 'unsafe-inline' が必要
  • img-src ->画像の取得元を指定。外部のCDN画像など使っていれば、それも明示的に指定
  • connect-src ->fetch や WebSocket、API通信などの接続先を制限。外部APIや計測タグ通信に関係
  • frame-src ->iframeで読み込む外部サイトのドメインを指定。広告タグや埋め込み系サービスが該当
XSS保護の観点では非常に重要な設定になりますので、後回しにするのではなく、挿入するタグや利用するCDNが確定したら、こまめに設定を見直していく必要があります。

add_header Referrer-Policy "strict-origin-when-cross-origin" always;

このヘッダは、リンクをクリックした時などに参照元(Referrer)情報をどこまで送るかを制御するものです。
効果:
  • strict-origin-when-cross-origin は、同一オリジン間ではフルのURLを送るが、異なるオリジンにはスキーム+ドメインだけを送る
  • これにより、URLにクエリパラメータ(例えばセッションIDなど)が含まれていても漏洩しにくくなる
  • セキュリティと利便性のバランスが良い設定で、現在の推奨値

add_header Permissions-Policy "geolocation=(), microphone=()" always;

ブラウザの機能(カメラ、マイク、位置情報など)へのアクセスを制御するためのヘッダです(旧称:Feature-Policy)。
効果:
  • geolocation=(), microphone=() は、このページでは位置情報とマイクの使用を一切許可しないという意味
  • ブログや静的Webサイトではこれらの機能は使わないことが多いので、攻撃対象から外すのが目的

2. ついでにキャッシュの設定

これは下記のように/_next/static/のlocationブロックを追加して設定します。静的ファイルのキャッシュ制御を強化しています。
bash : example.com.conf
1# 静的ファイル(ハッシュ付き)のみ長期キャッシュ 2location ~* ^/_next/static/ { 3 proxy_pass http://localhost:3000; 4 proxy_http_version 1.1; 5 proxy_set_header Host $host; 6 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 7 expires 100d; 8 access_log off; 9} 10 11# それ以外の _next はキャッシュしない 12location /_next/ { 13 proxy_pass http://localhost:3000; 14 proxy_http_version 1.1; 15 proxy_set_header Host $host; 16 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 17}
/_next/static/はNext.jsの静的アセット(ビルド後は内容変更なし)であるため、長期キャッシュして問題ないものになります。上記の例では100日間にしています。
access_log off; は、該当のリクエストに対してアクセスログの出力を無効化するディレクティブです。/_next/static/ 以下のファイル(JS, CSS, フォントなど)は、Next.jsがビルド時に生成するキャッシュ可能な静的ファイルです。これらはCDN的に長期間キャッシュされるもので、アクセス回数が多くなる傾向があるため、ログが大量に出てしまってノイズになるのでログ出力を無効化しています。
_next/ 全体に設定してしまうと/_next/data/xxxx.jsonなどの動的なデータも含まれてしまうので、静的なアセットのlocationを追記してそこだけキャッシュ設定を行います。

3.Nginxのリロード

以上の設定が終わったら、Nginxをリロードします。
bash
1sudo nginx -t 2sudo systemctl reload nginx
これで、Next.jsのWebサイトやWebアプリケーションを安全に運用する準備が整いました。どんどんコンテンツを作成していけます。
次回は番外編ですがこちら...
前回の確認はこちら...
この記事の執筆・編集担当
DE

松本 孝太郎

DELOGs編集部/中年新米プログラマー

ここ数年はReact&MUIのフロントエンドエンジニアって感じでしたが、Next.jsを学んで少しずつできることが広がりつつあります。その実践記録をできるだけ共有していければと思っています。