コンテンツへスキップ

headers

Headers を使用すると、特定のパスへの受信リクエストに対するレスポンスにカスタムHTTPヘッダーを設定できます。

カスタムHTTPヘッダーを設定するには、next.config.jsheadersキーを使用します。

next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/about',
        headers: [
          {
            key: 'x-custom-header',
            value: 'my custom header value',
          },
          {
            key: 'x-another-custom-header',
            value: 'my other custom header value',
          },
        ],
      },
    ]
  },
}

headersは、sourceheadersプロパティを持つオブジェクトを保持する配列が返されることを期待する非同期関数です。

  • sourceは受信リクエストのパスパターンです。
  • headersは、keyvalueプロパティを持つレスポンスヘッダーオブジェクトの配列です。
  • basePath: false または undefined - falseの場合、マッチング時にbasePathは含まれません。外部リライトでのみ使用できます。
  • locale: false または undefined - マッチング時にロケールを含めるべきではないかどうか。
  • hasは、typekeyvalueプロパティを持つhas オブジェクトの配列です。
  • missingは、typekeyvalueプロパティを持つmissing オブジェクトの配列です。

ヘッダーは、ページと/publicファイルを含むファイルシステムより前にチェックされます。

ヘッダーの上書き動作

2つのヘッダーが同じパスに一致し、同じヘッダーキーを設定した場合、最後のヘッダーキーが最初のを上書きします。以下のヘッダーを使用すると、パス/helloは、最後に設定されたヘッダー値がworldであるため、ヘッダーx-helloworldになります。

next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          {
            key: 'x-hello',
            value: 'there',
          },
        ],
      },
      {
        source: '/hello',
        headers: [
          {
            key: 'x-hello',
            value: 'world',
          },
        ],
      },
    ]
  },
}

パスのマッチング

パスのマッチングは許可されており、例えば/blog/:slug/blog/hello-worldに一致します(ネストされたパスは除く)。

next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/blog/:slug',
        headers: [
          {
            key: 'x-slug',
            value: ':slug', // Matched parameters can be used in the value
          },
          {
            key: 'x-slug-:slug', // Matched parameters can be used in the key
            value: 'my other custom header value',
          },
        ],
      },
    ]
  },
}

ワイルドカードパスのマッチング

ワイルドカードパスに一致させるには、パラメーターの後に*を使用します。例えば、/blog/:slug*/blog/a/b/c/d/hello-worldに一致します。

next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/blog/:slug*',
        headers: [
          {
            key: 'x-slug',
            value: ':slug*', // Matched parameters can be used in the value
          },
          {
            key: 'x-slug-:slug*', // Matched parameters can be used in the key
            value: 'my other custom header value',
          },
        ],
      },
    ]
  },
}

正規表現パスのマッチング

正規表現パスに一致させるには、パラメーターの後に正規表現を括弧で囲みます。例えば、/blog/:slug(\\d{1,})/blog/123には一致しますが、/blog/abcには一致しません。

next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/blog/:post(\\d{1,})',
        headers: [
          {
            key: 'x-post',
            value: ':post',
          },
        ],
      },
    ]
  },
}

以下の文字(){}:*+?は正規表現パスのマッチングに使用されるため、sourceで特殊でない値として使用する場合は、それらの前に\\を追加してエスケープする必要があります。

next.config.js
module.exports = {
  async headers() {
    return [
      {
        // this will match `/english(default)/something` being requested
        source: '/english\\(default\\)/:slug',
        headers: [
          {
            key: 'x-header',
            value: 'value',
          },
        ],
      },
    ]
  },
}

ヘッダー、Cookie、またはクエリの値がhasフィールドにも一致する場合、またはmissingフィールドに一致しない場合にのみヘッダーを適用するには、それらのフィールドを使用できます。ヘッダーが適用されるには、sourceとすべてのhas項目が一致し、すべてのmissing項目が一致しない必要があります。

hasおよびmissing項目は、以下のフィールドを持つことができます。

  • type: String - headercookiehost、またはqueryのいずれかである必要があります。
  • key: String - 選択されたタイプからマッチング対象となるキー。
  • value: String または undefined - チェックする値。undefinedの場合、任意の値が一致します。値の特定の部分をキャプチャするために正規表現のような文字列を使用できます。例えば、値first-(?<paramName>.*)first-secondに使用された場合、second:paramNameでデスティネーションで使用できます。
next.config.js
module.exports = {
  async headers() {
    return [
      // if the header `x-add-header` is present,
      // the `x-another-header` header will be applied
      {
        source: '/:path*',
        has: [
          {
            type: 'header',
            key: 'x-add-header',
          },
        ],
        headers: [
          {
            key: 'x-another-header',
            value: 'hello',
          },
        ],
      },
      // if the header `x-no-header` is not present,
      // the `x-another-header` header will be applied
      {
        source: '/:path*',
        missing: [
          {
            type: 'header',
            key: 'x-no-header',
          },
        ],
        headers: [
          {
            key: 'x-another-header',
            value: 'hello',
          },
        ],
      },
      // if the source, query, and cookie are matched,
      // the `x-authorized` header will be applied
      {
        source: '/specific/:path*',
        has: [
          {
            type: 'query',
            key: 'page',
            // the page value will not be available in the
            // header key/values since value is provided and
            // doesn't use a named capture group e.g. (?<page>home)
            value: 'home',
          },
          {
            type: 'cookie',
            key: 'authorized',
            value: 'true',
          },
        ],
        headers: [
          {
            key: 'x-authorized',
            value: ':authorized',
          },
        ],
      },
      // if the header `x-authorized` is present and
      // contains a matching value, the `x-another-header` will be applied
      {
        source: '/:path*',
        has: [
          {
            type: 'header',
            key: 'x-authorized',
            value: '(?<authorized>yes|true)',
          },
        ],
        headers: [
          {
            key: 'x-another-header',
            value: ':authorized',
          },
        ],
      },
      // if the host is `example.com`,
      // this header will be applied
      {
        source: '/:path*',
        has: [
          {
            type: 'host',
            value: 'example.com',
          },
        ],
        headers: [
          {
            key: 'x-another-header',
            value: ':authorized',
          },
        ],
      },
    ]
  },
}

basePath対応のヘッダー

ヘッダーでbasePathサポートを利用する場合、ヘッダーにbasePath: falseを追加しない限り、各sourceは自動的にbasePathがプレフィックスされます。

next.config.js
module.exports = {
  basePath: '/docs',
 
  async headers() {
    return [
      {
        source: '/with-basePath', // becomes /docs/with-basePath
        headers: [
          {
            key: 'x-hello',
            value: 'world',
          },
        ],
      },
      {
        source: '/without-basePath', // is not modified since basePath: false is set
        headers: [
          {
            key: 'x-hello',
            value: 'world',
          },
        ],
        basePath: false,
      },
    ]
  },
}

i18n対応のヘッダー

ヘッダーでi18nサポートを利用する場合、ヘッダーにlocale: falseを追加しない限り、各sourceは設定されたlocalesを処理するために自動的にプレフィックスされます。locale: falseを使用する場合、正しく一致させるにはsourceをロケールでプレフィックスする必要があります。

next.config.js
module.exports = {
  i18n: {
    locales: ['en', 'fr', 'de'],
    defaultLocale: 'en',
  },
 
  async headers() {
    return [
      {
        source: '/with-locale', // automatically handles all locales
        headers: [
          {
            key: 'x-hello',
            value: 'world',
          },
        ],
      },
      {
        // does not handle locales automatically since locale: false is set
        source: '/nl/with-locale-manual',
        locale: false,
        headers: [
          {
            key: 'x-hello',
            value: 'world',
          },
        ],
      },
      {
        // this matches '/' since `en` is the defaultLocale
        source: '/en',
        locale: false,
        headers: [
          {
            key: 'x-hello',
            value: 'world',
          },
        ],
      },
      {
        // this gets converted to /(en|fr|de)/(.*) so will not match the top-level
        // `/` or `/fr` routes like /:path* would
        source: '/(.*)',
        headers: [
          {
            key: 'x-hello',
            value: 'world',
          },
        ],
      },
    ]
  },
}

Cache-Control

Next.jsは、完全にイミュータブルなアセットに対してpublic, max-age=31536000, immutableCache-Controlヘッダーを設定します。これは上書きできません。これらのイミュータブルなファイルはファイル名にSHAハッシュを含んでいるため、安全に無期限にキャッシュできます。例えば、静的画像インポートがこれに該当します。これらのアセットに対してnext.config.jsCache-Controlヘッダーを設定することはできません。

ただし、他のレスポンスやデータに対してCache-Controlヘッダーを設定することは可能です。

静的に生成されたページのキャッシュを再検証する必要がある場合は、ページのgetStaticProps関数でrevalidateプロパティを設定することで可能です。

APIルートからのレスポンスをキャッシュするには、res.setHeaderを使用できます。

pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from 'next'
 
type ResponseData = {
  message: string
}
 
export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseData>
) {
  res.setHeader('Cache-Control', 's-maxage=86400')
  res.status(200).json({ message: 'Hello from Next.js!' })
}

getServerSideProps内でキャッシングヘッダー(Cache-Control)を使用して、動的なレスポンスをキャッシュすることもできます。例えば、stale-while-revalidateを使用するなどです。

pages/index.tsx
import { GetStaticProps, GetStaticPaths, GetServerSideProps } from 'next'
 
// This value is considered fresh for ten seconds (s-maxage=10).
// If a request is repeated within the next 10 seconds, the previously
// cached value will still be fresh. If the request is repeated before 59 seconds,
// the cached value will be stale but still render (stale-while-revalidate=59).
//
// In the background, a revalidation request will be made to populate the cache
// with a fresh value. If you refresh the page, you will see the new value.
export const getServerSideProps = (async (context) => {
  context.res.setHeader(
    'Cache-Control',
    'public, s-maxage=10, stale-while-revalidate=59'
  )
 
  return {
    props: {},
  }
}) satisfies GetServerSideProps

オプション

CORS

クロスオリジンリソース共有 (CORS)は、どのサイトがリソースにアクセスできるかを制御できるセキュリティ機能です。Access-Control-Allow-Originヘッダーを設定することで、特定のオリジンがあなたのAPIエンドポイント.

async headers() {
    return [
      {
        source: "/api/:path*",
        headers: [
          {
            key: "Access-Control-Allow-Origin",
            value: "*", // Set your origin
          },
          {
            key: "Access-Control-Allow-Methods",
            value: "GET, POST, PUT, DELETE, OPTIONS",
          },
          {
            key: "Access-Control-Allow-Headers",
            value: "Content-Type, Authorization",
          },
        ],
      },
    ];
  },

X-DNS-Prefetch-Control

このヘッダーはDNSプリフェッチを制御し、ブラウザが外部リンク、画像、CSS、JavaScriptなどに対してドメイン名解決を積極的に実行できるようにします。このプリフェッチはバックグラウンドで実行されるため、参照されているアイテムが必要になるまでにDNSが解決される可能性が高まります。これにより、ユーザーがリンクをクリックした際の遅延が減少します。

{
  key: 'X-DNS-Prefetch-Control',
  value: 'on'
}

Strict-Transport-Security

このヘッダーは、ブラウザに対し、HTTPではなくHTTPSを使用してのみアクセスされるべきであることを通知します。以下の設定を使用すると、現在および将来のすべてのサブドメインは、2年間のmax-ageでHTTPSを使用します。これにより、HTTP経由でのみ提供できるページやサブドメインへのアクセスがブロックされます。

Vercelにデプロイする場合、next.config.jsheadersを宣言しない限り、このヘッダーはすべてのデプロイに自動的に追加されるため、必要ありません。

{
  key: 'Strict-Transport-Security',
  value: 'max-age=63072000; includeSubDomains; preload'
}

X-Frame-Options

このヘッダーは、サイトがiframe内に表示されることを許可するかどうかを示します。これにより、クリックジャッキング攻撃を防ぐことができます。

このヘッダーは、CSPのframe-ancestorsオプションに置き換えられています。これは最新のブラウザでより良いサポートを提供しています(設定の詳細についてはコンテンツセキュリティポリシーを参照してください)。

{
  key: 'X-Frame-Options',
  value: 'SAMEORIGIN'
}

Permissions-Policy

このヘッダーは、ブラウザで使用できる機能とAPIを制御できます。以前はFeature-Policyという名前でした。

{
  key: 'Permissions-Policy',
  value: 'camera=(), microphone=(), geolocation=(), browsing-topics=()'
}

X-Content-Type-Options

このヘッダーは、Content-Typeヘッダーが明示的に設定されていない場合にブラウザがコンテンツのタイプを推測しようとするのを防ぎます。これにより、ユーザーがファイルをアップロードおよび共有できるウェブサイトにおけるXSS攻撃を防ぐことができます。

例えば、ユーザーが画像をダウンロードしようとした際に、それが実行可能ファイルのような異なるContent-Typeとして扱われ、悪意のあるものとなる可能性があります。このヘッダーはブラウザ拡張機能のダウンロードにも適用されます。このヘッダーに有効な値はnosniffのみです。

{
  key: 'X-Content-Type-Options',
  value: 'nosniff'
}

Referrer-Policy

このヘッダーは、ブラウザが現在のウェブサイト(オリジン)から別のサイトへ移動する際に、どれだけの情報を含めるかを制御します。

{
  key: 'Referrer-Policy',
  value: 'origin-when-cross-origin'
}

Content-Security-Policy

アプリケーションにコンテンツセキュリティポリシーを追加する方法について詳しくはこちらをご覧ください。

バージョン履歴

バージョン変更点
v13.3.0missingが追加されました。
v10.2.0hasが追加されました。
v9.5.0ヘッダーが追加されました。