Different ways to handle CORS on Vercel

Cross-Origin Resource Sharing (CORS) issues are common when you are hosting your backend and frontend applications on different origins. Browsers implement the CORS mechanism as a safeguard to prevent applications on a different origin from loading your resources. This is why it is important to set up your backend application with the correct CORS configuration.

Getting Started


When using Vercel, you can configure CORS headers in the following ways:

  • Using the Vercel project configuration
  • Using the Edge Middleware
  • Using framework configuration
  • Route handler configuration

This post uses the code snippets from the cors-example GitHub repository, which showcases working examples of CORS configuration in popular web frameworks such as Next.js, Nuxt, Astro, and Flask deployed on Vercel.

Using the Vercel project configuration

You can use the headers configuration option in the vercel.json file of your project to add CORS headers. For example, the following configuration will add the CORS headers to all responses from the /vercel-config-cors endpoint.

{
  "headers": [
    {
      "source": "/vercel-config-cors",
      "headers": [
        {
          "key": "Access-Control-Allow-Origin",
          "value": "https://rrv7.vercel.app"
        },
        {
          "key": "Access-Control-Allow-Methods",
          "value": "GET, POST, PUT, DELETE, OPTIONS"
        },
        {
          "key": "Access-Control-Allow-Headers",
          "value": "Content-Type, Authorization"
        }
      ]
    }
  ]
}

Using the Edge Middleware

The Edge Middleware provides a fast and efficient way to modify headers on the requests and responses of your applications. For example, the middleware.ts from an Astro project adds the appropriate CORS headers to the requests coming to the /mw-cors endpoint without changing the endpoint handler code. To view the complete code, see this GitHub repository.

import { next } from "@vercel/edge";

const allowedOrigins = ["https://rrv7.vercel.app"];

const corsOptions = {
  "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
  "Access-Control-Allow-Headers": "Content-Type, Authorization",
};

export default function middleware(request: Request) {
  // Check the origin from the request

  const origin = request.headers.get("origin") ?? "";
  const isAllowedOrigin = allowedOrigins.includes(origin);

  // Handle preflighted requests
  const isPreflight = request.method === "OPTIONS";

  if (isPreflight) {
    const preflightHeaders = {
      ...(isAllowedOrigin && { "Access-Control-Allow-Origin": origin }),
      ...corsOptions,
    };

    return Response.json({ preflight: true }, { headers: preflightHeaders });
  }

  // Handle simple requests
  const response = new Response();

  if (isAllowedOrigin) {
    response.headers.set("Access-Control-Allow-Origin", origin);
  }

  Object.entries(corsOptions).forEach(([key, value]) => {
    response.headers.set(key, value);
  });

  return next(response);
}

export const config = {
  matcher: "/mw-cors",
};

Using framework configuration

Many frameworks such as Next.js and Nuxt provide easy APIs to add CORS headers from the config file. For example, the following nuxt.config.ts sets routeRules for a specific route (/api/nuxt-config-cors) to send the CORS headers automatically for all the requests made to this endpoint.

export default defineNuxtConfig({
  compatibilityDate: "2024-11-01",
  devtools: { enabled: true },
  routeRules: {
    "/api/nuxt-config-cors": {
      headers: {
        "Access-Control-Allow-Origin": "https://rrv7.vercel.app",
        "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
        "Access-Control-Allow-Headers": "Content-Type, Authorization",
      },
    },
  },
});

Route handler configuration

At last, you can always return the CORS headers from the route handler response. For example, the following Next.js route handler adds the headers to the endpoint response.

export async function GET() {
  return new Response("Hello, Next.js!", {
    status: 200,
    headers: {
      "Access-Control-Allow-Origin": "https://rrv7.vercel.app",
      "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
      "Access-Control-Allow-Headers": "Content-Type, Authorization, X-Custom-Header",
    },
  });
}

Troubleshooting common issues


Incorrect allow origin header

The Access-Control-Allow-Origin lets the server define which origin is allowed to send a cross-origin request. It is important to ensure that the origin value must matches all three aspects: scheme, hostname, and port of the incoming request’s Origin header.

Incorrect HTTP methods

The Access-Control-Allow-Methods header tells the browser about the HTTP methods that the server supports. For example, it can become an issue when your frontend application is making an HTTP request that triggers a Preflight request and you don’t support the OPTIONS request method, which is used by browsers to check if CORS is supported.

Missing allowed headers

It’s common to overlook the additional headers or custom headers that your frontend application might be sending along with the requests to your backend. The Access-Control-Allow-Headers header lets your server define a list of headers that are allowed for cross-origin requests.

Missing credentials header

By default, CORS doesn’t allow cookies with cross-origin requests, which will lead to errors in your application. To allow credentials to be sent with your requests you need to:

  1. Allow credentials on CORS in your server code by sending the Access-Control-Allow-Credentials header. For example, the following Flask application specifies supports_credentials=True to allow sending credentials.

    from flask import Flask
    from flask_cors import CORS
    
    app = Flask(__name__)
    CORS(app, supports_credentials=True, resources={r"/api/cors": {"origins": "https://rrv7.vercel.app"}})
    
  2. Update your fetch call to include credentials with the request, as follows:

        const response = await fetch(url, {
          method: 'GET',
          credentials: 'include', // This tells fetch to include credentials
          headers: {
            'Content-Type': 'application/json',
          }
        });
    

Resources


4 Likes