Fix NextAuth (Auth.js) Google redirect_uri_mismatch

Updated 27 Oct 2026 · Pewterkit Guides

Auth.js / NextAuth sends Google a redirect_uri of <your origin>/api/auth/callback/google, and Google returns Error 400 redirect_uri_mismatch unless that exact string is listed under Authorized redirect URIs on your OAuth client. Add one entry per origin you sign in from (localhost, production, www), and make sure Auth.js computes the origin you expect (AUTH_URL / NEXTAUTH_URL, trustHost).

Why it happens

Auth.js builds the redirect URI from three parts: the origin of your app, the auth base path (default /api/auth in next-auth), and /callback/google. Google compares the result with your registered list character by character: the http or https scheme, case and trailing slash must all match, and wildcards (*) are not allowed. Plain http is accepted only for localhost.

The mismatch usually comes from one of these:

Cause What Google receives Registered value
Only localhost registered https://example.com/api/auth/callback/google http://localhost:3000/api/auth/callback/google
App reached on www (or apex) only https://www.example.com/... https://example.com/...
Proxy host not trusted or not forwarded, so Auth.js sees the internal host http://localhost:3000/... or http://10.x.x.x/... your public URL
Leftover AUTH_URL / NEXTAUTH_URL from another environment that old origin your current origin
Custom base path https://example.com/custom/auth/callback/google https://example.com/api/auth/callback/google
Vercel preview deployment https://my-app-git-branch-team.vercel.app/... production only (wildcards can't be registered)

Step 1: See the redirect_uri Auth.js actually sent

  1. Open DevTools → Network, click your Google sign-in button, and find the request to accounts.google.com.
  2. Copy the value of its redirect_uri query parameter. That is the exact string Google checked.
  3. Optional (NextAuth v4 documents this endpoint): open /api/auth/providers on the same origin. It returns each configured provider with its sign-in and callback URLs, so you can compare callbackUrl with your Google entries.

If the value is the wrong origin, fix the environment (Step 3 or 4) first; if it is correct, register it (Step 2).

Step 2: Add the exact entries in Google Cloud

As of Oct 2026, the OAuth client lives under Google Auth Platform → Clients in the Google Cloud console. Open your Web application client and add, under Authorized redirect URIs, one line per origin:

http://localhost:3000/api/auth/callback/google
https://example.com/api/auth/callback/google
https://www.example.com/api/auth/callback/google
  • Use the port your dev server really runs on.
  • Add the www line only if users can reach that host.
  • No trailing slash, no wildcard, no fragment.
  • Save, then wait: Google says changes to these settings can take 5 minutes to a few hours to take effect.

You don't need an Authorized JavaScript origin for this server-side flow; the redirect URI is what Google checks.

Step 3: Auth.js v5 (next-auth@5) configuration

auth.ts in the project root:

import NextAuth from "next-auth"
import Google from "next-auth/providers/google"

export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [Google],
})

app/api/auth/[...nextauth]/route.ts:

import { handlers } from "@/auth"
export const { GET, POST } = handlers

.env.local (v5 reads AUTH_GOOGLE_ID and AUTH_GOOGLE_SECRET automatically):

AUTH_SECRET="generate-a-long-random-value"
AUTH_GOOGLE_ID="1234567890-abc.apps.googleusercontent.com"
AUTH_GOOGLE_SECRET="your-client-secret"

How v5 decides the origin:

  • No AUTH_URL needed in most setups. The upgrade guide says the host is detected from the request headers.
  • Behind a reverse proxy (Docker, nginx, a load balancer), set AUTH_TRUST_HOST=true (same as trustHost: true in the config) so Auth.js trusts the X-Forwarded-Host header. Without it, sign-in may fail or the callback may be built from the wrong host. Make sure your proxy forwards that header.
  • On Vercel and Cloudflare Pages trust is inferred automatically when the VERCEL or CF_PAGES variable is present.
  • Custom base path: set AUTH_URL to the full auth route, for example AUTH_URL=https://example.com/web/auth, and register https://example.com/web/auth/callback/google.
  • Stale values: v5 treats NEXTAUTH_URL as an alias of AUTH_URL. If either is set to an old origin, remove or correct it, because a value that is set can override the detected host.

Step 4: NextAuth v4 differences

// pages/api/auth/[...nextauth].js
import NextAuth from "next-auth"
import GoogleProvider from "next-auth/providers/google"

export default NextAuth({
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    }),
  ],
})
  • Set NEXTAUTH_URL to the canonical URL of your site in production, for example NEXTAUTH_URL=https://example.com.
  • On Vercel you can leave it unset, but only if Automatically expose System Environment Variables is enabled in the project settings (as of Oct 2026).
  • With a custom base path, include the full route: NEXTAUTH_URL=https://example.com/custom-route/api/auth, and pass basePath to <SessionProvider>.
  • NEXTAUTH_URL_INTERNAL changes only server-side calls; it does not change the redirect URI Google sees.

Step 5: Vercel preview deployments

Each preview gets a new hostname, and Google won't accept a wildcard. Auth.js v5 solves this with a redirect proxy:

  1. Pick a stable deployment, for example https://auth.example.com.
  2. In both the stable and preview environments, set:
AUTH_REDIRECT_PROXY_URL="https://auth.example.com/api/auth"
AUTH_SECRET="same-value-in-every-environment"
  1. Register only the stable callback in Google: https://auth.example.com/api/auth/callback/google.

The proxy is not enabled if the variable is missing in the stable environment, and AUTH_SECRET must be identical across the stable and preview deployments. Auth.js describes this variable as meant for advanced use cases.

Test this on a preview deployment before relying on it.

How to confirm it worked

  • The redirect_uri in the Network request matches one of your Google entries exactly.
  • After choosing an account, the browser lands on /api/auth/callback/google and then back in your app, with no Google error page.
  • GET /api/auth/session returns a session object instead of an empty object.
  • Test each origin you registered (localhost, apex, www, stable auth domain) at least once.

FAQ

What is the NextAuth Google callback URL?

It is https://<your-domain>/api/auth/callback/google, and http://localhost:3000/api/auth/callback/google in development. If you change the auth base path, the callback moves with it, so register the full path that /api/auth/providers or the Network tab shows.

Do I need AUTH_URL or NEXTAUTH_URL with Auth.js v5?

Usually not: v5 detects the host from request headers. Set AUTH_URL only for a custom base path, and set AUTH_TRUST_HOST=true when you run behind a reverse proxy that is not Vercel or Cloudflare Pages.

Why does production send a localhost redirect_uri?

Either AUTH_URL / NEXTAUTH_URL still holds http://localhost:3000, or a proxy hides the public host and Auth.js isn't set to trust forwarded headers. Remove the stale variable or set AUTH_TRUST_HOST=true, redeploy, and check the Network request again.

Can I add a wildcard for Vercel preview URLs in Google Cloud?

No. Google rejects * in redirect URIs. Use AUTH_REDIRECT_PROXY_URL so every preview signs in through one stable callback.

I added the URI but still see the error. Why?

Google notes that client changes can take 5 minutes to a few hours to apply. Also check that you edited the same client ID your app uses, and that scheme, host, port and path match with no trailing slash.

Sources

  1. Auth.js | Google
  2. Auth.js | Deployment
  3. Auth.js | Nextjs (reference)
  4. Migrating to v5 | Auth.js
  5. Google | NextAuth.js
  6. Options | NextAuth.js
  7. REST API | NextAuth.js
  8. Using OAuth 2.0 for Web Server Applications | Google for Developers
  9. Manage OAuth Clients | Google Cloud Platform Console Help

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

Related guides