What this usually means
OAuth providers (Google, GitHub, Auth0, Okta) require exact URL matching between the redirect_uri parameter in the authorisation request and the redirect URIs registered in the OAuth app settings. 'Exact' means exact: `http://` vs `https://`, `www.example.com` vs `example.com`, trailing slash vs no trailing slash. Even a difference in URL encoding can cause a mismatch. The provider compares the full URL string — if it is not an exact match, it rejects the request.
The first ten minutes \u2014 establish facts before touching code.
- 1Compare the redirect_uri in the auth request URL against the registered URIs in the OAuth provider dashboard. Check character by character.
- 2Check the protocol: `http://` vs `https://`. Production must use `https://`.
- 3Check the domain: `example.com` vs `www.example.com`. Both must be registered if both are accessible.
- 4Check the trailing slash: `/callback` vs `/callback/`. These are different URLs to an OAuth provider.
- 5Check URL encoding. Spaces, special characters, or query parameters in the redirect URI must be encoded consistently.
The specific files, logs, configs, and dashboards that usually own this bug.
- searchOAuth provider dashboard — registered redirect URIs for the app
- searchAuth request URL — the `redirect_uri` query parameter in the authorisation request
- searchApplication code — how the redirect_uri is constructed (env var, request header, hardcoded?)
- searchOAuth library configuration — passport.js, next-auth, Auth0 SDK settings
- searchEnvironment variables — `OAUTH_REDIRECT_URI`, `AUTH_REDIRECT_URI`, or similar
Practical causes, not theory. These are the things you will actually find.
- warningRedirect URI is registered with `http://` but the request uses `https://` (or vice versa)
- warningDomain mismatch: registered as `example.com` but request comes from `www.example.com`
- warningTrailing slash mismatch: registered `/callback`, request sends `/callback/`
- warningPort number included in development but not in production: `localhost:3000/callback` vs `example.com/callback`
- warningURL has a query parameter that is not registered
- warningMultiple redirect URIs are registered but the application picks the wrong one per environment
- warningReverse proxy or CDN rewrites the URL before it reaches the OAuth library
Concrete fix directions. Pick the one that matches your root cause.
- buildRegister every possible redirect URI variant with the OAuth provider — one per environment, with and without www, with and without trailing slash
- buildConstruct the redirect_uri from a single environment variable (`OAUTH_REDIRECT_URI`) that is set per environment
- buildUse the OAuth library's built-in redirect URI resolution instead of building the URL manually
- buildAdd a debug mode in staging that logs the exact redirect_uri being sent so you can compare it to the registered URI
- buildUse an OAuth proxy or identity platform (Auth0, Clerk, WorkOS) that handles redirect URI management for you
A fix you cannot prove is a guess. Close the loop.
- verifiedStart the OAuth flow in production. Before the redirect, inspect the auth URL and compare `redirect_uri` to the registered URI.
- verifiedRegister the tested URL in the OAuth provider dashboard and complete the flow end-to-end.
- verifiedTest in a staging environment with a different domain to confirm multi-environment configuration.
- verifiedTest from both `example.com` and `www.example.com` if both are accessible.
- verifiedRepeat for each OAuth provider (Google, GitHub, etc.) — each has its own registered URIs.
Things that make this bug worse or harder to find.
- warningNot registering the production redirect URI before deploying
- warningRegistering only `http://localhost` and forgetting production domains
- warningNot testing OAuth login in a staging environment that uses a different domain
- warningAssuming the redirect URI is case-insensitive — some providers are case-sensitive
- warningNot handling the case where users access the site via both `www` and non-`www` domains