Error 400: redirect_uri_mismatch, what it means and how to fix it

Updated 26 Oct 2026 · Pewterkit Guides

Error 400: redirect_uri_mismatch means the redirect_uri in your app's Google sign-in request does not exactly match any Authorized redirect URI on the OAuth client named by the request's client_id. To fix it, read the exact redirect_uri and client_id your app sent, then add that URI to that client in Google Cloud console, or change the app so it sends a URI that is already listed.

Why it happens

Google's OAuth documentation defines the error as: "The redirect_uri passed in the authorization request does not match an authorized redirect URI for the OAuth client ID." The value "must exactly match one of the authorized redirect URIs for the OAuth 2.0 client", and "the http or https scheme, case, and trailing slash ('/') must all match."

So the user's Google account is not the problem. The check is between two strings: the redirect_uri your app put in the request, and the list saved on one specific OAuth client. The usual reasons they differ:

  • The URI was never added (new domain, new callback route, new environment).
  • It was added to a different OAuth client than the one whose client_id the app sends.
  • It was added with a small difference: http vs https, www vs no www, a trailing slash, a different port or path, or different letter case.
  • The change was saved minutes ago. Google says "It may take 5 minutes to a few hours for changes made to these settings to take effect."

Step 1: Find the exact redirect_uri and client_id your app sent

Don't guess the value. A redirect-based Google authorization request carries redirect_uri and client_id as query parameters on Google's authorization endpoint, https://accounts.google.com/o/oauth2/v2/auth.

The most reliable place to read it is the request itself:

  1. Open your browser's developer tools on the page that starts sign-in, go to the Network panel and tick Preserve log so requests survive the page change. If sign-in opens a popup, the request happens in the popup window, so inspect that window or copy the URL from its address bar.
  2. Type accounts.google.com in the filter box and start sign-in.
  3. Select the first request to accounts.google.com/o/oauth2/... and open the Payload tab. Chrome shows the query string parameters decoded by default. Copy redirect_uri and client_id.

If you can copy the full sign-in URL from the address bar instead, this prints the two values decoded:

// Paste the full https://accounts.google.com/o/oauth2/... URL between the quotes
const url = new URL("PASTE_THE_FULL_URL_HERE");
console.log("redirect_uri:", url.searchParams.get("redirect_uri"));
console.log("client_id:", url.searchParams.get("client_id"));

Google's error page may also offer developers a link to more details about the request; if you see one, the values there should match what you read above.

Where the value usually comes from:

Your setup Where redirect_uri is set
Firebase Authentication (signInWithPopup / signInWithRedirect) Firebase's handler on your authDomain: https://<authDomain>/__/auth/handler
Auth.js (NextAuth) Google provider Your site URL plus /api/auth/callback/google
Your own server code (Passport, google-auth-library, etc.) The callback URL in your server config or code

Step 2: Compare it with Google's exact-match rules

Any one of these differences is enough to trigger the error:

What differs Registered on the client Sent by your app
Scheme https://example.com/auth/callback http://example.com/auth/callback
Host (www) https://example.com/auth/callback https://www.example.com/auth/callback
Trailing slash https://example.com/auth/callback https://example.com/auth/callback/
Port http://localhost:3000/auth/callback http://localhost:5173/auth/callback
Path https://example.com/auth/callback https://example.com/api/auth/callback/google
Case https://example.com/Auth/Callback https://example.com/auth/callback
Client Listed on client A Request uses client B's client_id

Google also rejects some redirect URIs outright when you try to register them, so a URI you can't save is a sign the app should send something else:

  • It must use https://. "Localhost URIs (including localhost IP address URIs) are exempt from this rule."
  • "Hosts cannot be raw IP addresses. Localhost IP addresses are exempted from this rule."
  • No wildcard characters (*), no fragment (#...), no userinfo (user:pass@), no path traversal (/..).
  • "Host TLDs (Top Level Domains) must belong to the public suffix list."

A hidden cause of the scheme row: if your app runs behind a proxy or load balancer that ends HTTPS, server code that builds the callback from the incoming request can produce an http:// URI. In Express, the trust proxy setting makes req.protocol reflect the X-Forwarded-Proto header your proxy sets:

// Express: trust the one proxy in front of the app (load balancer / platform router)
app.set("trust proxy", 1);

Express warns that the trusted proxy must overwrite X-Forwarded-* headers, so set this to match your actual proxy setup.

Step 3: Add the redirect URI in Google Cloud console

Console labels as of Oct 2026:

  1. Open Google Cloud console and select the project that owns the OAuth client.
  2. Go to Google Auth Platform > Clients. Older guides and menus call this APIs & Services > Credentials.
  3. Open the OAuth 2.0 client whose Client ID equals the client_id from Step 1.
  4. Under Authorized redirect URIs, add the exact redirect_uri from Step 1, character for character.
  5. Save.
  6. Wait before retesting. Google says changes can take 5 minutes to a few hours.

If the value your app sent is the wrong one (an http:// URI in production, a stray trailing slash, a preview hostname), fix the app instead, so it sends the URI you meant to register.

Authorized JavaScript origins are a different list. An origin "includes the scheme and fully qualified hostname only"; a redirect URI includes "the scheme, fully qualified hostname, and path". Adding an origin does not fix redirect_uri_mismatch.

Using Firebase Authentication?

With Firebase's popup and redirect sign-in, Google sends the user back to https://<your authDomain>/__/auth/handler, and Firebase's troubleshooting FAQ lists https://PROJECT_ID.firebaseapp.com/__/auth/handler as the redirect URI for a project's web client. The error typically appears after authDomain is changed to your own domain without adding the new handler URI. The full steps for that case are in redirect_uri_mismatch after changing authDomain, and what the handler path does is explained in the Firebase auth handler.

How to confirm it worked

  1. After the wait in Step 3, start sign-in again from your app.
  2. You should get Google's account chooser or consent screen instead of the Error 400 page.
  3. If the error is still there, repeat Step 1 and compare the new redirect_uri and client_id with the client's list character by character, using the table in Step 2.
  4. If Google now passes but your app shows a different error, look it up in Firebase Auth errors.

FAQ

What does Error 400: redirect_uri_mismatch mean?

The redirect_uri in your app's sign-in request doesn't match any Authorized redirect URI on the OAuth client ID it used. It is a setup problem in the app's OAuth client, not a problem with the user's Google account.

Can I use a wildcard in an Authorized redirect URI?

No. Google's validation rules don't allow wildcard characters (*) in redirect URIs, so each URI, including each subdomain and port, must be listed on its own.

I added the URI and it still fails. Why?

Google says changes can take 5 minutes to a few hours to take effect. If it still fails after that, the URI was probably added to a different client than the request's client_id, or it differs by scheme, www, trailing slash, port, path or letter case.

Do I need separate entries for localhost and my live site?

Yes, if the app signs in from both. Each URI your app sends must be listed, and only localhost URIs may use http://. See redirect_uri_mismatch on localhost for dev-server details.

Is redirect_uri_mismatch the same as auth/unauthorized-domain?

No. redirect_uri_mismatch comes from Google's sign-in page and is fixed on the OAuth client. Firebase's auth/unauthorized-domain ("This domain is not authorized for OAuth operations for your Firebase project") is fixed in Firebase's Authorized domains list.

Sources

  1. Using OAuth 2.0 for Web Server Applications (Google Identity): redirect_uri parameter, redirect_uri_mismatch, Redirect URI validation rules, authorization endpoint
  2. Manage OAuth Clients (Google Cloud Console Help): Clients page, "5 minutes to a few hours"
  3. Get your Google API client ID (Google Identity Services): JavaScript origins vs redirect URIs
  4. Network features reference (Chrome DevTools): Preserve log, filter, Payload tab
  5. URLSearchParams (MDN): get() and percent-encoding
  6. Express behind proxies (Express docs): trust proxy, X-Forwarded-Proto and req.protocol
  7. Google provider (Auth.js): callback URL format
  8. Best practices for using signInWithRedirect (Firebase): handler URI https://<the-domain-that-serves-your-app>/__/auth/handler
  9. Firebase Authentication FAQ and troubleshooting (Firebase): default OAuth web client redirect URIs, Credentials page
  10. Firebase JS SDK auth error messages (firebase-js-sdk on GitHub, errors.ts)

How this page was made: drafted with AI from the sources listed, reviewed by Pewterkit. Docs checked 26 Oct 2026. Spotted something out of date? Tell us.

Related guides