Firebase auth/unauthorized-domain on Vercel: fix preview deployments and production
Firebase sign-in fails on Vercel with auth/unauthorized-domain because each deployment is served from a generated *.vercel.app URL, and Firebase only allows hosts on its Authorized domains list.
Add the hosts that stay the same (your production domain and a Git branch URL or branch domain), not the per-commit URLs, and keep previews on the default authDomain while production uses your own domain.
Why it happens on Vercel
Firebase's SDK throws auth/unauthorized-domain with the message "This domain is not authorized for OAuth operations for your Firebase project. Edit the list of authorized domains from the Firebase console." Firebase's help page says you "must whitelist the full domain names of any other of your web app's hosts".
Vercel gives one deployment several URLs, and they don't all behave the same:
| Vercel URL | Pattern | Changes when you push? |
|---|---|---|
| Commit (unique deployment) URL | <project-name>-<unique-hash>-<scope-slug>.vercel.app |
Yes, a new one for each commit |
| Git branch URL | <project-name>-git-<branch-name>-<scope-slug>.vercel.app |
No, it "won't change if you push new commits to the branch" |
| Production URL / custom domain | for example <project-name>.vercel.app, www.example.com |
No |
If you test sign-in on the commit URL (the View deployment link in a pull request), every push gives you a host Firebase has never seen. Vercel can also shorten long generated labels to fit DNS limits, so copy the hostname from Vercel rather than typing it from the pattern. Firebase's list takes full domain names; for what wildcard entries can and can't do, see Firebase authorized domains and wildcards.
Fix step by step
1. Decide which hosts need sign-in
Typically:
- Your production custom domain, for example
www.example.com(andexample.comif both serve the app). <project-name>.vercel.app, only if people actually sign in there.- One Git branch URL per branch you review, for example
my-app-git-staging-myteam.vercel.app. Vercel shows it behind the Visit Preview button in the pull request comment.
2. Add them to Firebase's Authorized domains
As of Oct 2026, Firebase's docs give the path Security > Authentication > Settings tab > Authorized domains > Add domain. Older guides and console menus show it as Build > Authentication > Settings. Enter the hostname only, without https:// or a path:
www.example.com
example.com
my-app-git-staging-myteam.vercel.app
One entry covers "any URL and port of that domain".
3. (Optional) Give a preview branch a custom domain
A branded host is easier to remember than a generated one. As of Oct 2026, Vercel's steps are: project Settings > Domains, choose Edit on the domain, select Preview under Connect to an environment, and enter the branch name in Git Branch. A domain such as staging.example.com then always serves that branch's latest deployment, and you add it to Firebase once.
4. (Optional) Add domains from a script
Firebase's Authorized domains live in the Identity Toolkit project config as authorizedDomains. Read the current list, then send the full list back with your additions:
PROJECT_ID="<project-id>"
TOKEN="$(gcloud auth print-access-token)"
# 1. Read the current list
curl -s -H "Authorization: Bearer $TOKEN" \
-H "x-goog-user-project: $PROJECT_ID" \
"https://identitytoolkit.googleapis.com/admin/v2/projects/$PROJECT_ID/config" \
| jq '.authorizedDomains'
# 2. Send the complete list back (existing entries + the new host)
curl -s -X PATCH -H "Authorization: Bearer $TOKEN" \
-H "x-goog-user-project: $PROJECT_ID" \
-H "Content-Type: application/json" \
"https://identitytoolkit.googleapis.com/admin/v2/projects/$PROJECT_ID/config?updateMask=authorizedDomains" \
-d '{"authorizedDomains":["<project-id>.firebaseapp.com","<project-id>.web.app","www.example.com","my-app-git-staging-myteam.vercel.app"]}'
The updateMask limits the change to that field ("Fields set in the config but not included in this update mask will be ignored"). The x-goog-user-project header is there for user credentials from gcloud; if Google returns a quota-project or "API not enabled" error, check that header and that the Identity Toolkit API is enabled. Try the PATCH on a test project first, because it replaces the whole list. Don't try to add every commit URL this way; keep the list to hosts people actually sign in on.
Production on Vercel: authDomain and the /__/auth rewrite
Getting past auth/unauthorized-domain is step one. If production uses signInWithRedirect, Firebase's redirect best-practices page says one of its options is "required for redirect sign-in to work on Google Chrome M115+" and "already required on Firefox 109+ and Safari 16.1+". For an app hosted outside Firebase, the options include switching to signInWithPopup() (Option 2) or proxying /__/auth/ through your own domain (Option 3). On Vercel, Option 3 is a rewrite.
Next.js: rewrite in next.config.js (Next.js supports rewrites to an external URL):
// next.config.js
module.exports = {
async rewrites() {
return [
{
source: "/__/auth/:path*",
destination: "https://<project-id>.firebaseapp.com/__/auth/:path*",
},
];
},
};
If you use trailingSlash: true, Next.js says the source needs the trailing slash too.
Vite, Create React App or other static builds: rewrite in vercel.json (Vercel's "rewrites to external origins" make it act as a reverse proxy). Put it above any single-page-app catch-all:
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"rewrites": [
{
"source": "/__/auth/:path*",
"destination": "https://<project-id>.firebaseapp.com/__/auth/:path*"
},
{ "source": "/(.*)", "destination": "/index.html" }
]
}
A rewrite keeps the URL in the browser unchanged, which matches Firebase's requirement: "Ensure that this forwarding is transparent to the browser; this cannot be done via a 302 Redirect." A redirects entry won't work here. Firebase asks for GET and POST requests to be forwarded, so check that sign-in completes through the rewrite.
Test this on a preview deployment before relying on it.
Then finish Firebase's steps for production:
- Add
https://www.example.com/__/auth/handlerto Authorized redirect URIs on the Google OAuth web client ("the trailing/__/auth/handleris important"). Full walkthrough: change Firebase authDomain. - Set
authDomainto your production domain on production only. Preview hosts change, and Google's redirect URIs can't contain wildcards, so previews should keep the defaultauthDomain:
// firebase.js
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
const PROD_HOST = "www.example.com";
const onProd =
typeof window !== "undefined" && window.location.hostname === PROD_HOST;
const app = initializeApp({
apiKey: "<api-key>",
authDomain: onProd ? PROD_HOST : "<project-id>.firebaseapp.com",
projectId: "<project-id>",
appId: "<app-id>",
});
export const auth = getAuth(app);
Checking the hostname, rather than Vercel's NEXT_PUBLIC_VERCEL_ENV (or VITE_VERCEL_ENV), keeps <project-name>.vercel.app on the default authDomain too, because that URL also serves the production deployment. On previews, redirect sign-in still has the third-party storage limits Firebase describes, so test the sign-in method you ship (popup or redirect) on production.
How to confirm it worked
- Open your branch URL or branch domain, start Google sign-in, and check the browser console:
auth/unauthorized-domainshould be gone. - On production, open
https://www.example.com/__/auth/handler. You should get a response from Firebase's handler, not your app's own 404 page or home page. If you see your app, the rewrite isn't matching (check rule order and the deployedvercel.jsonornext.config.js). - Sign in on production. If Google now shows
Error 400: redirect_uri_mismatch, the handler URI from step 1 above is missing on the OAuth client: see Error 400: redirect_uri_mismatch.
Related problems
- Framework setup for Next.js: Next.js + Firebase Google sign-in.
- Same error on Netlify deploy previews: auth/unauthorized-domain on Netlify.
FAQ
Why does Firebase sign-in work on my Vercel production domain but not on previews?
Each preview commit is served from a new generated *.vercel.app hostname, and Firebase only allows hosts on its Authorized domains list. Add the branch URL or a branch domain, which stays the same across pushes.
Should I add every Vercel preview URL to Firebase?
No. Commit URLs change on every push. Add the Git branch URL (<project>-git-<branch>-<scope>.vercel.app) or assign a custom domain to the branch, and keep the list short.
Do I need the /__/auth rewrite on Vercel?
Only if you set authDomain to your own domain, which Firebase lists as one way to keep signInWithRedirect working in browsers that block third-party storage. If you use signInWithPopup with the default authDomain, you can skip it.
Can I use vercel.json redirects instead of rewrites for /__/auth?
No. Firebase says the forwarding must be transparent to the browser and "cannot be done via a 302 Redirect". A Vercel rewrite keeps the browser URL unchanged; a redirect doesn't.
Should previews use my custom authDomain too?
No. With a custom authDomain, Google's OAuth client needs that exact host's /__/auth/handler URI, and redirect URIs can't use wildcards. Keep previews on <project-id>.firebaseapp.com.
Sources
- Accessing Deployments through Generated URLs (Vercel): commit, branch and production URL formats, truncation
- Assigning a domain to a Git branch (Vercel)
- Rewrites on Vercel (Vercel): vercel.json rewrites, rewrites to external origins
- vercel.json reference (Vercel): rewrites, SPA catch-all example
- Framework environment variables (Vercel): NEXT_PUBLIC_VERCEL_ENV, VITE_VERCEL_ENV
- System environment variables (Vercel): VERCEL_PROJECT_PRODUCTION_URL and production URL
- rewrites (Next.js): external URL rewrites, trailingSlash
- Best practices for using signInWithRedirect (Firebase): Options 2 and 3, browser versions, handler URI
- Authenticate with Firebase using Email Link (Firebase): Security > Authentication > Settings > Authorized domains
- Set a web app's OAuth redirect domains (Firebase Help): full domain names, any URL and port
- Firebase JS SDK auth error messages (firebase-js-sdk on GitHub, errors.ts)
- Identity Toolkit Config resource (Google Cloud): authorizedDomains
- Method: projects.updateConfig (Google Cloud): PATCH endpoint, updateMask
- Using OAuth 2.0 for Web Server Applications (Google Identity): no wildcard characters in redirect URIs
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.