Over the past few years, Next.js has become a go-to framework not only for frontend development, but also for robust API development. When building APIs, it is critical to spend some time on Cross-Origin Resource Sharing (CORS).
In this complete guide, you will explore several methods for using CORS in Next.js, enabling you to handle cross-origin requests effectively and securely.
Jump ahead:
- What is CORS?
- Why you need CORS with Next.js
- Prerequisites for learning to configure CORS in Next.js
- Enabling CORS for all API routes
- CORS headers in action
- General tips for using CORS in Next.js
What is CORS?
CORS is a security mechanism that enables a server to specify which origins are allowed to access and load resources in a web browser. In this context, an “origin” refers to the combination of the protocol, domain, and port number a request comes from.
In detail, CORS is a protection system implemented by web browsers to enforce restrictions on cross-origin requests. Its main goal is to protect user security and prevent unauthorized access to resources and data.
To enforce CORS, browsers and servers exchange a set of specific HTTP headers.
Before making certain types of cross-origin requests, the browser sends a preflight request using the HTTP OPTIONS
method to the server. The server then responds with some CORS headers, indicating whether the cross-origin should be permitted or denied.
If the preflight request is successful, the actual cross-origin request is performed by the browser. Otherwise, it gets blocked because of a CORS policy error.
The main CORS headers include:
Access-Control-Allow-Origin
— Specifies the origin that has access to the resourceAccess-Control-Allow-Methods
— Added to the preflight response to indicate the permitted HTTP methods, such asGET
,POST
,PUT
, etc.Access-Control-Allow-Headers
— Returned in response to a preflight request to specify the HTTP headers that are allowed in the current requestAccess-Control-Allow-Credentials
— Indicates whether the browser should include credentials such as cookies or HTTP authentication in the cross-origin request
CORS allows frontend applications to access resources from a different domain, ensuring secure cross-origin communication. By default, Next.js relies on a same-origin approach, imposing a strict policy. If you want to change that, you must configure it manually.
Why you need CORS with Next.js
Next.js has supported API development since v9.4. As mentioned earlier, APIs in Next.js do not involve CORS headers by default, following a same-origin policy instead. This means they can only be accessed in the browser by pages within the same domain.
However, there are scenarios where you may want to access those endpoints from other origins. For example, if you had an application hosted on a different domain that needed to access those APIs in the frontend, it would be blocked by those cross-origin restrictions.
To avoid that issue, you need to enable CORS by configuring the appropriate headers on the Next.js server. This way, you can explicitly allow cross-origin requests for specific origins. Applications hosted on the trusted domains will then be able to make requests to your Next.js API routes.
Let’s now learn how to configure CORS in Next.js!
Prerequisites for learning to configure CORS in Next.js
Before moving to the next section, be sure to meet the prerequisites below:
- Node.js v18+ installed on your machine
- A Next.js v13+ app
If your Next.js application is older than the recommended version, you can upgrade it by following the official migration guide.
To follow this tutorial, it does not matter if your project is based on the Pages Router or the new App Router. The proposed solutions will work in both scenarios.
From now on, we will assume that all API routes in your Next.js project are under the /api
path.
Enabling CORS for all API routes
The most common approach to CORS is to set it up globally on all API endpoints. There are many ways to achieve that in Next.js. Here, you will explore three methods for using CORS in Next.js.
Using the headers
config
You can manually set the CORS headers in Next.js thanks to the headers
key in next.config.js
:
// next.config.js /** @type {import('next').NextConfig} */ const nextConfig = { async headers() { return [ { // matching all API routes source: "/api/:path*", headers: [ { key: "Access-Control-Allow-Credentials", value: "true" }, { key: "Access-Control-Allow-Origin", value: "*" }, // replace this your actual origin { key: "Access-Control-Allow-Methods", value: "GET,DELETE,PATCH,POST,PUT" }, { key: "Access-Control-Allow-Headers", value: "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" }, ] } ] } } module.exports = nextConfig
headers()
is an async
function that must return an array of objects. When the incoming request matches the source
path regex, the server will produce a response containing the custom HTTP headers
.
Note that the source
must follow a specific syntax. If an incorrect path regex is used, Next.js will throw an Invalid header found
error and fail with a `source` parse failed
message.
Using a middleware
Next.js middleware enables you to perform specific operations before a request is completed. This also includes setting the CORS HTTP headers in the response. Based on your project structure, initialize a middleware.js
file inside the pages
, app
, or the src
folder as shown below:
// src/middleware.js // or // src/app/middleware.js // or // src/pages/middleware.js import { NextResponse } from "next/server"; export function middleware() { // retrieve the current response const res = NextResponse.next() // add the CORS headers to the response res.headers.append('Access-Control-Allow-Credentials', "true") res.headers.append('Access-Control-Allow-Origin', '*') // replace this your actual origin res.headers.append('Access-Control-Allow-Methods', 'GET,DELETE,PATCH,POST,PUT') res.headers.append( 'Access-Control-Allow-Headers', 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version' ) return res } // specify the path regex to apply the middleware to export const config = { matcher: '/api/:path*', }
Make sure to adapt the CORS policy specified in this snippet to your needs.
Note that the matcher
config accepts a regex path, forcing the middleware to run only on specific paths. If an incorrect value is used, Next.js will fail and display an Invalid middleware found
error message.
If your app relies on the App Router, you can remove the config
export and place this file directly in the src/app/api
path. In both cases, the middleware function will be applied to all API endpoints under the /api
path.
Using a Vercel configuration file
Chances are good that your Next.js project is hosted on Vercel, since Vercel is the development team behind Next.js. You can configure the CORS headers in a Next.js app deployed on Vercel by initializing a vercel.json
file in the root folder of your project like so:
{ "headers": [ { "source": "/api/(.*)", "headers": [ { "key": "Access-Control-Allow-Credentials", "value": "true" }, { "key": "Access-Control-Allow-Origin", "value": "*" }, { "key": "Access-Control-Allow-Methods", "value": "GET,DELETE,PATCH,POST,PUT" }, { "key": "Access-Control-Allow-Headers", "value": "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" } ] } ] }
As you can see, the headers
key has a similar syntax to the equivalent one in next.config.js
. What changes is the format of the source
field.
Alternatively, you can also set up CORS with the following Vercel serverless function:
const enableCors = fn => async (req, res) => { res.setHeader('Access-Control-Allow-Credentials', true) res.setHeader('Access-Control-Allow-Origin', '*') // replace this your actual origin res.setHeader('Access-Control-Allow-Methods', 'GET,DELETE,PATCH,POST,PUT') res.setHeader( 'Access-Control-Allow-Headers', 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version' ) // specific logic for the preflight request if (req.method === 'OPTIONS') { res.status(200).end() return } return await fn(req, res) } const handler = (req, res) => { const currentDate = new Date() res.end(currentDate.toString()) } module.exports = enableCors(handler)
Et voilà! You now know how to use CORS on all your Next.js API endpoints!
CORS headers in action
Now, assume your frontend app needs to perform a GET
API call to /api/hello-world
. This is what the preflight request will look like:
By the 2xx
HTTP status code, you can tell that the preflight request was performed successfully. The browser is now allowed to execute the actual cross-origin request:
Focus on the CORS headers in the “Response Headers” section. They match the ones defined in the approaches above.
General tips for using CORS in Next.js
Before wrapping up, there are some CORS Next.js tips and tricks you should know.
Reading the ALLOW
header values from the env variables
CORS policies may change over time. Thus, you should not hard-code them. Instead, you can set up some environment variables as shown below:
ACCESS_CONTROL_ALLOW_CREDENTIALS="true" ACCESS_CONTROL_ALLOW_ORIGIN="*" ACCESS_CONTROL_ALLOW_METHODS="GET,OPTIONS,PATCH,DELETE,POST,PUT" ACCESS_CONTROL_ALLOW_HEADERS="X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version"
Then, use them in your code:
res.headers.append('Access-Control-Allow-Credentials', process.env.ACCESS_CONTROL_ALLOW_CREDENTIALS) res.headers.append('Access-Control-Allow-Origin', process.env.ACCESS_CONTROL_ALLOW_ORIGIN) // replace this your actual origin res.headers.append('Access-Control-Allow-Methods', process.env.ACCESS_CONTROL_ALLOW_METHODS) res.headers.append('Access-Control-Allow-Headers', process.env.ACCESS_CONTROL_ALLOW_HEADERS)
Great! The CORS definition logic no longer involves hard-coded strings.
Allowing multiple CORS origins
The Access-Control-Allow-Origin
header is a binary option that accepts either a single origin or all origins. You would use an asterisk *
to set this header to accept all domains, but this wildcard cannot be used when credentials are included in the request.
If you want to permit multiple but not all CORS origins, you need to write custom logic. Keep in mind that you can only implement this approach with the middleware solution. The other two options for enabling CORS in Next.js involve static files that do not accept custom behavior.
Update the middleware.js
file as below:
import { NextResponse } from "next/server"; // the list of all allowed origins const allowedOrigins = [ 'http://localhost:3000', 'https://example-1.com', 'https://example-2.com', // ... 'https://example-99.com', ]; export function middleware(req) { // retrieve the current response const res = NextResponse.next() // retrieve the HTTP "Origin" header // from the incoming request req.headers.get("origin") // if the origin is an allowed one, // add it to the 'Access-Control-Allow-Origin' header if (allowedOrigins.includes(origin)) { res.headers.append('Access-Control-Allow-Origin', origin); } // add the remaining CORS headers to the response res.headers.append('Access-Control-Allow-Credentials', "true") res.headers.append('Access-Control-Allow-Methods', 'GET,DELETE,PATCH,POST,PUT') res.headers.append( 'Access-Control-Allow-Headers', 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version' ) return res } // specify the path regex to apply the middleware to export const config = { matcher: '/api/:path*', }
Basically, all you have to do is check whether the current Origin
header sent by the browser matches one of those allowed. If so, specify the Origin
variable in the appropriate CORS header to enable cross-origin requests from it.
Enabling CORS only for a specific endpoint
Suppose you want to define a particular CORS policy for the /api/special-data
endpoint. To do so, you could add a new entry to the array returned by headers()
in next.config.js
:
// next.config.js /** @type {import('next').NextConfig} */ const nextConfig = { async headers() { return [ { // matching all API routes source: "/api/:path*", headers: [ // omitted for brevity... ] }, { source: "/api/special-data", headers: [ { key: "Access-Control-Allow-Credentials", value: "false" }, { key: "Access-Control-Allow-Origin", value: "https://example.com" }, { key: "Access-Control-Allow-Methods", value: "GET,DELETE,PATCH,POST,PUT" }, { key: "Access-Control-Allow-Headers", value: "Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date" }, ] } ] } } module.exports = nextConfig
Note that the source
field of the second entry matches the endpoint of the desired API. This means that Next.js will prefer it to the more generic first entry.
Otherwise, you could update middleware.js
and adapt its logic based on the request’s pathname:
import { NextResponse } from "next/server"; export function middleware(req) { // retrieve the current response const res = NextResponse.next() // if the incoming is for the desired API endpoint if (req.nextUrl.pathname === '/special-data') { res.headers.append('Access-Control-Allow-Credentials', "false") res.headers.append('Access-Control-Allow-Origin', 'https://example.com') res.headers.append('Access-Control-Allow-Methods', 'GET,DELETE,PATCH,POST,PUT') res.headers.append('Access-Control-Allow-Headers', 'Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date') } else { // generic CORS policy omitted for brevity.... } return res } // specify the path regex to apply the middleware to export const config = { matcher: '/api/:path*', }
If you are an App Router user, you can get the same result by creating a middleware.js
file in the src/app/special-data
folder.
Fantastic! The single-origin restriction is no longer an issue!
Conclusion
In this article, you learned what CORS is and why it is so important to use it in backend applications. More specifically, we took a look at the most critical CORS headers and how to set them in Next.js.
As you saw here, there are several ways to deal with CORS in Next.js. They all just involve some lines of code, so it does not take much to put a security policy in place.
Thanks to the Next.js capabilities, configuring CORS has never been easier. Make your site more secure today!