CORS in Firebase? Do this before you rage quit

Batta SreeyaBatta Sreeya
6 min read

You know it. You hate it. It is quite annoying when this dreaded error shows up when your app looks like a real product.

That is CORS (Cross Origin Resource Sharing). It is not quite a bug, not quite a feature (I’m sure you’ve read this in the million other articles explaining what is CORS), its more like a bouncer outside a club your JavaScript is trying to sneak into.

Okay, If you haven’t spent hours reading about CORS, here’s the gist:

Imagine your frontend is a browser from domain-A.com that wants to ask a question to your backend server at domain-B.com. CORS is the grumpy gatekeeper yelling, “WHO EVEN ARE YOU?” before letting anything through. That’s it.

Now enough theory. You’re here because you Googled “firebase CORS please fix before I rage quit”. So let’s get started, but be warned, this fix is for Firebase users.

1. Are You Using Firebase Hosting + Functions? Okay. Here’s What to Do.

You’re likely using Firebase Cloud Functions as your backend. In that case, Firebase does not handle CORS magically for you. So, what can you do?

Step 1: Install the CORS middleware

Inside your terminal, run:

npm install cors

Step 2: Import and Use It Inside Your Cloud Function

import * as functions from 'firebase-functions';
import * as express from 'express';
import * as corsModule from 'cors';

const cors = corsModule({ origin: true }); // Accepts all origins (not recommended for production)
const app = express();

app.use(cors);

app.get('/hello', (req, res) => {
  res.status(200).send('Hello from Firebase!');
});

export const api = functions.https.onRequest(app);

Great. No more CORS error.

But wait, maybe don’t leave origin: true in production. It’s like saying “let anyone in, no questions asked.” So, it’s best to restrict access:

const cors = corsModule({
  origin: ["https://yourapp.com", "https://yourapp.web.app"],
});

2. Not Using Express? Just a Simple Function?

You can still fix CORS without express.

import * as functions from 'firebase-functions';
import { Request, Response } from 'express';

export const simpleFunction = functions.https.onRequest((req: Request, res: Response) => {
  res.set('Access-Control-Allow-Origin', '*'); // YOLO mode
  res.set('Access-Control-Allow-Methods', 'GET, POST');

  if (req.method === 'OPTIONS') {
    // Preflight request
    res.status(204).send('');
    return;
  }

  // Your actual logic
  res.status(200).send("CORS shouldn't yell at you now.");
});

Pro tip: Keep an eye on preflight requests. They’re like browser smoke signals.

On a side note: a preflight request is when your frontend sends anything that’s not a basic GET/POST, like a PUT, DELETE, or uses non-standard headers the browser first sends an options request to politely ask the server: “Hey, are you cool if I send this request?”

That’s called a preflight request. Your server must respond with:

  • Access-Control-Allow-Origin

  • Access-Control-Allow-Methods

  • Possibly: Access-Control-Allow-Headers

Otherwise, the browser goes: “Nope, this backend’s shady,” and blocks the request.

3. Firebase Storage CORS? That’s a Different Challenge

Want to get images or files from Firebase Storage? You’ll need to set CORS config manually.

Run this from CLI:

gsutil cors set cors.json gs://your-bucket-name

And your cors.json should look like:

[
{
"origin": ["https://yourapp.web.app"],
"method": ["GET"],
"maxAgeSeconds": 3000
}
]

I know, it looks like overkill for just an image, but hey, welcome to web development.

4. Google Sign-In + Firebase + CORS? Yes, That’s a Thing Too

So you’ve added the “Continue with Google” button. You click it. A popup flashes. And then nothing happens. Or worse, your console throws a cryptic error that makes you question your career choices.

Good news: you’re not cursed.

Bad news: it’s probably still CORS.

First, What Method Are You Using?

There are two ways you can implement Google Sign-In with Firebase:

Method 1: Popup Sign-In

const provider = new GoogleAuthProvider();
signInWithPopup(auth, provider)
.then((result) => {
// user signed in
});

This usually doesn’t trigger CORS because it doesn’t involve cross-origin fetch calls from your app. BUT if you’re calling any Firebase Callable Cloud Function immediately after login (to store user info, for example), and that function doesn’t handle CORS, you get the error.

Method 2: Redirect Sign-In

const provider = new GoogleAuthProvider();
signInWithRedirect(auth, provider);

Then in your useEffect or onAuthStateChanged, you call:

getRedirectResult(auth).then((result) => {
// user signed in
});

This also usually works, unless your site is hosted on Firebase Hosting under one domain, and you’re calling functions from another (like a staging backend or another Firebase project). Then yes, you need to handle CORS just like in your backend functions.

What to Do After Sign-In?

Let’s say after a user signs in, you call something like this:

await fetch("https://us-central1-your-app.cloudfunctions.net/createUserProfile", {
method: "POST",
headers: {
Authorization: `Bearer ${idToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ uid: user.uid }),
});

And suddenly CORS screams in all caps. That’s because the function at createUserProfile needs CORS headers, or the browser won’t even try to send your real request.

To solve: use the same fix as before:

If You’re Using Express:

const cors = require("cors")({
origin: ["https://yourapp.web.app", "https://yourcustomdomain.com"]
});
app.use(cors);

If You’re Using Plain Functions:

exports.createUserProfile = functions.https.onRequest((req, res) => {
res.set("Access-Control-Allow-Origin", "https://yourapp.web.app");
res.set("Access-Control-Allow-Headers", "Content-Type, Authorization");
res.set("Access-Control-Allow-Methods", "POST, OPTIONS");
if (req.method === "OPTIONS") {
res.status(204).send(""); // Preflight response
return;
}
// Your actual logic
});

Bonus fix: if you are using firebase hosting with vite (React) and you’re calling external APIs like:

fetch("https://my-external-api.com/data")

Option A: Proxy It

Open your firebase.json:

"rewrites": [
{
"source": "/api/**",
"function": "api"
},
{
"source": "/external/**",
"destination": "https://external-api.com"
}
]

Now, calling /external/data on your frontend is silently forwarded to https://external-api.com/data.

Option B: Set Headers for Static Assets (like fonts, manifest, etc)

Sometimes it’s your custom font or favicon getting blocked by CORS. Just set headers in firebase.json.

"headers": [
{
"source": "**/*.woff2",
"headers": [
{
"key": "Access-Control-Allow-Origin",
"value": "*"
}
]
},
{
"source": "/manifest.json",
"headers": [
{
"key": "Access-Control-Allow-Origin",
"value": "*"
}
]
}
]

Don’t use “*” for private assets. Set it to “https://yourapp.web.app" in production.

Note: Firebase Auth SDK Handles Most CORS Internally

If you’re using Firebase Auth and Firebase Hosting under the same project, most of the Auth traffic is already CORS because it goes directly to firebaseapp.com or identitytoolkit.googleapis.com. BUT, any custom backend interaction post-auth, like calling your own function to:

  • Add user to Firestore

  • Check admin role

  • Validate email domain

still needs proper CORS handling.

Bonus Round: Common “Fixes” That Don’t Work (but will show up in your search anyway)

  • Adding headers in the frontend (fetch(…, { headers: { “Access-Control-Allow-Origin”: “*” } })) doesn’t work. CORS is a server-side problem.

  • “Just disable CORS in your browser” — useful only if you’re running a local Frankenstein dev stack, not production.

  • “Install a Chrome Extension to bypass CORS” please don’t.

In Conclusion (or: How to Not Hate CORS)

CORS is like that overprotective firewall parent: annoying, but for good reason. And Firebase? It’s like a really cool house with weird locks — you just need the right key. So the next time CORS shows up uninvited at your app party, just remember:

Middleware, headers, and a touch of caffeine(maybe a lot) can get you through it.

Thanks for sticking around, if you’ve made it this far, you deserve a cookie (or at least a break). CORS is a pain, Firebase has its quirks, and debugging this stuff can sometimes feel like playing Jenga with flaming blocks. But hey! You did it.

Thanks for reading this wildly long (but hopefully helpful and mildly entertaining) guide. If it saved you from flipping a table or yelling at your terminal, then my job here is done.

Got questions? Rants? Praise? Ping me. I’ll be around.

0
Subscribe to my newsletter

Read articles from Batta Sreeya directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Batta Sreeya
Batta Sreeya