CORS Configuration in Keycloak

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.

01

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.

Everyday Analogy

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.

The default

Same-Origin Policy

Browsers block cross-origin script access to responses unless explicitly told otherwise.

The mechanism

CORS Headers

Special response headers that tell the browser exactly which other origins are trusted.

In Keycloak

Web Origins Setting

A per-client list defining which application origins Keycloak will allow to call it directly.

The stakes

Break or Expose

Too strict, and legitimate applications fail. Too loose, and the wall exists in name only.

02

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.

03

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.

Everyday Analogy

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.

1

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.

2

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.

3

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.

4

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.

5

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.

6

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.

7

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.

“CORS is not Keycloak deciding whether to trust a request. It is Keycloak telling the browser whether the browser should trust the response.”
05

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.

06

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.

!
Common Misconception

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.

07

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.

Console
Browser developer tools report blocked CORS requests directly
Per-Client
Web Origins reviewed individually in each client’s settings
Server Logs
Keycloak’s own logs show the request even when the browser blocks the response

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.

08

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.

ADR-056 · CORS Quick Fix Under Deadline Anti-pattern
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.

09

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.

Best Practice

List exact origins, never wildcards

Every entry should be a specific, known, and currently valid application origin — nothing broader.

Best Practice

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.

Best Practice

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.

Best Practice

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.

Common Mistake

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.

Common Mistake

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.

i
Practical Tip

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.

10

In Practice

Real-World & Industry Examples

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.

SPAs
Front end and Keycloak almost always on different origins
Multi-Brand
Per-client origins keep separate products isolated
Mobile Web
Progressive web apps configured with their exact deployed origin

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.

11

Questions Answered

FAQ

Q1Why does my application work fine when tested with a tool like a REST client, but fail with a CORS error only in the browser?
Direct testing tools bypass the browser’s own Same-Origin Policy and CORS enforcement entirely, since that enforcement is a browser behavior, not a server requirement. Only real browser-based requests trigger preflight checks and CORS header validation, which is why the same request can succeed outside a browser and fail inside one.
Q2Is Web Origins the same setting as Valid Redirect URIs?
No, though they are related. Valid Redirect URIs control where Keycloak is allowed to send a full-page browser redirect after login. Web Origins controls which origins are allowed to make direct, background JavaScript requests to Keycloak’s endpoints. The “+” shortcut can derive one from the other, but they serve genuinely different purposes.
Q3What does the “+” value in Web Origins actually do?
It tells Keycloak to automatically treat every origin implied by that client’s configured Valid Redirect URIs as a trusted Web Origin as well, avoiding the need to maintain two separate, manually synchronized lists for the same set of trusted addresses.
Q4Is using a wildcard origin ever acceptable?
It significantly weakens the protection CORS is designed to provide, since it allows any website whatsoever to attempt cross-origin calls against that client. It should be avoided for any client handling real user sessions or sensitive data, and treated, at most, as a strictly temporary measure during isolated local development, never carried into production.
Q5Does correct CORS configuration alone make an application secure?
No. CORS only controls whether a browser allows a script to read a cross-origin response. It works alongside, not in place of, Keycloak’s actual authentication and authorization checks, both of which remain essential regardless of how carefully CORS itself is configured.
Q6Why do development and production often need different Web Origins entries?
Because an origin is defined by its exact protocol, domain, and port, a local development address such as one running on localhost with a specific port is a genuinely different origin from the deployed production domain, and each one that needs to call Keycloak directly must be explicitly listed for its corresponding client.
12

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.