c0bb1bc82c
x-ref: https://github.com/vercel/next.js/issues/22660#issuecomment-890400273 ## Documentation / Examples - [x] Make sure the linting passes
390 lines
9.9 KiB
Markdown
390 lines
9.9 KiB
Markdown
---
|
|
description: Add custom HTTP headers to your Next.js app.
|
|
---
|
|
|
|
# Headers
|
|
|
|
<details open>
|
|
<summary><b>Examples</b></summary>
|
|
<ul>
|
|
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/headers">Headers</a></li>
|
|
</ul>
|
|
</details>
|
|
|
|
<details>
|
|
<summary><b>Version History</b></summary>
|
|
|
|
| Version | Changes |
|
|
| --------- | -------------- |
|
|
| `v10.2.0` | `has` added. |
|
|
| `v9.5.0` | Headers added. |
|
|
|
|
</details>
|
|
|
|
Headers allow you to set custom HTTP headers for an incoming request path.
|
|
|
|
To set custom HTTP headers you can use the `headers` key in `next.config.js`:
|
|
|
|
```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` is an async function that expects an array to be returned holding objects with `source` and `headers` properties:
|
|
|
|
- `source` is the incoming request path pattern.
|
|
- `headers` is an array of header objects with the `key` and `value` properties.
|
|
- `basePath`: `false` or `undefined` - if false the basePath won't be included when matching, can be used for external rewrites only.
|
|
- `locale`: `false` or `undefined` - whether the locale should not be included when matching.
|
|
- `has` is an array of [has objects](#header-cookie-and-query-matching) with the `type`, `key` and `value` properties.
|
|
|
|
Headers are checked before the filesystem which includes pages and `/public` files.
|
|
|
|
## Header Overriding Behavior
|
|
|
|
If two headers match the same path and set the same header key, the last header key will override the first. Using the below headers, the path `/hello` will result in the header `x-hello` being `world` due to the last header value set being `world`.
|
|
|
|
```js
|
|
module.exports = {
|
|
async headers() {
|
|
return [
|
|
{
|
|
source: '/:path*',
|
|
headers: [
|
|
{
|
|
key: 'x-hello',
|
|
value: 'there',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
source: '/hello',
|
|
headers: [
|
|
{
|
|
key: 'x-hello',
|
|
value: 'world',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
}
|
|
```
|
|
|
|
## Path Matching
|
|
|
|
Path matches are allowed, for example `/blog/:slug` will match `/blog/hello-world` (no nested paths):
|
|
|
|
```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',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
}
|
|
```
|
|
|
|
### Wildcard Path Matching
|
|
|
|
To match a wildcard path you can use `*` after a parameter, for example `/blog/:slug*` will match `/blog/a/b/c/d/hello-world`:
|
|
|
|
```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',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
}
|
|
```
|
|
|
|
### Regex Path Matching
|
|
|
|
To match a regex path you can wrap the regex in parenthesis after a parameter, for example `/blog/:slug(\\d{1,})` will match `/blog/123` but not `/blog/abc`:
|
|
|
|
```js
|
|
module.exports = {
|
|
async headers() {
|
|
return [
|
|
{
|
|
source: '/blog/:post(\\d{1,})',
|
|
headers: [
|
|
{
|
|
key: 'x-post',
|
|
value: ':post',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
}
|
|
```
|
|
|
|
The following characters `(`, `)`, `{`, `}`, `:`, `*`, `+`, `?` are used for regex path matching, so when used in the `source` as non-special values they must be escaped by adding `\\` before them:
|
|
|
|
```js
|
|
module.exports = {
|
|
async headers() {
|
|
return [
|
|
{
|
|
// this will match `/english(default)/something` being requested
|
|
source: '/english\\(default\\)/:slug',
|
|
headers: [
|
|
{
|
|
key: 'x-header',
|
|
value: 'value',
|
|
},
|
|
],
|
|
},
|
|
]
|
|
},
|
|
}
|
|
```
|
|
|
|
## Header, Cookie, and Query Matching
|
|
|
|
To only apply a header when either header, cookie, or query values also match the `has` field can be used. Both the `source` and all `has` items must match for the header to be applied.
|
|
|
|
`has` items have the following fields:
|
|
|
|
- `type`: `String` - must be either `header`, `cookie`, `host`, or `query`.
|
|
- `key`: `String` - the key from the selected type to match against.
|
|
- `value`: `String` or `undefined` - the value to check for, if undefined any value will match. A regex like string can be used to capture a specific part of the value, e.g. if the value `first-(?<paramName>.*)` is used for `first-second` then `second` will be usable in the destination with `:paramName`.
|
|
|
|
```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 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',
|
|
},
|
|
],
|
|
},
|
|
]
|
|
},
|
|
}
|
|
```
|
|
|
|
### Headers with basePath support
|
|
|
|
When leveraging [`basePath` support](/docs/api-reference/next.config.js/basepath.md) with headers each `source` is automatically prefixed with the `basePath` unless you add `basePath: false` to the header:
|
|
|
|
```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,
|
|
},
|
|
]
|
|
},
|
|
}
|
|
```
|
|
|
|
### Headers with i18n support
|
|
|
|
When leveraging [`i18n` support](/docs/advanced-features/i18n-routing.md) with headers each `source` is automatically prefixed to handle the configured `locales` unless you add `locale: false` to the header. If `locale: false` is used you must prefix the `source` with a locale for it to be matched correctly.
|
|
|
|
```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: 'worlld',
|
|
},
|
|
],
|
|
},
|
|
]
|
|
},
|
|
}
|
|
```
|
|
|
|
### Cache-Control
|
|
|
|
Cache-Control headers set in next.config.js will be overwritten in production to ensure that static assets can be cached effectively. If you need to revalidate the cache of a page that has been [statically generated](https://nextjs.org/docs/basic-features/pages#static-generation-recommended), you can do so by setting `revalidate` in the page's [`getStaticProps`](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation) function.
|
|
|
|
## Related
|
|
|
|
For more information, we recommend the following sections:
|
|
|
|
<div class="card">
|
|
<a href="/docs/advanced-features/security-headers.md">
|
|
<b>Security Headers:</b>
|
|
<small>Improve the security of your Next.js application by add HTTP response headers.</small>
|
|
</a>
|
|
</div>
|