Firebase Google sign-in on Cloudflare Pages
To use Firebase Google sign-in on Cloudflare Pages, add every hostname that serves your app (your custom domain, <project>.pages.dev and any preview alias you test on) to Firebase's authorized domains.
If you use signInWithRedirect, also forward /__/auth/* to <project-id>.firebaseapp.com with a Pages Function (a _redirects rule can't do this), then set authDomain to your own domain.
Why Google sign-in breaks on Cloudflare Pages
1. Pages serves your app from several hostnames. Commits to the production branch update <project>.pages.dev. Every preview deployment gets its own hash-based address such as 373f31e2.<project>.pages.dev, and each branch gets an alias such as fix-api.<project>.pages.dev. A connected custom domain is one more hostname. Firebase only runs OAuth sign-in on domains in its authorized list, so any hostname you forgot to add fails with auth/unauthorized-domain. The list takes exact domains, so a new preview hash is a new domain (see wildcards in authorized domains).
2. Redirect sign-in needs your domain to answer /__/auth/. By default the Firebase JS SDK finishes signInWithRedirect() through a cross-origin iframe on your authDomain (<project-id>.firebaseapp.com). Firebase's docs say browsers that block third-party storage access break this, and that a fix is required on Chrome M115+, Firefox 109+ and Safari 16.1+. Firebase Hosting answers /__/auth/ on its own domains because it reserves the /__ path. Cloudflare Pages has no such path, so on Pages you either switch to a popup or forward /__/auth/ to Firebase yourself.
3. _redirects can't forward to Firebase. Cloudflare's docs say: "Proxying will only support relative URLs on your site. You cannot proxy external domains." Firebase also says the forwarding must be invisible to the browser and "cannot be done via a 302 Redirect." So a plain redirect rule won't work. A Pages Function will.
Choose your setup
| What you use | What you need |
|---|---|
signInWithPopup only |
Step 1 only. Google still shows <project-id>.firebaseapp.com during sign-in. |
signInWithRedirect, or you want your own domain shown at Google sign-in |
Steps 1 to 5 on a custom domain (or on <project>.pages.dev). |
| Preview deployments | Step 1 for the branch aliases you test on, plus the preview section below. |
Step 1: Add your Pages hostnames to Firebase authorized domains
As of Oct 2026, Firebase's docs give this path: in the Firebase console, go to Security > Authentication > Settings, then in Authorized domains click Add domain. Older guides and console versions put this under Build > Authentication > Settings.
Add each hostname, without https:// or a path. For example:
app.example.com
your-project.pages.dev
staging.your-project.pages.dev
Firebase projects created after April 28, 2025 don't include localhost by default. Add it only if you sign in from a local dev server.
Step 2: Connect your custom domain in Pages
As of Oct 2026: go to Workers & Pages, pick your Pages project, open Custom domains, click Set up a domain, enter the domain and click Continue.
- Apex domain (
example.com): the domain must be a Cloudflare zone on the same account as the Pages project, with Cloudflare's nameservers. - Subdomain (
app.example.com): if the DNS is elsewhere, add a CNAME from the subdomain to<project>.pages.devat your DNS provider. - Finish these steps in the dashboard before adding a CNAME. Cloudflare warns that a CNAME added by hand without linking the domain in Pages returns a 522 error.
Step 3: Forward /__/auth/* with a Pages Function
Create functions/_middleware.js at the root of your repo (next to your build config, not inside the build output folder). Replace your-project-id with your Firebase project ID.
// functions/_middleware.js
// Forwards Firebase's sign-in helper paths to firebaseapp.com.
// The browser stays on your domain (no 302).
const FIREBASE_AUTH_HOST = "your-project-id.firebaseapp.com";
export async function onRequest(context) {
const url = new URL(context.request.url);
if (url.pathname.startsWith("/__/auth/")) {
url.protocol = "https:";
url.hostname = FIREBASE_AUTH_HOST;
url.port = "";
// Keep method, headers and body (the handler accepts GET and POST).
const upstream = new Request(url.toString(), context.request);
// Pass any 3xx back to the browser as-is instead of following it here.
return fetch(upstream, { redirect: "manual" });
}
// Everything else: your other Functions or your static files.
return context.next();
}
Test this on a preview deployment before relying on it.
How it works: context.next() passes the request on to the next Function, or to your static assets if there isn't one. A _middleware.js file in the root functions folder covers your whole site. Building a new Request(url, request) from the incoming request is the pattern Cloudflare documents for changing a request's URL, and redirect: "manual" returns a 3xx response to the caller unchanged.
Optional: run the Function only on /__/auth/*. Middleware at the root also runs on your static files. Cloudflare's docs say a _routes.json file in your build output folder controls when Functions are invoked:
{
"version": 1,
"include": ["/__/auth/*"],
"exclude": []
}
If you already have other Functions (for example under /api/), add their paths to include, or they stop running. Cloudflare allows up to 100 include and exclude rules combined, and exclude always wins over include.
Cloudflare's docs also say Pages generates a _routes.json automatically when it finds a functions folder, and some frameworks write their own. The docs don't say which file is used when both exist, so after deploying, check that /__/auth/handler still reaches the Function and your other routes still work. This step is optional: the middleware works without it.
Step 4: Add the redirect URI in Google Cloud
As of Oct 2026, OAuth clients live on the Google Auth Platform > Clients page of the Google Cloud console (older guides call it APIs & Services > Credentials). Open the web client that Firebase created for your project and add this under Authorized redirect URIs:
https://app.example.com/__/auth/handler
Firebase stresses that the trailing /__/auth/handler matters. Google says changes to these settings can take 5 minutes to a few hours to apply. If Google shows Error 400: redirect_uri_mismatch, see fixing redirect_uri_mismatch.
Step 5: Set authDomain and redeploy
Change only authDomain in your Firebase config. Firebase's docs say you normally shouldn't change any other field.
import { initializeApp } from "firebase/app";
import { getAuth, GoogleAuthProvider, signInWithRedirect } from "firebase/auth";
const firebaseConfig = {
apiKey: "...",
authDomain: "app.example.com", // was "your-project-id.firebaseapp.com"
projectId: "your-project-id",
appId: "...",
};
const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
export function signIn() {
return signInWithRedirect(auth, new GoogleAuthProvider());
}
If the config comes from environment variables, rebuild after changing them. In Vite, for example, only variables prefixed with VITE_ reach client code. Set them in your Pages project's build settings and redeploy.
Preview deployments
Preview hostnames change, so the simplest pattern is to use the full redirect setup on production only, and popups on previews. Firebase lists signInWithPopup() as one of its fixes for hosts other than Firebase Hosting, so popups don't need the proxy.
const isProd = location.hostname === "app.example.com";
const firebaseConfig = {
// ...
authDomain: isProd ? "app.example.com" : "your-project-id.firebaseapp.com",
};
// later, in your click handler:
const provider = new GoogleAuthProvider();
if (isProd) {
signInWithRedirect(auth, provider);
} else {
signInWithPopup(auth, provider);
}
Previews still need their hostname in Firebase's authorized domains (Step 1). Branch aliases such as staging.<project>.pages.dev stay the same between commits, so authorize those rather than hash URLs.
How to confirm it worked
- Open
https://app.example.com/__/auth/handlerwith your browser's developer tools on the Network tab. The address bar should stay on your domain, with no 301/302 tofirebaseapp.com, and the response should not be your app's own page or 404. - Click your sign-in button. On Google's account chooser, Firebase's docs say users otherwise see
Continue to: https://<project-id>.firebaseapp.com. After this change, the domain shown should be yours. - After you return to your app,
getRedirectResult(auth)should resolve with a user, with noauth/unauthorized-domainorredirect_uri_mismatch.
Related problems
- Still blank after redirect: signInWithRedirect not working.
- On Cloudflare Workers instead of Pages: the Workers version of this setup.
- Considering Firebase Hosting, which serves
/__/auth/itself: Firebase Hosting custom domain for auth.
FAQ
Can I use a `_redirects` rule to proxy `/__/auth/` on Cloudflare Pages?
No. Cloudflare's docs say _redirects proxying only supports relative URLs on your own site and can't proxy external domains. A 301/302 redirect doesn't work either, because Firebase requires the forwarding to be invisible to the browser. Use a Pages Function as in Step 3.
Do I need the proxy if I only use `signInWithPopup`?
No. Firebase lists switching to signInWithPopup() as a fix for hosts other than Firebase Hosting. You still need your Pages hostnames in Firebase's authorized domains. Google will keep showing <project-id>.firebaseapp.com during sign-in, because that stays your authDomain.
Why does sign-in work on my custom domain but not on a preview URL?
Each preview deployment gets a new hash-based hostname, and Firebase only allows sign-in on domains in its authorized list. Authorize the branch alias you test on (for example staging.<project>.pages.dev), or test previews with a popup as shown above.
Can I use `<project>.pages.dev` as my `authDomain`?
Yes, the same steps apply. The Pages Function runs on pages.dev too, so add https://<project>.pages.dev/__/auth/handler as a redirect URI and authorize <project>.pages.dev. A custom domain lets users see your own brand name at Google sign-in instead.
Should I forward `/__/firebase/init.json` too?
Firebase's proxy option only asks you to forward /__/auth/. The file list that includes /__/firebase/init.json belongs to a different option, self-hosting the helper files.
Sources
- Best practices for using signInWithRedirect on browsers that block third-party storage access (Firebase)
- Authenticate Using Google with JavaScript (Firebase)
- Authenticate with Firebase Using Email Link in JavaScript (Firebase), authorized domains steps
- Load Firebase SDKs from reserved URLs (Firebase Hosting)
- Redirects (Cloudflare Pages docs)
- Routing (Cloudflare Pages docs)
- Middleware (Cloudflare Pages docs)
- API reference (Cloudflare Pages docs)
- Request (Cloudflare Workers docs)
- Preview deployments (Cloudflare Pages docs)
- Custom domains (Cloudflare Pages docs)
- Manage OAuth Clients (Google Cloud Platform Console Help)
- Env Variables and Modes (Vite)
How this page was made: drafted with AI from the sources listed, reviewed by Pewterkit. Docs checked Oct 27, 2026. Spotted something out of date? Tell us.