Skip to content
FlowPatrol

Guide

Why does Google sign-in redirect to localhost after deploy?

Continue with Google worked in the editor. After you publish, the browser opens Google and then lands on http://localhost:3000, on http://localhost:5173, or on the preview host. Sometimes it never leaves Google, and the page says Error 400: redirect_uri_mismatch. Sometimes your own Google account still works and a customer’s account does not. The button is usually fine. The allow lists still name the host the editor used when it generated the flow.

This is not a launch checklist. For signup, login, and checkout on a published Lovable URL, use how to test a Lovable app before launch. If email and password fail, the key is wrong, the session dies on reload, or the callback never exchanges the code, use why Supabase login stopped working after an AI edit. A confirmation or reset link that points at the wrong host is why Supabase sign-up confirmation emails are not arriving. The short version of the allow list is login works locally but breaks in production.

Read which page you landed on

Use the published URL in a private window. The editor preview is a different origin, and it is often already signed in. Click the Google button once. Read the address bar before you change a dashboard.

  1. Still on Google, and the page says Error 400: redirect_uri_mismatch. Google rejected its callback. That URI belongs to Supabase, not to your domain. Use the Google Cloud section before you touch Site URL.
  2. Google accepts the account, then the bar is http://localhost:3000, http://localhost:5173, or http://127.0.0.1 with another port. Supabase chose Site URL or redirectTo. Use those sections.
  3. The bar is the editor preview, or an old preview deployment, after you clicked on the custom domain. redirectTo is still the old origin. Use the custom domain section.
  4. The bar is your real domain, then a 404, or a code query parameter on the login page. The lists matched and the callback route did not. That route is the OAuth section of the login guide.
  5. Access blocked, or Error 403: access_denied, for a customer while the Google account that owns the Cloud project still works. That is the consent-screen audience, not a redirect.
  6. Nothing leaves your site. The console says the provider is not enabled, or the redirect URL is not allowed. The click never reached Google.

Two redirects, and they use different lists

Sign-in with Google through Supabase is two hops. Mixing them up is why the same button works on the preview and fails on the domain you shipped.

  1. The app calls signInWithOAuth with provider google. The browser goes to your Supabase project, then to Google. The redirect_uri Google checks is the callback on the Google provider page in the Supabase dashboard. On a hosted project that is https://<project-ref>.supabase.co/auth/v1/callback. For the local CLI the documented value is http://127.0.0.1:54321/auth/v1/callback. Copy it from the provider page. Do not type your own /auth/callback into Google.
  2. After Google accepts the account, Supabase sends the browser to options.redirectTo. With no redirectTo, it uses Site URL. The dashboard default for Site URL is http://localhost:3000. redirectTo has to be an absolute URL, and it has to match a Redirect URLs entry. Your app’s origin belongs on that list. It also belongs as a JavaScript origin in Google. It does not belong in Google’s authorized redirect URIs.
  3. The Client ID and Client Secret for that Web client are saved on Authentication, then Providers, then Google. signInWithOAuth does not read a Google secret from the page. An edit that changes a public variable in the Lovable, Bolt, v0, or Cursor project does not change the client Google is judging.

Fix redirect_uri_mismatch in Google Cloud

Google shows Error 400: redirect_uri_mismatch when the redirect_uri on its request is not exactly one Authorized redirect URI on that OAuth client. The comparison includes the scheme, the host, the port, and the path. A wildcard does not stand in for the Supabase callback.

  1. Open the Web application client whose Client ID is the one saved in Supabase. The current Google console lists clients under Google Auth Platform, then Clients. Older projects still show the same client under APIs and Services, then Credentials. A second client you made for localhost is the wrong one if the dashboard still has the first client’s ID.
  2. Under Authorized redirect URIs, paste the callback copied from the Supabase Google provider page. Delete an entry that is only your /auth/callback, a preview URL, or http://localhost:3000. Those are app URLs. Google does not redirect the Supabase flow there, so they do not clear this error.
  3. If you also use the Supabase CLI on your machine, add http://127.0.0.1:54321/auth/v1/callback. localhost and 127.0.0.1 are different hosts. Port 54321 is the local Auth API, not the app. The hosted project’s https callback still has to be on the client that production uses. One Web client can hold both URIs when both projects use that same client ID and secret.
  4. If you attached a custom domain to the Supabase project, the callback host may no longer be only <project-ref>.supabase.co. Open the Google provider page again and copy the callback it shows now. The old supabase.co value left in Google is a mismatch.
  5. Under Authorized JavaScript origins, add each origin the button is clicked on: scheme and host, plus a port only when it is not the default. https://example.com/auth/callback is not an origin. https://www.example.com and https://example.com are two origins. Supabase’s Google guide says to add http://localhost:<port> while you develop, and to remove it in production. Removing it does not stop a bounce to localhost. That bounce is Site URL or redirectTo.
  6. Save, then retry in a private window. If Google still shows the mismatch, the redirect_uri in that address bar is the string you must add, on the client ID in that same address bar. A save on a different client does nothing.

Set Site URL and the Redirect URLs list

This list is where your user lands after Google. It is not the URI Google checks. Open Authentication, then URL Configuration. Confirmation and password-reset mail use the same screen. Those messages are the confirmation guide. A password form that never opens Google is the login guide.

  1. Set Site URL to the production origin, including https, with no path. Left on http://localhost:3000, a Google sign-in that does not pass redirectTo ends on localhost. A preview host left as Site URL does the same with the preview.
  2. Add a Redirect URLs entry for every absolute redirectTo the code can send, including the path. https://example.com/auth/callback does not match a different path, a trailing slash the edit added, or http instead of https. In Supabase’s patterns, * matches characters other than . and /, and ** matches across those separators. An entry that should cover subpaths usually ends in /**. Prefer the exact callback on the production origin. Use a pattern for preview hosts you still sign in from.
  3. https://example.com/** does not match https://www.example.com/**, because the dot is a separator. If both hosts answer, add both, or add a pattern that actually matches both. Adding the domain in Lovable, Bolt, or the host does not write this list.
  4. Keep a localhost entry only while you still sign in from a local server. That entry does not make production work, and it does not by itself send production users to localhost. Production lands on localhost when Site URL or redirectTo still says localhost.

Stop redirectTo pointing at localhost or the preview

Search the project for signInWithOAuth and redirectTo. The value is options.redirectTo on that call. Generated apps often keep the sample the editor started from.

  1. http://localhost:3000 and http://localhost:5173 show up as string literals, including in an app you never ran locally. If the call passes that string and the allow list accepts it, the browser goes there after Google succeeds, even when Site URL is already the production origin.
  2. A hard-coded preview URL returns the user to the preview. window.location.origin is the page that was clicked, so a click in the editor preview asks to return there. The custom domain fails because the code never names it.
  3. redirectTo has to be absolute, https on the published site, and a route that exists. A relative /auth/callback is not that URL. A code query parameter has to reach exchangeCodeForSession. That exchange is the login guide. Leave flowType and detectSessionInUrl alone unless that section says to change them.
  4. If the click fails before Google opens and the message says the redirect URL is not allowed, add that exact redirectTo. Do not open the list to every host.
  5. Prefer the origin the visitor is already on, then allow-list it. Do not point production at localhost to satisfy Google. Google’s redirect URI stays the Supabase callback either way.

Add the custom domain, including www

Shipping on the editor’s host, then attaching a domain later, does not update Supabase or Google Cloud. Login keeps working on the old host and fails on the new one. That looks like the deploy broke a button that is still doing what it did yesterday.

  1. On the domain users type, set Site URL to that origin. Add its callback under Redirect URLs. Add the same origin under Google’s Authorized JavaScript origins, with no path. Leave Google’s Authorized redirect URI as the Supabase callback, unless the Supabase project’s own callback changed.
  2. Repeat for www if that host answers, or if the site redirects one host to the other. They are different origins. The sign-in stores a verifier in the browser for the origin that called signInWithOAuth. The callback has to load on that same origin. A bounce from www to the bare domain, or the reverse, before exchangeCodeForSession runs, drops the verifier. The URL can still show code, and the app sends you back to the form. The login guide covers that bounce. Finish the exchange on the origin that started it.
  3. Once the custom domain is the site you ship, stop passing a redirectTo that still names the preview. You can leave the preview on the allow list while you still test there. Do not leave the preview as Site URL.
  4. Publish again after the code change. The dashboard edits apply without a new build. A hard-coded redirectTo changes only when the published bundle does.

Check the edit, the audience, and the callback route

An AI edit does not, by itself, change the Supabase dashboard or Google Cloud. It does change the code those dashboards are compared with. Read the diff before you create another OAuth client.

  1. In the diff, look for signInWithOAuth, redirectTo, localhost, window.location.origin, exchangeCodeForSession, and the callback file. A moved or deleted route is a 404 on your domain after Google has already succeeded. Point redirectTo and Redirect URLs at the route that still exchanges the code.
  2. If the button no longer calls signInWithOAuth and instead loads Google’s script with a client ID in the page, that ID and the JavaScript origin have to match the Web client. A client ID baked into the build changes only on the next build. The secret still does not belong in the page. For signInWithOAuth it stays on the Supabase Google provider.
  3. If the provider is disabled, or the request never leaves your site, turn Google on under Authentication, then Providers, and save the Client ID and Secret from the Web client you edited. The wrong Supabase project in the app is the public-key section of the login guide. You would be changing a Google client the browser never calls.
  4. Access blocked, or Error 403: access_denied, for an account you did not add: the OAuth client’s audience is still in testing. Add that account as a test user, or publish the client. Supabase’s Google guide expects openid, the email scope, and the profile scope. Extra sensitive or restricted scopes can require Google’s verification, which takes time. Redirect URI edits do not clear this.
  5. Do not turn on Skip nonce check to get past a web redirect. Supabase’s web Google steps do not use that toggle. A nonce error means the button handed Google an ID token, which is not the signInWithOAuth redirect.

Check the whole path once

  1. Private window. Open the published URL on the host you will send to people, not the preview. Click the Google button.
  2. On Google’s screen, read redirect_uri in the address bar. It should be the callback from the Supabase Google provider page. If the page says redirect_uri_mismatch, stop and fix that client. Do not continue to Site URL until this hop succeeds.
  3. Pick an account. You should land on the published origin, on the callback path, then on the signed-in screen. Localhost, 127.0.0.1, or the preview host means Site URL or redirectTo is still that host.
  4. Reload. You should still be signed in. If the address bar had a code parameter and the form came back, use the callback section of the login guide.
  5. If both www and the bare domain answer, repeat the click on each host you expect people to use. Then repeat with a Google account that does not own the Cloud project, so a testing-mode audience cannot hide behind your own login.

A prompt you can paste

Paste this into Lovable, Bolt, v0, Cursor, or the editor you used, after you have the address bar and any Google error text. Replace the brackets. It keeps the edit on Google sign-in.

Google sign-in fails on my published app. It worked in the editor preview. Do not change unrelated pages, checkout, email copy, or layout. Do not disable Google sign-in.

What I see: [redirect_uri_mismatch / lands on localhost / lands on the preview host / 404 on the callback / access blocked / code in the URL then the login form]
Page I clicked from: [URL]
Address bar after the click: [URL or Google error text]
redirect_uri on the Google screen, if shown: [paste it]

Fix Google sign-in only:
1. The button calls supabase.auth.signInWithOAuth with provider google. Do not send Google the app /auth/callback as its redirect URI.
2. options.redirectTo is an absolute https URL on the production origin, on a route that exists and calls exchangeCodeForSession when the URL has a code query parameter. Do not point it at localhost, 127.0.0.1, or a preview host. Do not use window.location.origin unless that origin is where the user should return. Do not change flowType or detectSessionInUrl unless the code is being dropped.
3. Leave a comment with the Site URL and every Redirect URL to add under Authentication, then URL Configuration, including that redirectTo. Include www and the bare domain when both serve the app. Do not invent dashboard values.
4. Leave a comment with the Authorized redirect URI for the Google Web client: the callback copied from the Supabase Google provider page. For the local CLI that is http://127.0.0.1:54321/auth/v1/callback. Also list each Authorized JavaScript origin (scheme and host, no path).
5. Do not put the Google client secret in client code. It belongs, with the Client ID, on Authentication, then Providers, then Google, and the provider has to be enabled.
6. Exchange the code on the same origin the click started on. Do not bounce www to the bare domain, or the reverse, before exchangeCodeForSession.

Do not replace the Supabase callback with the app domain in Google's authorized redirect URIs.

How to keep the next edit from doing this

  1. After any prompt that touches auth, routes, or the Google button, use a private window on the published URL. Sign in with Google once, then reload.
  2. In the diff, look for signInWithOAuth, redirectTo, localhost, window.location.origin, exchangeCodeForSession, and the callback path. In the same prompt as a visual change, tell the editor not to hard-code localhost or a preview URL, and not to put the app callback into Google’s redirect URI list.
  3. Write down the production origin, whether you use www, the callback path, and the Supabase callback you copied into Google. Change the dashboards in the same sitting as the code. Keep a sign-in rewrite out of an unrelated feature so the diff stays small enough to check.

The free scan only loads the page. It does not click Continue with Google, and it cannot finish a screen on accounts.google.com. To rerun the steps that stay on your site, create an account and describe the flow, such as “Open the login page, then confirm the dashboard.” Test emails end in @example.com. The watch stays on your hostname and skips the Google account chooser. Walk one Google sign-in yourself when the edit touches the button or redirectTo.

Starter is $19/mo. Pro is $49/mo. What each plan includes, and how a watched flow compares with testing by hand, is on the facts page. FlowPatrol is not affiliated with Google, Supabase, Lovable, Bolt, or v0.