Running into CORS issue in my nextjs project

Hello,

My app is working fine in my local and in vercel prod.

However in preview env, i am running into CORS issue since my api url is different from preview url.
My preview URL changes each time I merge my code with deploy branch so I cannot whitelist specific URL

Error in my browser console
"Access to XMLHttpRequest at 'https://www.tennisduo.com/api/...' from origin 'https://tennisduo-7vjgavgu4-sgudipati2s-projects.vercel.app' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource."

I have done below 3 changes but my issue still exists

  1. 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: '*' },
          {
            key: 'Access-Control-Allow-Methods',
            value: 'GET,OPTIONS,PATCH,DELETE,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
  1. vercel.json
{
  "headers": [
    {
      "source": "/api/(.*)",
      "headers": [
        { "key": "Access-Control-Allow-Credentials", "value": "true" },
        { "key": "Access-Control-Allow-Origin", "value": "*" },
        {
          "key": "Access-Control-Allow-Methods",
          "value": "GET,OPTIONS,PATCH,DELETE,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"
        }
      ]
    }
  ]
}
  1. middleware.js
import { NextResponse } from 'next/server'

export function middleware(request) {
  // Handle simple requests
  const response = NextResponse.next()

  // Add the CORS headers to the response
  response.headers.set('Access-Control-Allow-Origin', '*')
  response.headers.set(
    'Access-Control-Allow-Methods',
    'GET, POST, PUT, PATCH, DELETE, OPTIONS'
  )
  response.headers.set(
    'Access-Control-Allow-Headers',
    'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version'
  )

  return response
}

export const config = {
  matcher: '/api/:path*'
}

Hey, @sgudipati2! Great to have you join us in the Vercel Community :smile:

The CORS issue in your preview environment stems from the mismatch between your API URL and the preview URL. The solution lies in configuring CORS on your API server (tennisduo.com) rather than in your preview deployment.

You could try removing all CORS configurations from your preview deployment files. Instead, modify the next.config.js on your API server to allow requests from Vercel preview URLs by setting the Access-Control-Allow-Origin header to https://*.vercel.app . Make sure your API server correctly handles OPTIONS preflight requests.

Another thing you can try is using environment variables for your API URL to avoid cross-origin requests in preview deployments. Set NEXT_PUBLIC_API_URL appropriately for each environment (local, preview, production).

From a security standpoint, remember to use more specific patterns for allowed origins rather than wildcards when possible.

Let us know how you get on!

I wanted to loop back in to share our latest community post that may be helpful with CORS issues:

This is most likely the reason:
Vercel Authentication breaks your CORS flow

Browser (Preview domain)
│ GET https://preview-domain

Vercel edge layer (Deployment-Protection 401 page)
│ ──► 401 HTML
│ (no Access-Control-Allow-Origin header)

Browser shows: “blocked by CORS policy…”

Vercel’s Password / SSO protection lives in front of your functions.
If the request is unauthenticated the platform returns its own 401 page and finishes the response
before it ever hits your app, so your cors() middleware never runs.
The _vercel_sso_nonce cookie that unlocks the preview is issued for the exact host you opened in the browser (preview domain). It is not valid for your API sub-domain, so every XHR still looks unauthenticated and gets the 401 page without CORS headers.

You can remove the protection from setting, and deployment protection, which opens up your preview to everyone, remove CORS from your preview, upgrade and do password protection or use some methods in your code to pass automation key and keep the vercel protection on but that automation key is passed in preview as env variable and can fix your 401-error problem

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.