Access to XMLHttpRequest at URL from ORIGIN has been blocked by CORS policy

Stack:

NodeJS + Express + Firebase - BE

VueJS, Vuex, Axios (for api calls) - FE

Error I am constantly getting from dev & production environment:

Access to XMLHttpRequest at 'https://www.url.com/api/do-something' from origin 'http://localhost:8081' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.

Good to note: I am using new netlify function to “host” my BE APIs.

BE Code (server.js):

  const express = require('express');
    const bodyParser = require('body-parser');
    const rateLimit = require('express-rate-limit');
    const cors = require('cors');
    const app = express();
    const router = express.Router();
    const admin = require('firebase-admin');
    const serverless = require('serverless-http');
    require('dotenv').config({path: __dirname + '/server.env'});
    
    const port = 8002;
    
    // Firebase config
    ...
    
    const serviceAccount = require('../secret_path.json');
    
    // Init the firebase app
    admin.initializeApp({
      credential: admin.credential.cert(serviceAccount),
    });
    const db = admin.firestore();
   
    const limiter = rateLimit({
      windowMs: 60 * 60 * 1000,
      max: 3, // max requests
      message: 'Too many requests to this endpoint. Please try again later.'
    });
    
    // Cors
    const corsOptions = {
      origin: '*',
      credentials: true,
      optionSuccessStatus: 200
    }
    app.use(cors(corsOptions))
    
    app.use(bodyParser.json());
    app.use(express.urlencoded({ extended: true }));
    app.set('trust proxy', 1);
    
    // Set the main route
    router.get('/api', (request, res) => {
      res.send(`Beginning`);
    });
    
    // POST - Do something fun
    router.post('/api/do-something', limiter, async (req, res) => {
      const data = {
        something: req.body.something,
      };
    
      try {
        // if successfull:
        return res.status(201).json({
          success: true,
          msg: 'Good'
        });
      } catch (err) {
        return res.status(400).json({
          success: false,
          msg: `Error`
        });
      }
    });
    
    app.use('/', router);
    
    app.listen(port);
    
    module.exports = app;
    module.exports.handler = serverless(app);

That is pretty much it for the BE part.
For the front-end part, I am handling the do-something API like so:

async coolEndpoint({ state, commit }, payload) {
          let result;
          try {
            result = await axios({
              headers: {
                'Access-Control-Allow-Origin': '*',
                Accept: 'application/json',
                'Content-Type': 'application/json'
              },
              url: 'https://www.url.com/api/do-something',
              data: {
                something: payload.something
              },
              method: 'POST'
            });
          } catch (err) {
            console.error(err);
          }
          return result;
        }

And calling it on the FE through Vuex helpers (not relatable for this).

Also have setup redirects:

[build]
  base = "/"
  publish = "/dist"
  functions = "functions"
  command = "npm install && npm run build"

[dev]
  port = 8081
  publish = "/server"
  command = "npm run start"

[[redirects]]
  from = '/api/*'
  to = '/.netlify/functions/server/:splat'
  force = true
  status = 200

[functions]
  directory = "functions"

From dev & prod environments, I am calling the API url directly from where it is hosted, which netlify handles. Ex: https://www.url.com/api/do-something (my domain)

And no matter from where do I call it, localhost or production through the same domain, it is constantly throwing the CORS error.

I also did setup a proxy in vue.config file like a so proxy: 'https://www.url.com/'.

Really could use some help as I’ve been struggling with this for month and a half (lol), and If any other info is needed I am here to edit and add.

This won’t make a difference, this header needs to be sent by the server, not the client.

You should take a look at setting up a rewrite:

Hey @hrishikesh thanks for the answer. I tried setting up a rewrite like so:

from = '/api/*'
to = 'https://www.url.com/:splat'
force = true 
status = 200

But no luck. I already have a similar block of code in .toml file:

from = '/api/*'
to = '/.netlify/functions/server/:splat'
force = true
status = 200

Could this block of code be the reason why I am constantly getting the CORS error saying that Redirects are not allowed for a preflight request?

Unsure.

If you already have that, then the new one would not work. You can only 1 redirect that would affect a path and the one on the top takes highest preference.

if you already have that rewrite to Netlify Functions in place, what exactly do you need the url.com rewrite for? Are you get CORS issues from Netlify Functions?

Yes. I am hitting my hosted netlify function url like this: https://www.url.com/api/do-something from the front end using axios.

And as for my BE, I create the endpoint like this: router.post('/api/do-something', limiter, async (req, res)...

It works on Postman when I’m testing it, and everything goes successfully. But as for the dev environment & production, it constantly throws the above mentioned CORS error…

Is there a specific reason for that? Are you hosting your Netlify Functions on a different site than your front end? If not, you could simply use relative URLs like /api/ and it should work.

Could you share your site name or ID? It could remove a lost of guess work from the thread.

Is there a specific reason for that? Are you hosting your Netlify Functions on a different site than your front end?

Netlify functions & front end are all together on one domain. What do you exactly mean by “Using relative URLs like /api/”?. I mean that’s what I am kinda doing right now on the FE.

App ID: 00d1fed4-8caa-4da9-9edd-8258179eb0a3

Then I don’t see why you’d get a CORS error from your own domain. But, I’ll report back after I check the site.

How do I trigger the function? I can see there’s only 1 page on that website with nothing that seems to trigger your function.

Clicking the GET NOTIFIED button and submitting the email on the input field.

My ad blocker was not showing me the pop-up. But yeah, coming back to the actual problem:

This is not a typical CORS error:

I think your backend is sending a 301 for this request and thus the issue. Is the code that you’ve shared above the latest one at the moment? If possible could you share your repo?

Yes, this is the latest code from the project. I am unsure how can BE be sending 301, as I specifically inserted status codes that should be returned both on success and error.

I kinda can share the repo but it is locked by organization on GitHub, so I will need to see to it, what would be the best way to give you the access and hence checking the repository.

If you can’t make it public, I think you can add Hrishikesh-K to the repo on GitHub.

1 Like

Hey @AndreasDeveloper,

I was checking the repository and from my understanding, this seems to be overly complicated for a small task. Feel free to correct me if that’s not what you think.

To start with the most important things, you’re using netlify-lambda, and in recent times, it’s not required anymore. So to me, it appears like you’re building the function locally and sending it over, but doing so is not required. You could simply save the source code of the function in the functions directory and Netlify would automatically build and bundle the function for you. Furthermore, I don’t know how your workflow is, but looks like with your current setup, you have to build your function locally first before deploying and even then, you need to publish your built function to your repo. That’s something you can avoid.

Now coming back to the actual code, I spent quite some time trying to read more about this and understand what your code is doing. It appears to me that you’re simply trying to check the existence of an email in your Firebase database and add it if it doesn’t exist. Adding Express for this task is adding complications in my opinion. The only thing I was worried about was your rate limiting functionality. About that I’ve two points:

  1. If you move away from Express, you can still refer to this: Simple Rate-Limiting for Netlify/Vercel Functions - lihbr to add rate-limiting.
  2. I don’t think it’s working or if it’s even reliable to rate-limit this way. I was able to reach the /api endpoint multiple times without issues.

However, if you have other requirements for which Express is a must, let us know and we can see what needs to be done, but if the above understanding is the only requirement, I think Express is an overkill.

For example, I was sending emails and logging the data to a database just recently and this was the code:

import {Client} from 'faunadb'
import {Collection, Create} from 'faunadb/query'
import {SMTPClient} from 'emailjs'
export async function handler({body}) {
  const {email, message, name} = JSON.parse(body)
  const {FAUNA, SMTP_PASSWORD, SMTP_RECEIVER, SMTP_USERNAME} = process.env
  return new Client({
    secret: FAUNA
  }).query(Create(Collection('Submissions'), {
    data: {email, message, name}
  })).then(() => {
    return new SMTPClient({
      host: 'smtp-relay.sendinblue.com',
      password: SMTP_PASSWORD,
      port: 587,
      tls: true,
      user: SMTP_USERNAME
    }).sendAsync({
      from: `${name} <${email}>`,
      'reply-to': `${name} <${email}>`,
      subject: 'New form submission',
      text: message,
      to: SMTP_RECEIVER
    }).then(() => {
      return {
        body: JSON.stringify({
          success: true
        }),
        statusCode: 200
      }
    }).catch(error => {
      return {
        body: JSON.stringify({
          error,
          success: false
        }),
        statusCode: 500
      }
    })
  })
}

It uses a different tech stack than yours, but does quite what your function is doing.

Hey @hrishikesh

This is great and I love the effort that you put into this. I am using express as this will grow to a very complex BE system in near future. And from my personal experience I know that express with node can very well handle it.

Question is, how did you hit the API successfully? By removing the limiter?
I would like to be able to fix the CORS error with the current stack, which is node with express. If fixing the error means changing the whole stack, than I believe I am in a slump.

As for the other points, for example netlify-lambda, yes, that is a good point and I will change that system soon as I fix this goddamn CORS issue. Thanks a lot for the support.

At present, I was simply calling /api/ in the browser and not actually requesting the API through the Frontend, to try and see where the error is coming from. I haven’t deployed the app anywhere yet, so your latest deploy is what I was testing. So, from that I could see that the rate limiting is not really working.

So if Express is a hard-wired requirement, I’ll do some further digging.

This appears to be an easy fix. Apparently, the answer was here:

Just changed the following from your ./src/store/index.js:

url: '/api/save-newsletter-email/',

Also removed the header: {...} as like I said that wasn’t doing any good, but you can keep it if needed. I don’t get CORS here: https://infallible-meninsky-9ca520.netlify.app/

1 Like