CORS Issue when deploying on Vercel

I have an express.ts (typescript) backend.

When I test an endpoint locally, ‘Access-Control-Allow-Origin’ header is present.
When I deploy it on Vercel, I get the error:

No ‘Access-Control-Allow-Origin’ header is present on the requested resource

I have no idea why, and I am completely blocked on that, I did:

  const corsOptions = {
    origin: config.frontEndUrl, // I checked that is correct on vercel
    credentials: true, // access-control-allow-credentials:true
    optionSuccessStatus: 200,
    methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
    allowedHeaders: "Content-Type, Authorization",
  };

  app.use(cors(corsOptions));

Here Here is my repo.

Here is my vercel.json:

{
  "version": 2,
  "builds": [
    {
      "src": "dist/index.js",
      "use": "@vercel/node",
      "config": { "includeFiles": ["dist/**"] }
    }
  ],
  "rewrites": [{ "source": "/(.*)", "destination": "/api" }]
}

If you’re having trouble deploying an Express app, this guide can help.

You can also ask v0 for suggestions tailored to your own project setup.

Hi @lauriane-open-source, welcome to the Vercel Community!

Thanks for sharing the details and the repository. Let me try and recreate this issue.

1 Like

I also tried what VO suggested me, it is on this branch.

But the result is still the same…

Here are the main changes:

const corsOptions = {
  origin: config.frontEndUrl,
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  optionsSuccessStatus: 200,
};

app.use(cors(corsOptions));
{
  "version": 2,
  "builds": [
    {
      "src": "api/index.ts",
      "use": "@vercel/node"
    }
  ],
  "routes": [
    {
      "src": "/(.*)",
      "dest": "/api/index.ts"
    }
  ]
}

And even add middleware…

import { Request, Response, NextFunction } from 'express';
import { config } from '../your-config-file-path'; // Adjust this import as needed

export function corsMiddleware(req: Request, res: Response, next: NextFunction) {
  res.setHeader('Access-Control-Allow-Origin', config.frontEndUrl);
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  res.setHeader('Access-Control-Allow-Credentials', 'true');

  if (req.method === 'OPTIONS') {
    return res.status(200).end();
  }

  next();
}

Hi @lauriane-open-source, thanks for the update.

Can you share the URLs where I can confirm the behavior? For example, the frontend URL that connects to any of the endpoints which isn’t working as you expect it to.

I have 2 methods for testing:

First

curl -v --request OPTIONS 'https://dev-web2-backend.vercel.app/api/v1/auth/status' -H 'Origin: https://dev-web2-backend.vercel.app/api/v1/auth/status' -H 'Access-Control-Request-Method: GET'

VS

curl -v --request OPTIONS 'http://localhost:3001/api/v1/auth/status' -H 'Origin: http://localhost:3001/api/v1/auth/status' -H 'Access-Control-Request-Method: GET' 

And then, on vercel, I deployed my frontend that is calling the same end-point,

1 Like

@anshumanb I think I spotted the issue.

On main deployment (in sync with dev branch on github), it works.

If I do:

curl -v --request OPTIONS 'https://api.open-source-economy.com/api/v1/auth/status' -H 'Origin: https://api.open-source-economy.com/api/v1/auth/status' -H 'Access-Control-Request-Method: GET'

It returns:

< access-control-allow-credentials: true
< access-control-allow-headers: Content-Type, Authorization
< access-control-allow-methods: GET,HEAD,PUT,PATCH,POST,DELETE
< access-control-allow-origin: http://localhost:3000/
< cache-control: public, max-age=0, must-revalidate

So it is working. But on my dev branch and dev vercel deployment, the access control headers are not included…

Do you know how to fix that?

1 Like

In the last reply,

curl -v --request OPTIONS 'https://api.open-source-economy.com/api/v1/auth/status' -H 'Origin: https://api.open-source-economy.com/api/v1/auth/status' -H 'Access-Control-Request-Method: GET'

Both the URLs seem to be of the same origin, so I’m not sure if that’s what you were aiming for.

Also, why does it return access-control-allow-origin: http://localhost:3000/ on the main deployment. Shouldn’t this be the domain of the frontend?

When I do that (wich is a preview deployment)

curl -v --request OPTIONS 'https://dev-web2-backend.vercel.app/api/v1/auth/status' -H 'Origin: https://dev-web2-backend.vercel.app/api/v1/auth/status' -H 'Access-Control-Request-Method: GET'

I do not not see any access-control-allow-XXXX and that is my problem.

I have them in the production deployment (and that works, if I will change the http://localhost:3000/ to the correct one), but not on the preview, I do have the access-control-allow-XXXX headers.

Why is so?