Skip to content

blog: Keycloak "Invalid parameter: redirect_uri" — every cause and fix - #356

Draft
xgp wants to merge 2 commits into
mainfrom
content/2026-w1-redirect-uri
Draft

blog: Keycloak "Invalid parameter: redirect_uri" — every cause and fix#356
xgp wants to merge 2 commits into
mainfrom
content/2026-w1-redirect-uri

Conversation

@xgp

@xgp xgp commented Aug 31, 2026

Copy link
Copy Markdown
Member

Keycloak "Invalid parameter: redirect_uri" — Every Cause and Fix

Queue item A1, first of the error-message troubleshooting cluster. The pattern: someone pastes a literal error string into a search engine at 2am, in a moment of acute pain. These posts are short, hard to get wrong, and convert unusually well because the reader is actively broken.

Everything here was tested, not read off a spec

Written against a live Keycloak 26.7.3. Every row of every table was produced by probing the authorization endpoint against clients registered with different patterns. Findings that are genuinely non-obvious:

1. * is a substring wildcard, not a path wildcard.
/cb* matches /cbXYZ and /cb/deep — it crosses the / boundary. And /a/* matches bare /a.

2. Trailing slashes are significant in both directions.
http://host:3000 and http://host:3000/ are different registrations, and neither matches the other. If you don't know which form your library sends, register both.

3. Keycloak rejects any redirect_uri containing a ? — regardless of what you registered.

Registered Sent Result
http://localhost:3000/* .../cb?tenant=acme rejected
http://localhost:3000/cb* .../cb?tenant=acme rejected
http://localhost:3000/cb?* .../cb?tenant=acme rejected

We tried to register our way around it and could not. Apps that put their own parameters on the callback URL — a tenant hint, a "return to this page" pointer — have to move that data into state. The post shows how, and explains that state is the correct mechanism anyway since it also carries CSRF protection.

4. The server log names the rejected URI; the error page deliberately doesn't.

WARN [org.keycloak.events] type="LOGIN_ERROR", clientId="c-multi",
  error="invalid_redirect_uri", redirect_uri="http://localhost:3000/cb?tenant=acme"

The browser can't be shown this without turning the login endpoint into an open redirector. Grepping for invalid_redirect_uri resolves most cases in seconds, and almost nobody knows the line exists. This is the single most useful thing in the post.

Also included

  • A curl one-liner that tests a redirect URI without a browser — exactly how the tables were generated. Turns a click-and-reload loop into a one-second check.
  • kcadm.sh / Terraform snippets so URIs stop drifting between environments.
  • An honest section on wildcard security: why redirect URI validation exists, why broad wildcards are dangerous, and why an upgrade may have broken a redirect that used to work.

Verification

  • pnpm build passes
  • 1,332 words
  • Named author byline, keyword in title / description / first 150 words
  • Direct-answer block in the opening for snippet and AI extraction

Note

The closing CTA originally linked to /tutorials/, which ships in #355. Removed here so the link checker passes on this branch — worth adding once #355 merges.

Queue item A1, first of the error-message troubleshooting cluster. Someone pastes
an error string into a search engine at 2am; this is the page that should be
waiting for them.

Written against a live Keycloak 26.7.3. Every row of every matching table was
produced by probing the authorization endpoint, not read off a spec:

- Exact registrations reject a trailing slash, a different case, or any query
  string.
- The `*` wildcard is a substring wildcard, not a path wildcard: `/cb*` matches
  `/cbXYZ` and `/cb/deep`, and `/a/*` matches `/a` itself.
- Trailing slashes are significant in both directions: `http://host:3000` and
  `http://host:3000/` are different registrations and neither matches the other.
- Keycloak rejects any redirect_uri containing a `?` regardless of the registered
  pattern — we tried `/*`, `/cb*` and `/cb?*` and all three refuse. Apps sending
  their own callback query parameters need to move that data into `state`.

Also documents the server-side log line (error="invalid_redirect_uri" with the
exact rejected URI), which is the fastest path to a diagnosis and is not
mentioned in the error page the user sees.

Includes a curl one-liner that checks a redirect URI without a browser, which is
how the tables were generated.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 31, 2026

Copy link
Copy Markdown

Deploying phasetwo-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 6338a24
Status: ✅  Deploy successful!
Preview URL: https://3e671114.phasetwo-docs.pages.dev
Branch Preview URL: https://content-2026-w1-redirect-uri.phasetwo-docs.pages.dev

View logs

Moves the publication date forward to Wednesday 2 September.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant