Supabase

Firebase Google Sign-in on Vercel: Custom authDomain Fix

Keep the Next.js app on Vercel and make the Google sign-in screen name your own domain instead of the project-id.firebaseapp.com subdomain.

Mahdi Ben RhoumaMahdi Ben Rhouma
8 min read

TL;DR#

The Google account screen shows my-app-12345.firebaseapp.com because that is the Firebase project's auth domain: the sign-in helper page lives there and Google redirects back to it. Changing authDomain in the Firebase config to the Vercel domain fails on its own, because nothing on Vercel answers /__/auth/handler. For apps hosted outside Firebase, the option that keeps the helper current is to proxy /__/auth/* from your domain to <project>.firebaseapp.com. On Vercel, a Next.js rewrite does that. Then point authDomain at your domain, add https://<your-domain>/__/auth/handler to the OAuth client's authorised redirect URIs, and keep the domain in Firebase's Authorised domains.

This is the situation in the Stack Overflow question: a Next.js app with Firestore and Firebase Authentication, deployed and registered on Vercel. The asker followed Firebase's "Customizing the redirect domain for Google sign-in" steps, but those steps assume Firebase Hosting. They did not want to move hosting away from Vercel.

What the sign-in screen shows, and where that domain comes from#

Firebase's Google sign-in guide explains the domain in the popup. Each project gets a subdomain such as my-app-12345.firebaseapp.com, and the guide says it is also the redirect mechanism for OAuth sign-in:

However, this means that users may see that domain while signing in to Google before redirecting back to the application

The guide's remedy is to set up a custom domain on Firebase Hosting, allow https://auth.custom.domain.com/__/auth/handler as a redirect URL in the OAuth setup page, and pass the custom domain as authDomain. Every step starts from a domain connected to Firebase Hosting, and Firebase's best-practices page says the same of its first option: "If you don't use Firebase Hosting, you need to use a different option." A Vercel deployment is that case, which is why the asker's edit failed:

js
const firebaseConfig = {
  // ...
  authDomain: 'customdomain.com', // nothing on Vercel answers /__/auth/handler
}

Why the custom authDomain fails on Vercel#

The SDK does not send the user to Google from your page. Firebase's best-practices page describes the flow: the auth domain "hosts the sign-in helper code that redirects to the Identity Provider", and the redirect URI registered with the provider is the auth domain followed by /__/auth/handler. If authDomain names your Vercel domain, the popup opens your app's /__/auth/handler. A Next.js app has no such route.

This was reproduced on 13 October 2026 with Next.js 16.4.0 on Node.js v24.21.0 (official node:24-bookworm-slim Docker image), running next build and next start on an app with no rewrite. Each helper path probed returned the app's own 404 page:

text
### config: next.config.before.js (port 3001)
GET /__/auth/handler -> 404 bytes=6905 widget=false
POST /__/auth/handler -> 404 bytes=6905 widget=false
GET /__/auth/iframe -> 404 bytes=6905 widget=false
GET /__/auth/handler.js -> 404 bytes=6905 widget=false

widget=false means the response does not contain the call that starts Firebase's helper widget. The popup gets a Next.js "not found" page instead of the Firebase sign-in helper, so sign-in never reaches Google.

What the two Stack Overflow answers fix, and what they leave out#

Both answers on the question say to add the deployed domain under Authentication → Settings → Authorised domains. That step is required: the Firebase JS SDK has an error code for it, auth/unauthorized-domain. Its message in the SDK source reads "This domain is not authorized for OAuth operations for your Firebase project. Edit the list of authorized domains from the Firebase console."

That list governs which domains may run OAuth operations for the project. It does not change the domain that hosts the helper page, and the helper page's domain is the one the guide warns users "may see". With only that change, the popup still goes through my-app-12345.firebaseapp.com. Adding the domain fixes an error, but it does not customise anything.

Fix: proxy /__/auth through your Vercel deployment#

Firebase's page Best practices for using signInWithRedirect on browsers that block third-party storage access lists the setups for apps hosted outside Firebase. Option 3 is a reverse proxy, so that requests to https://<app domain>/__/auth/ are forwarded to https://<project>.firebaseapp.com/__/auth/. The page adds a constraint that matters for the choice of tool:

Ensure that this forwarding is transparent to the browser; this cannot be done via a 302 Redirect.

Next.js rewrites meet that constraint. The rewrites reference describes them as follows: "Rewrites act as a URL proxy and mask the destination path, making it appear the user hasn't changed their location on the site." The same page documents rewriting to an external URL. On Vercel's side, the rewrites documentation states that "Rewrites to external origins forward requests to these destinations, effectively allowing Vercel to function as a reverse proxy or standalone CDN."

  1. Add the rewrite to next.config.js. The destination is your project's default auth domain, which the Google sign-in guide gives as PROJECT_ID.firebaseapp.com. Replace my-app-12345 with your own project ID:
js
const projectId = 'my-app-12345' // your Firebase project ID
 
/** @type {import('next').NextConfig} */
module.exports = {
  async rewrites() {
    return [
      {
        source: '/__/auth/:path*',
        destination: `https://${projectId}.firebaseapp.com/__/auth/:path*`,
      },
    ]
  },
}

next build resolves the rewrite and writes the final destination into the build output; the reproduction below found it in .next/routes-manifest.json. If you build the project ID from an environment variable instead, make sure the variable exists in the Vercel build. A missing one turns the destination into undefined.firebaseapp.com, and the next section explains why that mistake does not show up as a 404.

  1. Point authDomain at the domain that serves the app. Firebase's best-practices page shows authDomain set to the domain that serves your app, and warns that you normally should not modify any other field of the config object:
js
import { initializeApp } from 'firebase/app'
 
const firebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  authDomain: 'www.example.co.uk', // was 'my-app-12345.firebaseapp.com'
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
}
 
export const app = initializeApp(firebaseConfig)
  1. Authorise the new redirect URI with Google. The best-practices page says the URI to add looks like https://<the-domain-that-serves-your-app>/__/auth/handler, and that the trailing /__/auth/handler is important. For Google sign-in, that is the Google developer console or OAuth setup page named in the Google sign-in guide. With the example domain, the URI to authorise is https://www.example.co.uk/__/auth/handler.

  2. Keep www.example.co.uk in Authentication → Settings → Authorised domains in the Firebase console. This is the step from the Stack Overflow answers. It is still needed, but it is not enough on its own.

  3. Redeploy on Vercel. The rewrite only exists in a build that contains the new next.config.js.

The proxy is not limited to the redirect flow. The page is written around signInWithRedirect(), but the comment in its own nginx sample says "reverse proxy for signin-helpers for popup/redirect sign in". The popup in the question loads the same /__/auth/handler page from whatever authDomain names.

Verify the fix#

The same Next.js 16.4.0 app was rebuilt with the rewrite from step 1, pointed at fir-ui-demo-84a6c, a public Firebase project. Every helper path then returned Firebase's files through the Next.js server. The handler page was byte-for-byte identical to the one served directly by firebaseapp.com:

text
### config: next.config.after.js (port 3002)
GET /__/auth/handler -> 200 bytes=462 widget=true
POST /__/auth/handler -> 200 bytes=449 widget=true
GET /__/auth/iframe -> 200 bytes=439 widget=false
GET /__/auth/handler.js -> 200 bytes=280750 widget=false
direct handler bytes=462 identical-to-proxied=true

The POST line matters too. Firebase's page asks for GET and POST requests to be forwarded, and the rewrite passed the POST to Firebase, which answered with the helper page. The iframe and script lines carry widget=false because only the handler page contains that call. Their status code is what counts.

To check your own deployment after step 5, request the handler on your domain. A 404 means the rewrite is not in the deployed build. A 200 means the proxy is in place, but it does not prove the destination names the right project:

bash
curl -s -o /dev/null -w '%{http_code}\n' https://www.example.co.uk/__/auth/handler

The handler page is generic. On 13 October 2026, https://undefined.firebaseapp.com/__/auth/handler also answered 200 with the same 462 bytes, although no project of that name serves a Firebase config there: its /__/firebase/init.json returned a "Site Not Found" page. To check the project ID itself, compare the destination in your next.config.js with the projectId field of your Firebase config.

Then sign in with Google in a private window. The account chooser should now show your domain instead of the firebaseapp.com subdomain. The local reproduction proves the proxy. What Google displays is not reproducible outside a real OAuth client, and that part rests on Firebase's own guides.

One email a month — the latest explained errors

What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.

A redirect looks like the same rule, but it is not#

Next.js redirects() takes the same source / destination shape, so it is an easy mistake. Run as a negative control with the same paths, the redirect version sends every request straight back to the Firebase domain:

text
### config: next.config.redirect.js (port 3003)
GET /__/auth/handler -> 307 location=https://fir-ui-demo-84a6c.firebaseapp.com/__/auth/handler bytes=57 widget=false
POST /__/auth/handler -> 307 location=https://fir-ui-demo-84a6c.firebaseapp.com/__/auth/handler bytes=57 widget=false

The browser follows that location, so the helper runs on firebaseapp.com again. That is the domain the change was meant to hide, and the cross-origin setup Firebase's page tells you to remove. The status is 307 rather than the 302 named on the page, but the problem is the same: the browser can see it.

When moving auth to Firebase Hosting is simpler#

The rewrite keeps Vercel as the only host, which is what the asker wanted. The best-practices page numbers five options, and the proxy is Option 3. Option 4 self-hosts the sign-in helper files under /__/auth/ on your own domain (on Vercel, a public/__/auth/ folder), with no proxy, but you have to download them from <project>.firebaseapp.com again whenever Firebase updates them. Option 1 serves the app from Firebase Hosting on a custom domain and uses that domain as authDomain. That means changing host, which the asker had ruled out. If you do move the project to Firebase Hosting, check two known traps first: Next.js static exports that 404 on every route except the home page, and the webframeworks experiment that CI deploys need.

Firebase Authentication on Next.js has other failure points before you reach the sign-in screen. If the module that builds firebaseConfig still uses v8-style imports, it crashes with Cannot read properties of undefined (reading 'apps'). If it passes the config object where the SDK expects the app, it fails with Cannot read property 'getProvider' of undefined. Whether the apiKey next to authDomain is safe to ship is a separate question, covered in its own guide.

After switching authDomain, three configuration gaps each produce a distinct error. A popup that fails with auth/unauthorized-domain means the domain is missing from Authorised domains (step 4). A Google error page about the redirect URI means the /__/auth/handler URL from step 3 is missing from the OAuth client. A 404 inside the popup means the deployed build has no rewrite. All three come back to the same idea: the helper page has to exist on your domain, and Google and Firebase both have to be told that it lives there.

Frequently Asked Questions

|

Have more questions? Contact us

Still stuck? Paste your error.

Paste the error message or stack trace — we check it against every fix and tool on the site.

Matching happens in your browser. When you search, a cleaned error signature is sent so we can spot errors we do not cover yet — never the raw text, your IP address or your e-mail. Do not paste passwords or keys. Details

Written by

Mahdi Ben Rhouma
Mahdi Ben Rhouma

Full-Stack Dev — Next.js & Supabase

Solo developer building SaaS products with Next.js and Supabase. Writing about production patterns the official docs skip.

Remote