ChatGPT解决这个技术问题 Extra ChatGPT

Enabling CORS in Cloud Functions for Firebase

I'm currently learning how to use new Cloud Functions for Firebase and the problem I'm having is that I can't access the function I wrote through an AJAX request. I get the "No 'Access-Control-Allow-Origin'" error. Here's an example of the function I wrote:

exports.test = functions.https.onRequest((request, response) => {
  response.status(500).send({test: 'Testing functions'});
})

The function sits in this url: https://us-central1-fba-shipper-140ae.cloudfunctions.net/test

Firebase docs suggests to add CORS middleware inside the function, I've tried it but it's not working for me: https://firebase.google.com/docs/functions/http-events

This is how I did it:

var cors = require('cors');    

exports.test = functions.https.onRequest((request, response) => {
   cors(request, response, () => {
     response.status(500).send({test: 'Testing functions'});
   })
})

What am I doing wrong? I would appreciate any help with this.

UPDATE:

Doug Stevenson's answer helped. Adding ({origin: true}) fixed the issue, I also had to change response.status(500) to response.status(200) which I completely missed at first.

Also a sample in the docs here
I have some functions that work with the solution provided but now am trying a new function which essentially adds open graphs to the top of my index.html and returns the updated index.html and I can't get it to work :( keep getting the ACCESS-CONTROL--- error
wrapping the incoming request in cors() like above was the only thing that worked for me
can you edit your "update" to underline that the cors middleware is required? This will save some people some time

D
Doug Stevenson

There are two sample functions provided by the Firebase team that demonstrate the use of CORS:

Time server with date formatting

HTTPS endpoint requiring Authentication

The second sample uses a different way of working with cors than you're currently using.

Consider importing like this, as shown in the samples:

const cors = require('cors')({origin: true});

And the general form of your function will be like this:

exports.fn = functions.https.onRequest((req, res) => {
    cors(req, res, () => {
        // your function body here - use the provided req and res from cors
    })
});

It looks like this is where the whitelist of domains to allow access is defined? And setting origin: true allows any domain to access? (npmjs.com/package/cors) @Doug Stevenson Do you think firebase could write up a doc on the basics needed for client/server https functions? The samples repo is good, but we had missed this extra bit of require.
To anyone willing to add CORS support to their back-ends: please make sure you understand the consequences and how to properly configure it. "origin: true" is cool for testing but it defeats the whole purpose :)
google cloud functions do not allow the wildcard origin: cloud.google.com/functions/docs/writing/…
Just a reminder for everyone still importing cors like Doug brilliantly mentioned, don't forget to wrap the response like Andrey did, otherwise it won't work!
As others have mentioned, can you update your answer to point out that cors middleware is indeed required? If we skim through your answer, we think that the only step needed is to write const cors = require('cors')({origin: true});, but it's not the case
d
deanwilliammills

You can set the CORS in the cloud function like this

response.set('Access-Control-Allow-Origin', '*');

No need to import the cors package


This works perfectly for my case, a cloud function that makes a XHR call to Mailchimp API.
google cloud functions do not allow the wildcard origin: cloud.google.com/functions/docs/writing/…
@CoreyCole I think that's only if you need to add the Authorization header. The above seems to work ok.
I added that line into my cloud function, and my localhost fetch is still blocked by CORS policy.
@elersong check whether you have http rather than https for your localhost.
Y
Yayo Arellano

For anyone trying to do this in Typescript this is the code:

import * as cors from 'cors';
const corsHandler = cors({origin: true});

export const exampleFunction= functions.https.onRequest(async (request, response) => {
       corsHandler(request, response, () => {});
       //Your code here
});

Solution will make you lose logging on cloud functions (very bad) and proper async / await functionality, you risk the function content being prematurely ended inside the callback on long calls.
google cloud functions do not allow the wildcard origin: cloud.google.com/functions/docs/writing/…
@YayoArellano , thanks. Your answer helped me, however I made a slight change: corsHandler(request, response, () => { YOUR CODE HERE });
@OliverDixon do you have a better solution that does not have the downside of possibly risking the function content to being prematurely ended during long calls? Probably wrapping it in a Promise that only resolves once the callback "completed"?
@OliverDixon Can you explain "you risk the function content being prematurely ended inside the callback on long calls." please?
P
Pablo Urquiza

One additional piece of info, just for the sake of those googling this after some time: If you are using firebase hosting, you can also set up rewrites, so that for example a url like (firebase_hosting_host)/api/myfunction redirects to the (firebase_cloudfunctions_host)/doStuff function. That way, since the redirection is transparent and server-side, you don't have to deal with cors.

You can set that up with a rewrites section in firebase.json:

"rewrites": [
        { "source": "/api/myFunction", "function": "doStuff" }
]

imo, this is the best answer, since it solves the actual problem without adding any additional security problems. This way the cloud functions are served from the same domain as the rest and you dont even need any cors.
This is a great feature indeed, but it currently only works if the functions live in the default region (us-central1). I wanted to deploy my functions to europe-west1 for latency reasons and ran into this issue: github.com/firebase/firebase-tools/issues/842
The redirect works fine and makes the URL cleaner, but I haven't figured out how to pass GET parameters. The function (after rewrite) seems to be called without parameters.
using firebase Hosting? if yes, then your cloud function MUST be in "us-central1" region, this is still true in june 2021.
Is there any way to use this rewrite with the httpCallable? Or do we need to resort to normal web functions at that point?
t
tbone849

No CORS solutions worked for me... till now!

Not sure if anyone else ran into the same issue I did, but I set up CORS like 5 different ways from examples I found and nothing seemed to work. I set up a minimal example with Plunker to see if it was really a bug, but the example ran beautifully. I decided to check the firebase functions logs (found in the firebase console) to see if that could tell me anything. I had a couple errors in my node server code, not CORS related, that when I debugged released me of my CORS error message. I don't know why code errors unrelated to CORS returns a CORS error response, but it led me down the wrong rabbit hole for a good number of hours...

tl;dr - check your firebase function logs if no CORS solutions work and debug any errros you have


this drove me crazy. in my case it wasn't even error in code! it was Error: quota exceeded (Quota exceeded for quota group 'NetworkIngressNonbillable' and limit 'CLIENT_PROJECT-1d' of service 'cloudfunctions.googleapis.com so basically free quota was exceeded and functions returned cors error
Happend a couple of times here, same error is returned from the server aswell as cors: Error: internal is basically the error. This error will also happen if you run the wrong function, for example mistyping a function name
When you try to request for Google reCAPTCHA verification within cloud function, the browser throws you the CORS error too. When I check the Firebase Console function log, it says access to external network resources not allowed if the billing account is not enabled. After enabling the billing account, it works perfectly. This is also one of the non-cors related examples but a cors error is thrown.
In my case, the issue was that I wasn't logged in under the correct Firebase project (firebase use <project-name> on the command-line) before running the emulator.
K
Kevin Danikowski

I have a little addition to @Andreys answer to his own question.

It seems that you do not have to call the callback in the cors(req, res, cb) function, so you can just call the cors module at the top of your function, without embedding all your code in the callback. This is much quicker if you want to implement cors afterwards.

exports.exampleFunction = functions.https.onRequest((request, response) => {
    cors(request, response, () => {});
    return response.send("Hello from Firebase!");
});

Do not forget to init cors as mentioned in the opening post:

const cors = require('cors')({origin: true});

Update: Any response function that takes time risk a CORS error with this implementation because this doesn't have the appropriate async/await. Don't use outside of quick prototyping endpoints that return static data.


this worked when other SO answers with setting the headers manually did not
This works but it can cause TSlint error if you had it enabled and you cannot deploy to firebase. Put the response inside the cors closure to overcome it cors(request, response, () => { return response.send("Hello from Firebase!"); });
2 errors here guys. First one. Anything after the cors function will run twice (since the first request is preflight). Not good. Second, @SpiralOut your solution will make you lose logging on cloud functions (very bad) and proper async / await functionality, you risk the function content being prematurely ended inside the callback.
Having learned a lot about gcf in the last year, I wouldn’t recommend this answer anymore. It could be handy for quick prototypes, but avoid this in real production cases
@JaapWeijland please update your answer with any info why this approach is bad for production code. As eventually all (seemingly working) examples from stackoverflow sooner or later end up in production...
G
GorvGoyl

Updated answer: using cors library with Typescript support:

install cors

npm i -S cors
npm i --save-dev @types/cors

index.ts:

import * as cors from "cors";
const corsHandler = cors({ origin: true });

// allow cors in http function
export const myFunction = functions.https.onRequest((req, res) => {
corsHandler(req, res, async () => {

// your method body

 });
});

Old answer: (not working anymore)
Found a way to enable cors without importing any 'cors' library. It also works with Typescript and tested it in chrome version 81.0.

exports.createOrder = functions.https.onRequest((req, res) => {
// browsers like chrome need these headers to be present in response if the api is called from other than its base domain
  res.set("Access-Control-Allow-Origin", "*"); // you can also whitelist a specific domain like "http://127.0.0.1:4000"
  res.set("Access-Control-Allow-Headers", "Content-Type");

  // your code starts here

  //send response
  res.status(200).send();
});

This worked for me adding res.set("Access-Control-Allow-Origin", "*"); only did not work res.set("Access-Control-Allow-Headers", "Content-Type"); solved my issue
This did not work for me, because it didn't support the OPTIONS method "preflight check" before the GET/POST request. I had to switch to the cors package (or recreate the OPTIONS special response, which ended up being more trouble than it's worth)
S
Sandy

This might be helpful. I created firebase HTTP cloud function with express(custom URL)

const express = require('express');
const bodyParser = require('body-parser');
const cors = require("cors");
const app = express();
const main = express();

app.post('/endpoint', (req, res) => {
    // code here
})

app.use(cors({ origin: true }));
main.use(cors({ origin: true }));
main.use('/api/v1', app);
main.use(bodyParser.json());
main.use(bodyParser.urlencoded({ extended: false }));

module.exports.functionName = functions.https.onRequest(main);

Please make sure you added rewrite sections

"rewrites": [
      {
        "source": "/api/v1/**",
        "function": "functionName"
      }
]

To anyone reading this considering putting express inside their cloud function - just don't. Spinning up express takes up extra resources, etc, and you already have all that functionality implemented. Firebase functions !== node.
It may literally spin up a new express server per request. I'm really not sure why they have this stuff in the official docs.
K
KasparTr

If you don't/can't use cors plugin, calling the setCorsHeaders() function first thing in the handler function will also work.

Also use the respondSuccess/Error functions when replying back.

const ALLOWED_ORIGINS = ["http://localhost:9090", "https://sub.example.com", "https://example.com"]


// Set CORS headers for preflight requests
function setCorsHeaders (req, res) {
  var originUrl = "http://localhost:9090"


  if(ALLOWED_ORIGINS.includes(req.headers.origin)){
    originUrl = req.headers.origin
  }

  res.set('Access-Control-Allow-Origin', originUrl);
  res.set('Access-Control-Allow-Credentials', 'true');

  if (req.method === 'OPTIONS') {
    // Send response to OPTIONS requests
    res.set('Access-Control-Allow-Methods', 'GET,POST','PUT','DELETE');
    res.set('Access-Control-Allow-Headers', 'Bearer, Content-Type');
    res.set('Access-Control-Max-Age', '3600');
    res.status(204).send('');
  }
}

function respondError (message, error, code, res) {
  var response = {
    message: message,
    error: error
  }
  res.status(code).end(JSON.stringify(response));
}


function respondSuccess (result, res) {
  var response = {
    message: "OK",
    result: result
  }
  res.status(200).end(JSON.stringify(response));
}

d
dimib

Simple solution using the Google Cloud Console Dashboard:

Go to your GCP console dashboard:

https://console.cloud.google.com/home/dashboard

Go to menu

"Cloud Functions" ("Compute" section)

Select your cloud function, e.g. "MyFunction", a side menu should appear on the right showing you the access control settings for it Click on "Add Member", type in "allUsers" and select the role "Cloud Function Invoker" Save it -> now, you should see a remark "Allow unauthenticated" in the list of your cloud functions

Access is now available to everybody from the internet with the correct config to your GCP or Firebase project. (Be careful)


z
zeekrey

If there are people like me out there: If you want to call the cloud function from the same project as the cloud function it self, you can init the firebase sdk and use onCall method. It will handle everything for you:

exports.newRequest = functions.https.onCall((data, context) => {
    console.log(`This is the received data: ${data}.`);
    return data;
})

Call this function like this:

// Init the firebase SDK first    
const functions = firebase.functions();
const addMessage = functions.httpsCallable(`newRequest`);

Firebase docs: https://firebase.google.com/docs/functions/callable

If you can't init the SDK here is the essence from the other suggestions:

If you use firebase hosting and host in the default location, choose rewrites: https://firebase.google.com/docs/hosting/full-config#rewrites

Or use CORS like krishnazden suggested: https://stackoverflow.com/a/53845986/1293220


actually when I use onCall func on browser I got cors error. Could I set costom headers in this request ?
For those also getting onCall cors error, try this post: stackoverflow.com/q/50278537/1411473
G
Gleb Dolzikov

Only this way works for me as i have authorization in my request:

exports.hello = functions.https.onRequest((request, response) => {
response.set('Access-Control-Allow-Origin', '*');
response.set('Access-Control-Allow-Credentials', 'true'); // vital
if (request.method === 'OPTIONS') {
    // Send response to OPTIONS requests
    response.set('Access-Control-Allow-Methods', 'GET');
    response.set('Access-Control-Allow-Headers', 'Content-Type');
    response.set('Access-Control-Max-Age', '3600');
    response.status(204).send('');
} else {
    const params = request.body;
    const html = 'some html';
    response.send(html)
} )};

google cloud functions do not allow the wildcard origin: cloud.google.com/functions/docs/writing/…
where do i put in my function body? I have posted my code here stackoverflow.com/questions/68646425/…
w
wisetap.com

A cors error can occur if you don't catch an error in a function. My suggestion is to implement a try catch in your corsHandler

const corsHandler = (request, response, handler) => {
    cors({ origin: true })(request, response, async () => {
        try {
            await handler();
        }
        catch (e) {
            functions.logger.error('Error: ' + e);
            response.statusCode = 500;
            response.send({
                'status': 'ERROR' //Optional: customize your error message here
            });
        }
    });
};

Usage:

exports.helloWorld = functions.https.onRequest((request, response) => {
    corsHandler(request, response, () => {
        functions.logger.info("Hello logs!");
        response.send({
            "data": "Hello from Firebase!"
        });
    });
});

Thanks to stackoverflow users: Hoang Trinh, Yayo Arellano and Doug Stevenson


Good answer but one nit: As far as the status code of the error, rather than a 500, I think that it’s probably better to response with a 403. That’s (roughly) what the spec recommends at fetch.spec.whatwg.org/#http-responses. And in general, IMHO, it seems not optimal to send a 500 from application code — because that’s gonna make it harder for you to distinguish cases of your application code throwing an intended error from cases of the server/runtime code hitting some other kind of internal failure (maybe even before it ever executes your application code).
Thanks for the comment :) The 500 response code is because is not an error related to cors, because of that I think is more appropriate to use a status code >= 500 rather than 403. What do you think?
I have tried almost all answers on this page and this is the only one that worked. Thank you!
I'm happy that worked! :)
m
mhaligowski

I have just published a little piece on that:

https://mhaligowski.github.io/blog/2017/03/10/cors-in-cloud-functions.html

Generally, you should use Express CORS package, which requires a little hacking around to meet the requirements in GCF/Firebase Functions.

Hope that helps!


Not sure what you mean by hacking ? Care to elaborate a bit? Read your post but I don't see you mentioning it
author of the cors module here; by "hacking" mhaligowski simply meant that he had to wrap the call to the cors module to make it match the way Express calls middleware (i.e. supply a function as third parameter after req & res)
That link is broken mhaligowski.github.io/blog/2017/03/10/… this is why its better to put the content(summary) of external resources rather than external links
R
Rob

Changing true by "*" did the trick for me, so this is how it looks like:

const cors = require('cors')({ origin: "*" })

I tried this approach because in general, this is how this response header is set:

'Access-Control-Allow-Origin', '*'

Be aware that this will allow any domain to call your endpoints therefore it's NOT secure.

Additionally, you can read more on the docs: https://github.com/expressjs/cors


s
shadyhill

For what it's worth I was having the same issue when passing app into onRequest. I realized the issue was a trailing slash on the request url for the firebase function. Express was looking for '/' but I didn't have the trailing slash on the function [project-id].cloudfunctions.net/[function-name]. The CORS error was a false negative. When I added the trailing slash, I got the response I was expecting.


also make sure you add your [project-id] as this was the issue i faced
S
Sercan

Go into your Google Cloud Functions. You may have not seen this platform before, but it's how you'll fix this Firebase problem. Find the Firebase function you're searching for and click on the name. If this page is blank, you may need to search for Cloud Functions and select the page from the results. Find your function, click on the name. Go to the permissions tab. Click Add (to add user). Under new principles, type 'allUsers' -- it should autocomplete before you finish typing. Under select a role, search for Cloud Functions, then choose Invoker. Save. Wait a couple minutes.

This should fix it. If it doesn't, do this AND add a CORS solution to your function code, something like:

  exports.sendMail = functions.https.onRequest((request, response) => {
  response.set("Access-Control-Allow-Origin", "*");
  response.send("Hello from Firebase!");
});

Wont this will make your function public and available to anyone if you don't handle the authentication yourself inside the function.
For me, this is what it took to get the thing working and save my sanity. I worked backwards and implemented security measures from there. How that is handled is outside the scope of what I'm answering, but it is worth mentioning.
S
Suraj Rao

If You are not using Express or simply want to use CORS. The following code will help resolve

const cors = require('cors')({ origin: true, });   
exports.yourfunction = functions.https.onRequest((request, response) => {  
   return cors(request, response, () => {  
        // *Your code*
    });
});

G
GorvGoyl

If you're testing firebase app locally then you need to point functions to localhost instead of cloud. By default, firebase serve or firebase emulators:start points the functions to server instead of localhost when you use it on your web app.

Add below script in html head after firebase init script:

 <script>
      firebase.functions().useFunctionsEmulator('http://localhost:5001')
 </script> 

Make sure to remove this snippet when deploying code to server.


u
user1689987

I got the error because I was calling a function that didn't exist on the client side. For example:

firebase.functions().httpsCallable('makeSureThisStringIsCorrect');

T
Thomas

Adding my piece of experience. I spent hours trying to find why I had CORS error.

It happens that I've renamed my cloud function (the very first I was trying after a big upgrade).

So when my firebase app was calling the cloud function with an incorrect name, it should have thrown a 404 error, not a CORS error.

Fixing the cloud function name in my firebase app fixed the issue.

I've filled a bug report about this here https://firebase.google.com/support/troubleshooter/report/bugs


N
Noks1

From so much searching, I could find this solution in the same firebase documentation, just implement the cors in the path:

import * as express from "express";
import * as cors from "cors";


const api = express();
api.use(cors({ origin: true }));
api.get("/url", function);

Link firebase doc: https://firebase.google.com/docs/functions/http-events


K
Kevin Danikowski

If you prefer to make a single handler function (reference answer)

const applyMiddleware = handler => (req, res) => {
  return cors(req, res, () => {
    return handler(req, res)
  })
}
exports.handler = functions.https.onRequest(applyMiddleware(handler))

This is an ideal solution if you might need to add more handlers or have many functions
G
Guillaume Renoult

I'm a very beginner with Firebase (signed up 30 minutes ago). My issue is that I called my endpoint

https://xxxx-default-rtdb.firebaseio.com/myendpoint

Instead of

https://xxxx-default-rtdb.firebaseio.com/myendpoint.json

If you just started with Firebase, make sure you don't forget the .json extension.


A
Abraham

I have been trying this for a long time.

It finally finally worked when I made this change.

app.get('/create-customer', (req, res) => {
  return cors()(req, res, () => {
    ... your code ...

The Big difference is that I used cors()(req, res... instead of directly cors(req, res...

It Now works perfectly.


T
Tris

With the same access allow control origin error in the devtool console, I found other solutions with also more modern syntax :

My CORS problem was with Storage (and not RTDB neither the browser...), and then I'm not in possession of a credit card (as requested by the aforementioned solutions), my no-credit card solution was to :

install gsutil : https://cloud.google.com/storage/docs/gsutil_install#linux-and-macos to create a cors.json file to be loaded via terminal with gsutil

gsutil cors set cors.json gs://[ your-bucket ]/-1.appspot.com

https://firebase.google.com/docs/storage/web/download-files#cors_configuration


K
Kacpero

In my case the error was caused by cloud function invoker limit access. Please add allUsers to cloud function invoker. Please catch link. Please refer to article for more info


Please provide some explanation of linked material in your answer, why is it relevant and such
A
Agilan I

If none of the other solutions work, you could try adding the below address at the beginning of the call to enable CORS - redirect:

https://cors-anywhere.herokuapp.com/

Sample code with JQuery AJAX request:

$.ajax({
   url: 'https://cors-anywhere.herokuapp.com/https://fir-agilan.web.app/gmail?mail=asd@gmail.com,
   type: 'GET'
});

this is no longer possible.
J
Joel Walkley

See below for how I set up my Express with CORS.

The 'https://pericope.app' is my custom domain for my Firebase project.

It looks like all other answers recommend origin:true or *.

I'm hesitant to allow all origins since it would allow anyone else access to the api. That's fine if you are creating a public service, but if you're doing anything with your data it is risky since it is a privileged environment. For example, this admin SDK bypasses any security rules you have setup for Firestore or Storage.

//Express
const express = require('express');
const app = express();

const cors = require('cors');
app.use(cors({
  origin: 'https://pericope.app'
}));

B
BorisD

Use cors on your https.onRequest with Typescript like this:

import * as cors from 'cors';
const corsHandler = cors({origin: true});

export const pingFunctionWithCorsAllowed = functions.https.onRequest((request, response) => {
  corsHandler(request, response, () => {
    response.send(`Ping from Firebase (with CORS handling)! ${new Date().toISOString()}`);
  });
});

From Source