The "invalid_request" error for redirect_uri means the authorization server rejected your callback URL because it does not exactly match a URI pre-registered for that OAuth client. The fix is to compare the two strings byte-for-byte — scheme, host, port, path, and trailing slash — and register the missing variant in the provider's console before retrying the flow.
The mechanism behind redirect URI matching
OAuth 2.0 treats the redirect URI as a security boundary, not a convenience. When a client application registers with an authorization server, it submits one or more callback URLs. During the authorization request, the server compares the submitted redirect_uri against that registry. If no match exists, the request fails before any user consent screen appears. The specification (RFC 6749, Section 3.1.2) requires the server to reject requests whose redirect URI is not identical to a registered value, unless the provider explicitly supports partial matching.
Identical is stricter than most developers assume. The comparison is typically a string equality check after minimal normalization, not a semantic URL comparison. "HTTPS://App.Example.com/Callback" and "https://app.example.com/callback" may fail against each other depending on the server's implementation, because scheme and host case handling varies. The trailing slash is the single most common mismatch: https://app.example.com/callback and https://app.example.com/callback/ are different strings, and many servers treat them as different URIs.
The mechanism exists because the redirect URI is where the authorization code or token gets delivered. If an attacker could substitute an arbitrary URI, they could intercept codes or tokens. Exact matching closes that hole. A plain-language version: the server only sends the secret envelope to an address it already has on file.
Quantified context: ports, schemes, and loopback addresses
Port numbers are part of the URI and part of the match. A registered http://localhost:3000/callback will not match a request from http://localhost:8080/callback. Most authorization servers compare the port literally, so a development server that shifts ports on restart breaks the flow.
Loopback redirects deserve special attention. The OAuth 2.0 for Native Apps guidance (RFC 8252) recommends that servers allow loopback IP literals with variable ports for native clients, but many servers do not implement that flexibility. http://127.0.0.1/callback and http://localhost/callback are also different host strings; neither is a synonym for the other at the matching layer. Register both if your tooling uses both.
Scheme matters too. Production callbacks must generally use https. A registered https://app.example.com/cb will reject a request for http://app.example.com/cb. Some providers also reject custom schemes such as myapp://callback unless the client is classified as a native app.
The error name itself is a clue. invalid_request is a request-level error, distinct from invalid_client or invalid_grant. It means the server could not process the request as formed, and the redirect_uri is the cited parameter. That usually points at configuration, not credentials or user input.
Edge cases that survive string comparison
Percent-encoding is a frequent trap. https://app.example.com/cb%2Fpath and https://app.example.com/cb/path may not normalize to the same value. Query strings are generally not permitted in registered redirect URIs at all; a URI with ?state=abc appended often fails because the registered value had no query component.
Fragment identifiers are prohibited by the specification. A redirect URI containing # must be rejected, and some servers surface that rejection as the same invalid_request error.
Subdomain and path depth mismatches are common in multi-tenant apps. A registered wildcard https://*.example.com/cb only works if the provider supports wildcard registration, which many large providers do not. Path case sensitivity is server-dependent; do not rely on case-insensitive matching.
One counterintuitive detail: the error can appear even when the client ID is correct and the user is authorized. Matching happens early in request validation, before authentication and consent, so a valid session does not help. A second professional caveat: some providers silently fall back to a default registered URI when the parameter is omitted, but reject an explicitly supplied URI that does not match. Omitting the parameter and sending a wrong one are not equivalent behaviors.
Common misconceptions
"The provider will normalize my URL." Most do minimal normalization; assume byte-level comparison and register exact strings. "localhost and 127.0.0.1 are the same." They are different host strings to the matcher; register both or standardize on one. "Trailing slashes are cosmetic." They change the string and therefore the match result. "Changing the redirect URI only needs a code edit." The server-side registry must be updated first; code alone cannot authorize a new URI. "The error means my client secret is wrong." That is a different error; redirect_uri validation is about the callback address.
If you see the error after a provider-side dashboard migration, check whether the console reset or versioned your registered URIs; some platforms tie registrations to environment and app version.
Comparison of fix strategies
| Strategy | What it changes | When it works | Trade-off |
|---|---|---|---|
| Register exact variant | Server-side client config | Mismatch is a real unregistered URI | Requires console access and propagation time |
| Standardize on one URI | Client code and config | Multiple environments drifted | Needs discipline across dev, staging, prod |
| Use provider SDK defaults | Library behavior | SDK supplies the canonical URI | Less control over callback path |
| Wildcard registration | Server-side client config | Provider supports wildcards | Broader exposure; many providers forbid it |
| Omit the parameter | Request formation | Provider has a safe single default | Ambiguous behavior across providers |
| Loopback variable port | Server-side client config | Native app per RFC 8252 | Not universally implemented |
The table makes the reconciliation point explicit: these thresholds are not interchangeable. Registering an exact variant fixes a genuine registry gap; standardizing fixes drift; omitting the parameter relies on provider-specific defaults and should not be assumed portable.
Timeline: diagnosing the mismatch
| Stage | Action | Typical outcome |
|---|---|---|
| Capture the failing request | Log the full authorization URL | Reveals exact redirect_uri sent |
| Capture the registered value | Read the client config in the provider console | Reveals exact registry string |
| Diff the two strings | Compare scheme, host, port, path, slash | Localizes the mismatch in seconds |
| Check encoding | Inspect percent-encoding and query strings | Catches normalization traps |
| Update the registry | Add or correct the URI | Propagation can take minutes |
| Retry in a fresh session | Clear cached auth state | Confirms the fix |
| Verify all environments | Repeat for dev, staging, prod | Prevents recurrence |
The propagation delay deserves emphasis. Some providers cache client configuration for a short window, so a corrected registration may not take effect instantly. A retry that fails within seconds of the update is not proof the fix is wrong.
Practical actions and thresholds
Work through these in order; stop at the first mismatch you find.
Log the exact authorization URL your client sends, then extract the raw redirect_uri value without decoding it. Open the provider's client configuration and copy the registered value character-for-character. Diff the pair for scheme, host, port, path, and trailing slash, in that order. If the client is a native app, confirm whether the provider permits loopback with variable ports; if not, pin the port. Register any additional environment variants, then wait for propagation before retesting. If the mismatch persists, test with the parameter omitted only to observe provider behavior, not as a permanent fix.
A useful threshold: if the two strings are identical after accounting for encoding and trailing slash, the problem is likely propagation or caching, not matching. If they differ in even one character, you have found the cause.
Red-flag warnings as bullets:
Never widen a redirect URI to a wildcard domain to "make it work"; that expands the attack surface. Never register a production callback on an insecure scheme. Never paste a redirect URI with embedded credentials or tokens into logs.
Limitations and what this error cannot tell you
This error confirms a redirect_uri mismatch but says nothing about whether your client secret, scopes, or user consent are valid. It also cannot distinguish between an unregistered URI and a registered URI that failed normalization; you infer that from the string diff. Providers implement matching with varying strictness, and the specification permits some latitude, so behavior is not fully portable across authorization servers. Without documentation from your specific provider, treat exact-match behavior as the safe default.
This article is general educational information about OAuth configuration, not a security audit or a guarantee of interoperability. For production identity systems handling sensitive data, consult a qualified security professional familiar with your provider's documented requirements.