headers
Headers allow you to set custom HTTP headers on the response to an incoming request on a given path.
To set custom HTTP headers you can use theheaders key innext.config.js:
module.exports= {asyncheaders() {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 withsource andheaders properties:
sourceis the incoming request path pattern.headersis an array of response header objects, withkeyandvalueproperties.basePath:falseorundefined- if false the basePath won't be included when matching, can be used for external rewrites only.locale:falseorundefined- whether the locale should not be included when matching.hasis an array ofhas objects with thetype,keyandvalueproperties.missingis an array ofmissing objects with thetype,keyandvalueproperties.
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 headerx-hello beingworld due to the last header value set beingworld.
module.exports= {asyncheaders() {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/first-post (no nested paths):
module.exports= {asyncheaders() {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', }, ], }, ] },}The pattern/blog/:slug matches/blog/first-post and/blog/post-1 but not a nested path like/blog/a/b. Patterns are anchored to the start,/blog/:slug will not match/archive/blog/first-post.
You can use modifiers on parameters:* (zero or more),+ (one or more),? (zero or one). For example,/blog/:slug* matches/blog,/blog/a, and/blog/a/b/c.
Read more details onpath-to-regexp documentation.
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:
module.exports= {asyncheaders() {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:
module.exports= {asyncheaders() {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 thesource as non-special values they must be escaped by adding\\ before them:
module.exports= {asyncheaders() {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 header, cookie, or query values also match thehas field or don't match themissing field can be used. Both thesource and allhas items must match and allmissing items must not match for the header to be applied.
has andmissing items can have the following fields:
type:String- must be eitherheader,cookie,host, orquery.key:String- the key from the selected type to match against.value:Stringorundefined- 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 valuefirst-(?<paramName>.*)is used forfirst-secondthensecondwill be usable in the destination with:paramName.
module.exports= {asyncheaders() {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', }, ], }, ] },}Headers with basePath support
When leveragingbasePath support with headers eachsource is automatically prefixed with thebasePath unless you addbasePath: false to the header:
module.exports= { basePath:'/docs',asyncheaders() {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 leveragingi18n support with headers eachsource is automatically prefixed to handle the configuredlocales unless you addlocale: false to the header. Iflocale: false is used you must prefix thesource with a locale for it to be matched correctly.
module.exports= { i18n: { locales: ['en','fr','de'], defaultLocale:'en', },asyncheaders() {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 sets theCache-Control header ofpublic, max-age=31536000, immutable for truly immutable assets. It cannot be overridden. These immutable files contain a SHA-hash in the file name, so they can be safely cached indefinitely. For example,Static Image Imports. You cannot setCache-Control headers innext.config.js for these assets.
However, you can setCache-Control headers for other responses or data.
If you need to revalidate the cache of a page that has beenstatically generated, you can do so by setting therevalidate prop in the page'sgetStaticProps function.
To cache the response from anAPI Route, you can useres.setHeader:
importtype { NextApiRequest, NextApiResponse }from'next'typeResponseData= { message:string}exportdefaultfunctionhandler( req:NextApiRequest, res:NextApiResponse<ResponseData>) {res.setHeader('Cache-Control','s-maxage=86400')res.status(200).json({ message:'Hello from Next.js!' })}You can also use caching headers (Cache-Control) insidegetServerSideProps to cache dynamic responses. For example, usingstale-while-revalidate.
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.exportconstgetServerSideProps= (async (context)=> {context.res.setHeader('Cache-Control','public, s-maxage=10, stale-while-revalidate=59' )return { props: {}, }})satisfiesGetServerSidePropsOptions
CORS
Cross-Origin Resource Sharing (CORS) is a security feature that allows you to control which sites can access your resources. You can set theAccess-Control-Allow-Origin header to allow a specific origin to access your API Endpoints.
asyncheaders() {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
This header controls DNS prefetching, allowing browsers to proactively perform domain name resolution on external links, images, CSS, JavaScript, and more. This prefetching is performed in the background, so theDNS is more likely to be resolved by the time the referenced items are needed. This reduces latency when the user clicks a link.
{ key:'X-DNS-Prefetch-Control', value:'on'}Strict-Transport-Security
This header informs browsers it should only be accessed using HTTPS, instead of using HTTP. Using the configuration below, all present and future subdomains will use HTTPS for amax-age of 2 years. This blocks access to pages or subdomains that can only be served over HTTP.
{ key:'Strict-Transport-Security', value:'max-age=63072000; includeSubDomains; preload'}X-Frame-Options
This header indicates whether the site should be allowed to be displayed within aniframe. This can prevent against clickjacking attacks.
This header has been superseded by CSP'sframe-ancestors option, which has better support in modern browsers (seeContent Security Policy for configuration details).
{ key:'X-Frame-Options', value:'SAMEORIGIN'}Permissions-Policy
This header allows you to control which features and APIs can be used in the browser. It was previously namedFeature-Policy.
{ key:'Permissions-Policy', value:'camera=(), microphone=(), geolocation=(), browsing-topics=()'}X-Content-Type-Options
This header prevents the browser from attempting to guess the type of content if theContent-Type header is not explicitly set. This can prevent XSS exploits for websites that allow users to upload and share files.
For example, a user trying to download an image, but having it treated as a differentContent-Type like an executable, which could be malicious. This header also applies to downloading browser extensions. The only valid value for this header isnosniff.
{ key:'X-Content-Type-Options', value:'nosniff'}Referrer-Policy
This header controls how much information the browser includes when navigating from the current website (origin) to another.
{ key:'Referrer-Policy', value:'origin-when-cross-origin'}Content-Security-Policy
Learn more about adding aContent Security Policy to your application.
Version History
| Version | Changes |
|---|---|
v13.3.0 | missing added. |
v10.2.0 | has added. |
v9.5.0 | Headers added. |
Was this helpful?