A made-up app and a made-up provider list
This is a synthetic specification, not a customer case and not a test of any real provider. The addresses use the reserved .invalid suffix, which never resolves, so no real site is implied. The provider list holds three registered callbacks. The matrix lists the address the app might send in each environment and applies one rule: the sent string must equal a registered string exactly. The results column was produced by a short comparison of the two strings, character by character.
- The rule is stricter than it looks: a trailing slash or a missing www is a different string.
- Real providers have their own extra rules, for example Google forbids wildcards, and some treat localhost ports specially (see the sources).
If the matrix is wider than the box, scroll horizontally to read every column. Keyboard: focus the matrix and use Left/Right.
registered list (exact strings):
https://app.invalid/auth/callback
https://staging.app.invalid/auth/callback
http://localhost:3000/auth/callback
environment | address the app sent | result
production | https://app.invalid/auth/callback | match
production (www host) | https://www.app.invalid/auth/callback | host differs (www)
production (slash added) | https://app.invalid/auth/callback/ | trailing slash differs
production (proxy made it http) | http://app.invalid/auth/callback | scheme differs
staging | https://staging.app.invalid/auth/callback | match
preview deployment | https://pr-42.preview.app.invalid/auth/callback | host not registered
local, other port | http://localhost:3001/auth/callback | port differs
local | http://localhost:3000/auth/callback | matchReading the rows
The first row matches. The www row fails because the app built its address from a host that is not registered. The slash row fails by one character. The http row is the proxy case: the app believed the connection was plain HTTP and built an http address, which is a different string and, for most providers, not allowed for a public site. Staging and plain local match. The preview row fails because each preview deployment has a new host, and the provider will not match a pattern. The local row with another port fails under exact matching, although some providers exempt loopback ports; check yours.
- Fix the app's address-building for the www, slash and http rows.
- Register the exact missing address only if it is a stable address you control.
- For previews, sign in on one stable test address instead of registering each one.
What to send when you ask for help
The matrix is a format to copy: your registered list, the address in the browser when the error appears for each environment, and the provider's error wording. Remove tokens, codes and secrets before you send anything. With that, a repair can be scoped as the first row that fails and the smallest change that fixes it, rather than a guess.
- Never add a wildcard to make previews work; some providers forbid it and others widen where codes can go.
- The matching paid job is our fixed-scope sign-in callback repair (posted test price £195, untested, for one provider and up to three environments).
Limits of this example
It does not show a sign-in succeeding, does not test any provider and does not include the state or code-verifier checks that also decide whether a return is accepted. Those are covered in the state and verifier guide. Nothing here means any sign-in has been repaired for a client.
Sources and limits
- RFC 9700: OAuth 2.0 Security Best Current Practice (January 2025) Checked 2026-10-11.
- Redirect URIs are compared by exact string matching, with a carve-out for variable ports on localhost in native apps.
- Google: OAuth 2.0 for web server applications Checked 2026-10-11.
- Scheme, letter case and trailing slash must be identical; wildcards are not allowed; HTTPS is required except for localhost.
- GitHub: Authorizing OAuth apps Checked 2026-10-11.
- Without wildcard matching the redirect URL must exactly match the callback URL; loopback addresses are exempt from port matching.