redirect_uri_mismatch after deploy (Google sign-in)
What Google showsError 400: redirect_uri_mismatch
The redirect_uri passed in the authorization request does not match an
authorized redirect URI for the OAuth client ID.
Google only sends users back to URLs you listed for your OAuth client, and they must match exactly. Add your production callback URL in Google Cloud and make sure your app builds it from your real domain.
Why it happens
During sign-in your app tells Google where to send the user back (the redirect_uri). Google compares it with the authorized redirect URIs on your OAuth client and refuses anything that isn't an exact match: scheme, host, path, case and trailing slash all count.
Locally you registered http://localhost:3000/.... After deploying, the app sends https://yourdomain.com/..., which isn't on the list. Or the app still sends the localhost URL because its base URL variable was copied from .env.
Fix it by hand
- Find the URI your app sent. Start sign-in on the live site and look at the Google URL in the address bar. The
redirect_uri=parameter (URL-encoded) is the value that must be registered. - Add it in Google Cloud. Open the Clients page of the Google Auth Platform in the Google Cloud console, select your Web application client, and add the URI under Authorized redirect URIs. With Auth.js (NextAuth) in Next.js the format is:
Rules from Google: it must usehttps://yourdomain.com/api/auth/callback/googlehttps(only localhost is exempt), and it can't contain wildcards or a#fragment. Addwwwand non-wwwseparately if you serve both. - Fix your app's base URL. If the URI from step 1 still says
localhost, your app is building it from a local value. Check variables likeAUTH_URL,NEXTAUTH_URLorSITE_URLin your production settings. Auth.js v5 infers the host from the request and trusts it automatically on Vercel, soAUTH_URLis usually only needed for a custom base path; a leftover localhost value there is a common culprit. - Set the production secret too. Auth.js needs
AUTH_SECRETin production. Missing it is a separate error that often shows up right after this one. - Redeploy if you changed variables, then retry sign-in in a private window.
Stop it happening again
- Register a redirect URI for every environment you actually sign in on: localhost, a stable staging domain, and production.
- Keep the base URL in an environment variable per environment, never hardcoded.
- Do the same check for every OAuth provider and webhook (GitHub, Stripe, Supabase auth): each has its own list of allowed URLs.
Broke After Deploy $9
Broke After Deploy finds login and webhook callback URLs that still point at localhost or an old ngrok tunnel, and tells you which provider consoles need the production URL.
- Checks your project for 37 known causes of "works locally, breaks in production" and runs your production build.
- Paste the error and it finds the cause in your code, then lists what will break next.
- Verdict first. Every finding shows the file and line, what it breaks, and the fix as a diff that is applied only with your OK.
Broke After Deploy: how it works, with examples · Install guide
Works with Claude Code, Claude.ai, Cursor, Codex and other agents that read SKILL.md files. No installs, no accounts, works offline. No-questions refund.