CORS Configuration in Keycloak
A zero-prior-knowledge guide to why browsers block cross-origin requests by default, how Keycloak's Web Origins setting lets the right applications through anyway, and why getting this one setting wrong either breaks logins or quietly opens a door.
Imagine a bank that, by default, refuses to let any teller discuss your account with someone calling from a different phone number than the one on file — not because that caller is necessarily dishonest, but because the bank has no reliable way to know for certain who is really on the line. Web browsers apply a strikingly similar rule to the applications you use every day. By default, a script running on one website is blocked from making requests to a server on a different website, purely as a built-in safety net against a whole class of attacks. Keycloak, sitting at the center of countless logins across the web, constantly needs legitimate applications from different addresses to talk to it. This article explains, from first principles, what CORS Configuration in Keycloak actually is, why the browser’s default caution exists in the first place, and exactly how Keycloak’s Web Origins setting carefully punches the right, deliberate holes in that default wall.
Foundations
Core Concepts
Before CORS configuration in Keycloak makes any sense, we need to understand the browser rule it exists to work around, one idea at a time.
What is an “origin”?
An origin is the combination of a website’s protocol, domain name, and port number — for example, https://app.example.com is a distinct origin from https://admin.example.com, even though both belong to the same company, and distinct again from http://app.example.com using plain, unencrypted http instead of https. Two web addresses are only considered the “same origin” if all three of these pieces match exactly.
The Same-Origin Policy
Every modern web browser enforces a security rule called the Same-Origin Policy, which says that a script running on one origin cannot freely read the response of a request it makes to a different origin, unless that different origin explicitly says it is allowed to. This rule exists specifically to stop a malicious website from silently using a visitor’s already-logged-in session on a completely different site to steal their private data behind the scenes.
Picture an apartment building where every resident’s mail is delivered to a shared front desk. By default, the front desk clerk will only hand your mail to someone standing at your own apartment’s designated pickup window — not to a stranger who simply walks up and asks for it, even if they know your name. CORS is the equivalent of you giving the front desk a specific, written list of other pickup windows that are allowed to collect your mail on your behalf. Without being on that list, no other window gets anything, no matter how politely they ask.
What is CORS?
Cross-Origin Resource Sharing, or CORS, is the standardized mechanism browsers use to relax the Same-Origin Policy in a controlled way. A server can include special response headers that explicitly tell the browser, “requests coming from this specific other origin are allowed to read my response.” Without those headers present and correct, the browser will block the response from ever reaching the requesting script, even if the server itself processed the request successfully.
Why Keycloak specifically needs CORS
Keycloak is almost never hosted on the exact same origin as the applications that rely on it for login. A single-page application running at https://app.example.com commonly needs to call Keycloak’s own endpoints — hosted at something like https://auth.example.com — directly from JavaScript running in the user’s browser, to check login status, refresh tokens, or fetch user information. Without correct CORS configuration, the browser would silently block these calls, even though the application and Keycloak both belong to the exact same company and trust each other completely.
Same-Origin Policy
Browsers block cross-origin script access to responses unless explicitly told otherwise.
CORS Headers
Special response headers that tell the browser exactly which other origins are trusted.
Web Origins Setting
A per-client list defining which application origins Keycloak will allow to call it directly.
Break or Expose
Too strict, and legitimate applications fail. Too loose, and the wall exists in name only.
Structure
Architecture & Components
Keycloak’s CORS handling is not one single global switch — it is built from a small number of related pieces, each configured at a specific level.
Web Origins — set per client
The primary place CORS is configured in Keycloak is the Web Origins field on each individual client’s settings. Because different applications connecting to the same realm may legitimately be hosted at completely different addresses, this setting lives at the client level rather than the realm level, letting each application declare exactly which of its own origins are allowed to call Keycloak directly.
The special “+” wildcard shortcut
Keycloak provides a convenient shortcut: entering a single plus sign, +, as a Web Origin tells Keycloak to automatically trust every Valid Redirect URI already configured for that same client, deriving the allowed origins from them rather than requiring the administrator to list every origin separately in two different places.
How this relates to Valid Redirect URIs
Every Keycloak client already has a separate setting called Valid Redirect URIs, which controls where Keycloak is allowed to send a browser back to after a successful login. Web Origins is a conceptually related but distinct setting: Redirect URIs control full-page browser navigation after login, while Web Origins specifically controls which origins are allowed to make background, JavaScript-driven requests directly to Keycloak’s endpoints, such as checking session status or refreshing a token without a full page reload.
Where CORS headers actually get added
When a browser makes a cross-origin request to one of Keycloak’s endpoints, Keycloak’s server checks the requesting origin against that client’s configured Web Origins list, and if it matches, adds the appropriate CORS response headers confirming that origin is trusted. If the origin is not on the list, Keycloak either omits those headers or actively responds in a way the browser will refuse to let the calling script read, and the request effectively fails from the application’s point of view, even though Keycloak itself may have processed it correctly on the server side.
flowchart TD
A["Browser running app.example.com"] --> B["JavaScript calls Keycloak at auth.example.com"]
B --> C["Browser sends a preflight OPTIONS request"]
C --> D["Keycloak checks Web Origins list for this client"]
D -->|"Origin is listed"| E["Keycloak responds with matching CORS headers"]
D -->|"Origin is NOT listed"| F["Keycloak omits or denies CORS headers"]
E --> G["Browser allows the real request through"]
F --> H["Browser blocks the response from reaching the script"]
Fig 1 — How a client’s Web Origins list decides whether the browser allows a cross-origin call to Keycloak through.
Notice that Keycloak’s server-side processing and the browser’s own enforcement are two separate things. Keycloak can correctly process a request and still have the browser discard the response, purely because the CORS headers did not match what the browser required for that specific requesting origin.
Mechanics
Internal Working
Understanding exactly what happens on the wire during a cross-origin request explains why CORS problems feel so confusing when they first appear.
The preflight request
For many kinds of cross-origin requests, the browser does not immediately send the actual request the application intended. Instead, it first sends a lightweight, automatic preflight request, using the HTTP method OPTIONS, essentially asking the server in advance, “if I were to send a real request like this, from this specific origin, would that be allowed?” Only if the server’s preflight response confirms the origin, method, and headers are acceptable does the browser proceed to send the actual request.
What the CORS response headers actually say
When Keycloak recognizes a request’s origin as trusted, it responds with a set of specific headers. One header explicitly names the trusted origin, confirming to the browser that this particular origin is allowed to read the response. Other headers describe which HTTP methods and which custom request headers are permitted for cross-origin calls to this endpoint. The browser reads these headers itself — the requesting JavaScript code never sees them directly — and decides, purely based on them, whether to let the calling script access the actual response data.
A preflight request is like calling a restaurant ahead of time to ask, “if a large group of twelve shows up tonight, can you seat us?” before actually driving over. If the restaurant says yes, the group goes ahead and arrives. If the restaurant says no, the group saves the trip entirely and looks elsewhere. The browser performs this same courtesy check before sending certain real cross-origin requests, to avoid wasting effort on a request the server has already indicated it will not honor.
Why this happens invisibly to most developers
Well-built client libraries and frameworks that integrate with Keycloak typically handle preflight requests and CORS headers automatically, without requiring the application developer to manually construct them. This is precisely why CORS misconfiguration often surfaces as a confusing, generic-looking network error in the browser’s developer console, rather than a clear, Keycloak-specific error message — the browser itself is the one blocking the response, before it even reaches the application’s own code.
Same-origin requests skip all of this
It is worth remembering that CORS only applies to genuinely cross-origin requests. An application hosted on the exact same origin as Keycloak — matching protocol, domain, and port precisely — never triggers any of this preflight or header-checking behavior at all, since the Same-Origin Policy simply does not apply in that case.
Following a single background request from a single-page application shows exactly where CORS checking fits into the overall sequence.
User is already logged in
A single-page application, hosted at its own origin, has already completed a login through Keycloak and holds a valid session and token.
The application needs fresh user information
Instead of reloading the entire page, the application’s JavaScript code silently calls one of Keycloak’s account or token endpoints directly from the browser, in the background.
The browser recognizes a cross-origin call
Since the application’s origin differs from Keycloak’s own origin, the browser automatically sends a preflight OPTIONS request first, before attempting the real call.
Keycloak checks its Web Origins configuration
Keycloak looks up the client this request corresponds to and checks whether the requesting origin appears in that client’s configured Web Origins list.
A match is found
Keycloak responds to the preflight request with headers confirming the origin is trusted, and the browser proceeds to send the actual request.
The real request completes successfully
Keycloak processes the actual request and responds with the requested data, once again including the correct CORS headers so the browser allows the application’s JavaScript to read the response.
What happens instead on a mismatch
If the requesting origin had not been listed in Web Origins, the preflight response would have lacked the necessary confirmation, and the browser would have blocked the entire exchange before the real request was even sent.
Weighing the Choice
Advantages, Disadvantages & Trade-offs
Configuring Web Origins precisely, rather than broadly, is a genuine trade-off between administrative convenience and the strength of the browser-level protection CORS is meant to provide.
Advantages of Precise Configuration
- Ensures only genuinely trusted application origins can make direct, background calls to Keycloak’s endpoints from a user’s browser
- Preserves the full protective value of the browser’s Same-Origin Policy for every origin not explicitly listed
- Makes it straightforward to audit exactly which applications are permitted to interact with Keycloak directly, client by client
- Limits the impact if a single application origin is ever compromised, since other clients’ trust configurations remain unaffected
Disadvantages & Trade-offs
- Requires ongoing maintenance as new application origins — new environments, new subdomains — are added over time
- Misconfiguration produces confusing, generic-looking browser errors that can be time-consuming to diagnose without CORS knowledge
- Development, staging, and production environments often need separate entries, adding a small but real configuration burden
- The convenience of the wildcard “*” origin option directly undermines the very protection this configuration exists to provide
Why the extra maintenance is worth it
The temptation to configure an overly permissive wildcard origin, allowing every possible website to call Keycloak directly, trades away nearly all of the protective value CORS provides, in exchange for slightly less configuration effort. For any Keycloak deployment protecting real user accounts and real data, maintaining an accurate, specific list of trusted origins per client remains one of the simplest, lowest-cost security practices available.
Defense
Security
CORS configuration sits directly at the boundary between “trusted application” and “everything else on the internet,” which makes small mistakes here unusually consequential.
The danger of the wildcard origin
Configuring a Web Origin of a bare wildcard, allowing literally any website to make cross-origin calls to a client’s Keycloak endpoints, effectively removes the browser’s protection entirely for that client. A malicious website could then attempt to make requests against Keycloak on behalf of an unsuspecting visitor who happens to already be logged in, since the browser would have no reason to block responses from reaching that malicious site’s own script.
Some teams assume that because Keycloak still requires valid tokens and credentials for most sensitive operations, a loose CORS configuration is a minor convenience issue rather than a real security risk. In reality, CORS misconfiguration can expose session-related information and enable certain request patterns that a correctly scoped configuration would have blocked at the browser level before they ever reached Keycloak’s own authorization checks.
CORS is not authentication or authorization
It is important to understand that CORS headers only control whether a browser lets a script read a response — they are not, by themselves, an authentication or authorization mechanism. A correctly configured Web Origins list is a necessary layer of defense, but it works alongside, not instead of, Keycloak’s actual token validation and access control checks, which remain essential regardless of how CORS is configured.
Keeping Web Origins tightly scoped per client
Because Web Origins is configured per client rather than realm-wide, a mistake made while configuring one application’s origins does not automatically affect every other application sharing the same realm. This isolation is valuable, but only if administrators actually take advantage of it by scoping each client’s Web Origins list specifically to that one application’s real, known origins, rather than reusing an overly broad list across multiple unrelated clients out of convenience.
Auditing origins during environment changes
Whenever an application’s hosting domain changes — a new custom domain, a migration to a new environment, the retirement of an old staging URL — the corresponding client’s Web Origins list should be reviewed and updated at the same time, since stale, no-longer-used origins left in the list serve no purpose and unnecessarily widen the set of addresses trusted to call Keycloak directly.
Visibility
Monitoring, Logging & Metrics
Because CORS failures are enforced by the browser rather than reported clearly by the server, visibility into what is actually happening requires looking in a couple of specific places.
Reading the browser’s own developer console
When a CORS-related failure occurs, the browser’s developer console typically reports it directly, describing the request that was blocked and, often, the specific origin that failed the check. This is usually the very first and most direct place to look when an application reports mysterious, unexplained failures calling Keycloak, especially failures that only happen from the browser and not from simple direct testing tools that bypass browser enforcement entirely.
Distinguishing a CORS failure from an authentication failure
A genuinely useful diagnostic habit is checking whether Keycloak’s own server-side logs show the request as having been received and processed at all. If Keycloak’s logs show the request succeeding, but the calling application still reports failure, the problem is very likely happening at the browser’s CORS enforcement layer rather than inside Keycloak’s authentication or authorization logic, which points the investigation directly toward the client’s Web Origins configuration.
Periodic review of configured origins
Since Web Origins lists tend to accumulate entries over time as new environments and domains are added, periodically reviewing each client’s list against the application’s actual, currently deployed hosting addresses helps catch stale or unnecessary entries before they become a forgotten, unmonitored source of unnecessary trust.
Shape
Design Patterns & Anti-patterns
Teams that manage multiple applications and multiple environments behind a single Keycloak deployment tend to settle into a small number of recognizable, repeatable patterns.
Pattern: Explicit origins per environment
Mature deployments maintain a clear, explicit list of exact origins for each environment — development, staging, production — rather than relying on shortcuts, making it obvious at a glance exactly which addresses are trusted for each client at any given time.
Pattern: Using the “+” shortcut deliberately, not by default
Some teams use Keycloak’s “+” shortcut, which derives Web Origins automatically from Valid Redirect URIs, specifically because they already maintain a tight, accurate Redirect URI list and want to avoid keeping two separate lists in sync. This is a reasonable pattern only when the Redirect URI list itself is genuinely kept precise — the shortcut simply inherits whatever level of care already exists there.
Pattern: Separate clients per environment
Rather than cramming every environment’s origins into one shared client, some organizations register a distinct Keycloak client per environment, each with its own tightly scoped Web Origins list, making it easier to reason about exactly what each specific client trusts without cross-environment clutter.
Anti-pattern: Wildcard origins “just to make it work”
The most common and most damaging anti-pattern is reaching for a wildcard origin configuration the moment a CORS error appears during development, simply to unblock progress quickly, and then never revisiting that decision before the application reaches production.
Situation
A developer hits a confusing CORS error the night before a launch deadline, quickly sets the client’s Web Origins to a wildcard to make the error disappear, and ships the release to meet the deadline, intending to “fix it properly later.”
Consequence
The wildcard configuration quietly remains in production for months, meaning any website on the internet is technically permitted to attempt cross-origin calls against that client’s Keycloak endpoints, a fact that only surfaces during a later security review, well after the original context and urgency have been forgotten.
Better Approach
Diagnose the actual origin mismatch causing the error — often a simple protocol, port, or subdomain discrepancy — and add exactly that specific origin to the Web Origins list, treating a wildcard as never an acceptable temporary measure, even under deadline pressure.
Getting It Right
Best Practices & Common Mistakes
A short set of disciplined habits keeps CORS configuration from becoming either a source of constant login friction or a quietly forgotten security gap.
List exact origins, never wildcards
Every entry should be a specific, known, and currently valid application origin — nothing broader.
Keep Redirect URIs and Web Origins consistent
Whether using the “+” shortcut or maintaining both lists manually, make sure they never drift out of sync with each other.
Review origins during every environment change
Treat a domain migration or new environment rollout as a trigger to update the corresponding client’s Web Origins immediately.
Diagnose the real mismatch before changing settings
Check the browser console and Keycloak’s own logs to understand exactly which origin is being rejected, rather than guessing.
Copying one client’s Web Origins list to another
Reusing a list meant for a different application quietly grants trust the new client’s actual origins may never have earned.
Forgetting protocol and port matter
An origin using plain http is not the same as one using https, and a different port counts as a genuinely different origin entirely.
Whenever a CORS error appears, resist the urge to broaden the configuration immediately. Instead, note the exact origin the browser’s console reports as blocked, compare it character-for-character against the client’s current Web Origins list, and add only that precise, verified origin.
CORS configuration becomes visible the moment an organization’s applications and its identity platform live on different addresses — which describes the overwhelming majority of real deployments.
Single-page application front ends
Organizations building modern single-page applications almost always host their front end at one domain and their Keycloak instance at another, making precise Web Origins configuration an everyday, unavoidable part of getting login and session management working correctly at all.
Multi-brand and multi-product companies
Companies operating several distinct products or brands, each hosted on its own domain but sharing a single Keycloak realm for unified account management, rely heavily on per-client Web Origins configuration to ensure each product’s front end can talk to Keycloak directly, without accidentally granting that same trust to every other product sharing the realm.
Progressive web apps and embedded widgets
Applications that embed Keycloak-protected login widgets inside progressive web apps, or inside third-party sites through an embedded component, must carefully configure Web Origins to match the exact origin the embedded content is actually served from, which can be a genuinely tricky and easily overlooked detail in more complex embedding scenarios.
API-first platforms with a separate developer portal
Platforms exposing APIs to external developers, with a separate developer portal for managing credentials and documentation, often need to configure Web Origins carefully to allow the portal’s own domain to interact with Keycloak directly, while keeping the underlying API-serving domain’s trust configuration entirely separate and independently scoped.
Questions Answered
FAQ
Wrapping Up
Summary & Key Takeaways
CORS Configuration in Keycloak exists entirely because of a browser rule that predates Keycloak itself: scripts on one origin cannot freely read responses from another, unless that other origin explicitly says so. Keycloak’s Web Origins setting is the precise, per-client mechanism for saying so — deliberately, narrowly, and only for the applications that genuinely need it. Every part of this system, from the invisible preflight request to the specific headers Keycloak returns, exists to answer one narrow but critical question correctly: should the browser trust what just came back from Keycloak, on behalf of this specific calling script, from this specific address?
Key Takeaways
- CORS exists because of the browser’s Same-Origin Policy — a default protection that blocks cross-origin script access to responses unless explicitly permitted.
- Web Origins is configured per client, not per realm — letting each application declare exactly which of its own origins Keycloak should trust.
- Preflight requests happen automatically and invisibly — the browser checks with Keycloak before sending many real cross-origin requests.
- CORS enforcement happens in the browser, not on the server — Keycloak can process a request successfully while the browser still blocks the application from reading the result.
- Wildcard origins trade away almost all of the protection CORS provides — and should never be treated as a permanent or production-ready shortcut.
- CORS is not authentication or authorization — it works alongside Keycloak’s real security checks, never as a substitute for them.
- Precise, actively maintained origin lists are cheap insurance — reviewing them during every environment change prevents both broken logins and quietly forgotten security gaps.