Microsoft Entra ID: AADSTS50011, the redirect URI specified in the request does not match the redirect URIs configured for the application
Hi, it's BlueByte. A user clicks Sign in with Microsoft, gets the familiar login page, types the right password — and instead of landing back in the app, gets a Microsoft error page: AADSTS50011: The redirect URI 'https://app.contoso.com/signin-oidc' specified in the request does not match the redirect URIs configured for the application '11111111-2222-3333-4444-555555555555'. The account is fine and the credentials were accepted; authentication ran to completion and Entra ID refused only the final hop back to your app. We'll walk through the wordings this error comes in, the mismatches that actually cause it, how to read the failing request instead of guessing, how to add the URI from the command line, and how to keep the next hostname from doing the same thing.
Two wordings of AADSTS50011, and the two facts each one hands you
Microsoft's troubleshooting article for this code gives the current text:
Error AADSTS50011 - The redirect URI <Redirect URI> specified in the request does not
match the redirect URIs configured for the application <AppGUID>. Make sure the redirect
URI sent in the request matches one added to your application in the Azure portal.
Navigate to https://aka.ms/redirectUriMismatchError to learn more about how to fix this.Older libraries and older tenants surface the same failure with the previous vocabulary: AADSTS50011: The reply URL specified in the request does not match the reply URLs configured for the application. "Reply URL" was the portal's earlier name for the same field. The error-code reference lists 50011 under its internal name as well, InvalidReplyTo - The reply address is missing, misconfigured, or doesn't match reply addresses configured for the app.
Whichever wording you get, it hands you the two facts the rest of this article compares: the exact URI your application sent, and the application ID it was checked against. Copy both out of the page before you close it.
The mismatches that actually cause it: case, trailing slash, scheme, port
Entra compares the redirect_uri in the authorization request against the registered list as a string, under rules the redirect-URI documentation states plainly. These are the ones that bite in practice:
- Case matters. Redirect URIs are case-sensitive and must match the case of the URL path of your running application, so
.../abc/response-oidcis not.../ABC/response-oidc. - Trailing slashes are not symmetric. A URI registered with no path segment is returned with a trailing slash when the response mode is
queryorfragment—https://contoso.comcomes back ashttps://contoso.com/. A URI that does contain a path segment is not given one. - The scheme must be https, with an exception only for localhost. The docs mark
http://contoso.com/abc/response-oidcinvalid, whilehttp://localhost,http://localhost/abcandhttps://localhostare all valid. - The port is ignored only for localhost.
http://localhost:1234/MyAppandhttp://localhost:8080/MyAppare documented as equivalent. Anywhere else the port is part of the match, sohttps://app.contoso.com:8443/signin-oidcis a different URI fromhttps://app.contoso.com/signin-oidc. - Some characters are rejected outright:
! $ ' ( ) , ;are not supported, and neither are internationalized domain names. - Query parameters depend on the audience. They are allowed for registrations that sign in work or school accounts, and not allowed for any registration configured for personal Microsoft accounts.
The IPv6 loopback [::1] is not currently supported either, which catches dev machines where a tool binds ::1 instead of 127.0.0.1. None of this needs memorizing — it just means the comparison is stricter than you expect, so compare strings rather than eyeballing them.
The other half: the right URI on the wrong platform, or on the wrong object
A URI can be present in the registration and still not match, because a registration keeps three separate lists. In the Microsoft Graph application resource they are web (a server-rendered app), spa (a single-page app — JavaScript, Angular, React, Blazor WebAssembly, Vue.js) and publicClient (mobile and desktop). Microsoft's platform tables map each framework to the list it belongs in, and a React app whose URI was added under Web is a common way to get 50011 from a registration that visibly contains the URI.
The second trap is which object holds it. The docs are blunt: always add redirect URIs to the application object only, and never to a service principal, because those values can be removed when the service principal object syncs with the application object. A URI added on the enterprise-application side can work for weeks and then quietly vanish.
Compare the sent URI with the registered list before touching anything
Pull the URI your app actually sent out of the failed /authorize request (the browser's Network tab keeps it), then read every list on the registration:
python3 -c "import sys,urllib.parse as u; q=u.parse_qs(u.urlparse(sys.argv[1]).query); print(q['redirect_uri'][0])" \
'https://login.microsoftonline.com/contoso.onmicrosoft.com/oauth2/v2.0/authorize?client_id=11111111-2222-3333-4444-555555555555&response_type=code&redirect_uri=https%3A%2F%2Fapp.contoso.com%2Fsignin-oidc'
az ad app show --id 11111111-2222-3333-4444-555555555555 \
--query "{web: web.redirectUris, spa: spa.redirectUris, publicClient: publicClient.redirectUris, audience: signInAudience}"https://app.contoso.com/signin-oidc{
"audience": "AzureADMyOrg",
"publicClient": [],
"spa": [],
"web": [
"https://app.contoso.com/signin-oidc/"
]
}Then stop reading and let diff do it, because a trailing slash is exactly the kind of thing eyes skip:
diff <(printf '%s\n' 'https://app.contoso.com/signin-oidc') \
<(az ad app show --id 11111111-2222-3333-4444-555555555555 --query "web.redirectUris" -o tsv)1c1
< https://app.contoso.com/signin-oidc
---
> https://app.contoso.com/signin-oidc/An empty web list with a populated spa list (or the reverse) is the platform problem instead. An audience of AzureADandPersonalMicrosoftAccount is your warning that query parameters and wildcards are off the table for this registration.
Write the exact string back with az ad app update, or a Graph PATCH for a SPA
--web-redirect-uris replaces the whole list rather than appending to it, so read the current values first and write them back together with the new one:
az ad app show --id 11111111-2222-3333-4444-555555555555 --query "web.redirectUris" -o tsvhttps://contoso-stg.azurewebsites.net/signin-oidcThen pass every URI you want to keep, plus the new one, in a single command. Spell them out rather than piping the previous output into the next command — anything you leave off the line is deleted from the registration:
az ad app update --id 11111111-2222-3333-4444-555555555555 \
--web-redirect-uris https://contoso-stg.azurewebsites.net/signin-oidc \
https://app.contoso.com/signin-oidcThe CLI has no flag for the SPA list, so go at the application object through Microsoft Graph. Note that the Graph URL takes the object ID, not the application ID:
OBJ=$(az ad app show --id 11111111-2222-3333-4444-555555555555 --query id -o tsv)
az rest --method PATCH \
--url "https://graph.microsoft.com/v1.0/applications/$OBJ" \
--headers "Content-Type=application/json" \
--body '{"spa":{"redirectUris":["https://app.contoso.com/","https://app.contoso.com/auth"]}}'The change is not instant. Microsoft's resolution steps say to save and then wait three to five minutes before sending the login request again, and to clear the browser's password cache or use an InPrivate window if the login page does not reappear. If the URI in the error was never the one you wanted, fix the application code or its configuration instead of registering the wrong value.
A worked example: staging moves behind a new hostname
A staging ASP.NET Core deployment moves from https://contoso-stg.azurewebsites.net to https://staging.contoso.com, and sign-in starts failing with AADSTS50011 naming https://staging.contoso.com/signin-oidc. If you check it yourself, az ad app show lists only https://contoso-stg.azurewebsites.net/signin-oidc under web, with spa and publicClient empty and signInAudience set to AzureADMyOrg. That rules out the platform trap and the audience restrictions in one command: the URI is simply not registered. One az ad app update writes both URIs back into web.redirectUris, and after a four-minute wait a private window completes sign-in and the callback arrives at https://staging.contoso.com/signin-oidc?code=.... Confirm it rather than assuming — re-run az ad app show --query "web.redirectUris" -o tsv and check that both lines are there, then sign out and in once more in a normal window to prove the cached login page was not doing the work.
Keep it from coming back
Give each environment its own app registration. The docs recommend exactly this, so development redirect URIs are never exposed in a production app. Know the ceilings before you start adding hostnames: 256 redirect URIs for a registration whose signInAudience is AzureADMyOrg or AzureADMultipleOrgs, 100 for AzureADandPersonalMicrosoftAccount, and 256 characters per URI, none of which can be raised.
Wildcards look like the answer to a pile of subdomains and are not. https://*.contoso.com strips query strings and fragments from the matched redirect URI, is unsupported for registrations that sign in personal Microsoft accounts, and can only be set through the manifest editor. The documented alternative is one shared redirect URI plus a state parameter carrying a key into browser storage — with CSRF protection, and without putting URLs or other sensitive data in state directly. For local development, prefer 127.0.0.1 over localhost so a renamed interface or a firewall cannot break sign-in, and never register several localhost URIs that differ only in port: the login server picks one arbitrarily, so tell them apart by path instead.
How this differs from AADSTS900971 and AADSTS700016
AADSTS900971: No reply address provided. means the request carried no redirect URI at all, which is a client-library or configuration gap rather than a mismatch — there is nothing to compare. AADSTS700016 - UnauthorizedClient_DoesNotMatchRequest - The application wasn't found in the directory/tenant. fails earlier still: the client ID or the tenant in the authorize URL is wrong, or the app was never consented to in that tenant, so Entra never reaches the redirect-URI check. If you see 700016, fix the client ID and tenant before you look at a single URI.
Next time a sign-in dies on the last hop, walk these checks back in order: what URI did the request carry, which platform list should hold it, does diff agree the two strings are identical, and only then edit the registration.
Related questions
I added the URI in the portal and it still fails.
Three usual reasons. Microsoft's steps say to wait three to five minutes after saving, so retry rather than concluding it didn't work. The browser may be replaying a cached login page — use InPrivate. And check the platform: run az ad app show and confirm the URI landed in the list your app type uses (web, spa or publicClient), not one of the others.
Can I register a wildcard like https://*.contoso.com?
Only for registrations that sign in work or school accounts in one tenant, only through the manifest editor, and the docs strongly recommend against it: when a wildcard URI matches, query strings and fragments in the redirect URI are stripped. The documented alternative is a single shared redirect URI plus a state parameter, guarded against CSRF.
Does the port count as part of the match?
Yes, everywhere except localhost. For localhost the port component is ignored, so http://localhost:1234/MyApp and http://localhost:8080/MyApp are equivalent. For any other host, https://app.contoso.com:8443/signin-oidc and https://app.contoso.com/signin-oidc are two different URIs.
Can I use http for local development?
For localhost, yes — both http://localhost/myApp and https://localhost/myApp are acceptable because the redirect never leaves the device. The docs prefer the literal 127.0.0.1 over the name, but the portal's Redirect URIs text box refuses an http loopback address, so an http://127.0.0.1 URI has to be added through the replyUrlsWithType attribute in the application manifest.
How many redirect URIs can one app registration hold?
256 when signInAudience is AzureADMyOrg or AzureADMultipleOrgs, and 100 when it is AzureADandPersonalMicrosoftAccount, with a maximum of 256 characters per URI. The docs state the limit can't be raised for security reasons and point to the state parameter approach if you need more.
References
- Microsoft Learn — Error AADSTS50011 the redirect URI does not match the redirect URIs configured for the application (symptom text, cause, resolution steps and the three-to-five minute wait)
- Microsoft Learn — Redirect URI (reply URL) best practices and limitations (case sensitivity, trailing slash, supported schemes, localhost port handling, wildcard and query-parameter restrictions, maximum counts)
Haneul Seo
Infrastructure engineer · 10+ years running Linux fleets
More in this category
Gmail rejects your mail with 550-5.7.26: unauthenticated email is not accepted due to the domain's DMARC policy
Gmail enforced the DMARC policy your own domain publishes: the message failed both SPF and DKIM alignment against the header From: domain, so a p=quarantine or p=reject policy turned it into a hard bounce. The Authentication-Results header names the failing check, and three dig queries against your SPF, DMARC and DKIM records name the cause. A near-identical bounce about authenticating with SPF or DKIM is a different problem — Gmail's baseline sender requirements, not your policy.
Exchange Online: 535 5.7.139 Authentication unsuccessful, SmtpClientAuthentication is disabled
A scanner, script or app that sends through smtp.office365.com gets 535 5.7.139 because the SMTP AUTH protocol is switched off for the tenant, for that mailbox, or by an authentication policy or security defaults that block Basic authentication. Read the wording (Tenant, Mailbox, or 'did not meet the criteria'), confirm with Get-TransportConfig, Get-CASMailbox and Get-AuthenticationPolicy, then open SMTP AUTH on the one mailbox that needs it rather than tenant-wide. Treat Basic SMTP AUTH as a bridge: Microsoft disables it by default for existing tenants at the end of December 2026, so move the sender to OAuth, High Volume Email or a relay connector.
Zoom: "Unable to connect" error code 5003 — the desktop app can't reach Zoom while the browser can
Error 5003 is the Zoom desktop app failing to complete its connection to Zoom's servers while the web client on the same machine joins fine. The app needs more than a browser does: Zoom's firewall article lists TCP 443/8801/8802 and UDP 3478/3479/8801–8810 for meetings, a set of CA hosts for certificate validation, and it asks that zoom.us and *.zoom.us be exempted from proxy or SSL inspection. A port test, a curl issuer check, and the app's built-in Network Connectivity Tool (Ctrl+Alt+Shift+D / Cmd+Option+Shift+D) show which of those is cut; fix that layer, and reinstall only when a single machine fails while its neighbours join.
Slack: "Slack cannot connect" and the grey "Last updated…" banner behind a corporate proxy
Slack loads channels over ordinary HTTPS but delivers new messages over a persistent WebSocket on port 443 to the three wss-*.slack.com hosts Slack names (primary, backup, mobile). When a proxy or firewall passes the HTTP side and blocks the upgrade — most often because SSL decryption is on for the wss hosts, or the allowlist stops at slack.com — the app shows the grey "Last updated…" banner or "Slack cannot connect." while the browser seems fine. Two curl probes from the affected machine show which layer is blocked; exempt the three wss hosts from decryption, allow every domain on my.slack.com/help/urls, and confirm with my.slack.com/help/test.
Word: "The document is locked for editing by another user"
Word found a lock — an owner file — for the document and assumed someone else has it open, so it offers only a read-only copy. Usually no one does: a crash left the lock behind, or a hidden Word process is still holding the file. Confirm which, close every Word instance, delete the stale ~$ owner file, and the document opens for editing again.
OneDrive: sync stuck — paused or a red X on the icon
A red circle with a white cross, or a paused status, on the OneDrive icon means the sync loop has stopped — your files are safe locally and in the cloud, but they aren't moving between them. Read the activity list to see whether it's the client or one bad file, then restart OneDrive, and reset it if a restart isn't enough. Resetting rebuilds the sync links and doesn't delete files.