I\'m not able to correctly handle CORS issues when doing either PATCH
/POST
/PUT
requests from the browser sending an Authorization
I faced a similar problem and the problem was solved byadding the header to the routes as follows:
"routes": [
{
"src": ".*",
"methods": ["GET", "POST", "OPTIONS"],
"headers": {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept",
"Access-Control-Allow-Credentials": "true"
},
"dest": "index.js",
"continue": true
},
{
"src": "/user/login", "methods": ["POST"], "dest": "index.js"
}
]
remember to add continue: true
.
https://github.com/super-h-alt/zeit-now-cors-problems/blob/master/now.json
I was in pretty much the same situation. I have a couple of serverless functions in Vercel (Now) and I wanted them to be available to anyone at any origin. The way I solved is similar to @illiteratewriter's answer.
First, I have the following now.json
at the root of my project:
{
"routes": [
{
"src": "/api/(.*)",
"headers": {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept",
"Access-Control-Allow-Credentials": "true"
},
"continue": true
},
{
"src": "/api/(.*)",
"methods": ["OPTIONS"],
"dest": "/api/cors"
}
]
}
Here is a breakdown of the two routes configurations:
{
"src": "/api/(.*)",
"headers": {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept",
"Access-Control-Allow-Credentials": "true"
},
"continue": true
}
"src": "/api/(.*)"
Matches any request going to /api/*
.
"headers": [...]
Applies the CORS headers to the route, indicating that CORS is allowed.
"continue": true
Continues looking for other route matches after applying the CORS headers. This allows us to apply the CORS headers to all routes, instead of having to do it per-route. For example, now /api/auth/login
and /api/main/sub/resource
will both have the CORS headers applied.
{
"src": "/api/(.*)",
"methods": ["OPTIONS"],
"dest": "/api/cors"
}
What this config does is to intercept all HTTP/OPTIONS
requests, which is the CORS pre-flight check, and re-route them to a special handler at /api/cors
.
The last point of the routes configuration breakdown leads us to the /api/cors.ts
function. The handler looks like this:
import {NowRequest, NowResponse} from '@now/node';
export default (req: NowRequest, res: NowResponse) => {
return res.status(200).send();
}
What this handler does is basically accept the CORS pre-flight OPTIONS
request and respond with 200/OK
to it, indicating to the client "Yes, we are open for CORS business."
The accepted answer did not work for me. However vercel now appear to have updated their advice, with their example code being:
const allowCors = fn => async (req, res) => {
res.setHeader('Access-Control-Allow-Credentials', true)
res.setHeader('Access-Control-Allow-Origin', '*')
// another option
// res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
res.setHeader('Access-Control-Allow-Methods', 'GET,OPTIONS')
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'
)
if (req.method === 'OPTIONS') {
res.status(200).end()
return
}
return await fn(req, res)
}
const handler = (req, res) => {
const d = new Date()
res.end(d.toString())
}
module.exports = allowCors(handler)
It's worth saying I'm not entirely sure of the difference between res.end
and res.send
but to actually ingest the response into my front end (React) I changed the handler
function to:
const handler = (req, res) => {
const d = {data: "Hello World"};
res.send(d)
}
which allowed me to ingest in React as:
function getAPIHelloWorld () {
let connectStr = "/api"
fetch(connectStr)
.then(response => response.json())
.then(response => {console.log(response.data)})
.catch(err => console.error(err))
}
I was able to bypass this issue using micro-cors.
I checked its code and it doesn't differ that much of what I attempted to do myself by using res.setHeader
manually, probably missed something I guess.
Nevertheless I don't understand why the settings in now.json
were not working correctly and I need to perform this manually in the serverless function.
Anyways, in case someone else finds this post, I ended up with something like this:
import micro from "micro-cors";
function MyApi(req, res) {
if (req.method === "OPTIONS") {
return res.status(200).end();
}
// handling other requests normally after this
}
const cors = micro();
export default cors(MyApi);
I'll probably will try again with a self-written solution in order to understand better what went wrong and also because I don't want an extra dependency.
Will update this answer if I do that.
Edit: After checking a bit deeper I found that another issue was the library express-jwt
specifically changing the res
object when the jwt
parse failed.
I had a small middleware that was breaking everything by doing:
await authValidateMiddleware(req, res);
When that await
failed, it broke everything down the line because express-jwt
changed the res
headers unknowingly (setting the error) and then I tried to set the res
headers manually trying to handle the error correctly myself, therefore throwing issues about "changing the res
headers more than once".
I have pretty much similar issues with CORS and Vercel serverless function.
After lots of try → failed process I just found solutions for this.
The simplest solution, just using micro-cors.
And having an implementation something like;
import { NowRequest, NowResponse } from '@now/node';
import microCors from 'micro-cors';
const cors = microCors();
const handler = (request: NowRequest, response: NowResponse): NowResponse => {
if (request.method === 'OPTIONS') {
return response.status(200).send('ok');
}
// handle incoming request as usual
};
export default cors(handler);
using vercel.json
to handle request headers
vercel.json
{
"headers": [
{
"source": "/.*",
"headers": [
{
"key": "Access-Control-Allow-Origin",
"value": "*"
},
{
"key": "Access-Control-Allow-Headers",
"value": "X-Requested-With, Access-Control-Allow-Origin, X-HTTP-Method-Override, Content-Type, Authorization, Accept"
},
{
"key": "Access-Control-Allow-Credentials",
"value": "true"
}
]
}
]
}
After self tried, there are 2 keys important in an above setting,
Access-Control-Allow-Origin
as what you wantAccess-Control-Allow-Headers
you must include Access-Control-Allow-Origin
into its value.then in serverless function, you still need to handle pre-flight request as well
/api/index.ts
const handler = (request: NowRequest, response: NowResponse): NowResponse => {
if (request.method === 'OPTIONS') {
return response.status(200).send('ok');
}
// handle incoming request as usual
};
I would suggest to read through the code in micro-cors, it's very simple code, you can understand what it'll do in under few minutes, which makes me didn't concern about adding this into my dependency.