Fixing CORS Errors with Keycloak

Guilliano Molaire Guilliano Molaire 10 min read

Last updated: June 2026

The most common cause of Keycloak CORS errors is a missing or incorrect entry in the client’s Web Origins field. Keycloak derives the Access-Control-Allow-Origin headers it sends on its own OIDC endpoints — token, userinfo, logout — directly from that field, so you must add your app’s exact scheme, host, and port there. If the error is appearing on your own API or resource server, that is a separate issue: Keycloak has no control over your API’s CORS headers, and you must configure them in your own server or gateway.

CORS errors during Keycloak integration are disorienting precisely because there are two completely different places the problem can live, and the browser error message looks identical either way. This guide untangles them, explains how Keycloak’s CORS implementation actually works, and gives you a step-by-step path to a fix.

What CORS Is and Why It Applies to Keycloak

CORS — Cross-Origin Resource Sharing — is a browser security mechanism that restricts JavaScript running on one origin (scheme + hostname + port) from making HTTP requests to a different origin unless the destination server explicitly permits it. The permission is granted via response headers: the server includes Access-Control-Allow-Origin: https://myapp.com (or *), and the browser allows the request to complete.

When a browser-based application uses Keycloak for authentication, several cross-origin requests happen automatically:

  • Token endpoint (/realms/{realm}/protocol/openid-connect/token) — the authorization code is exchanged here for access and refresh tokens.
  • UserInfo endpoint (/realms/{realm}/protocol/openid-connect/userinfo) — called to retrieve profile claims.
  • JWKS endpoint (/realms/{realm}/protocol/openid-connect/certs) — fetched by token validation libraries to get Keycloak’s public signing keys.
  • Logout endpoint (/realms/{realm}/protocol/openid-connect/logout) — called during RP-initiated logout flows.

All of these live under your Keycloak domain, not your app’s domain. Without CORS configuration, the browser blocks every one of them.

The Critical Distinction: Keycloak’s Endpoints vs. Your API

This is where most developers lose time. A browser application typically makes two kinds of cross-origin calls during an authenticated session:

  1. Calls to Keycloak to obtain and refresh tokens.
  2. Calls to your own resource server or API to fetch application data, using the access token as a Bearer credential.

Keycloak controls CORS only for category 1. It has no influence over category 2. If your React app gets a CORS error calling https://api.myapp.com/data with an Authorization: Bearer ... header, adding anything to Keycloak’s Web Origins field will not help. You need to configure CORS on your API server directly — for example, in your Express middleware, Spring Boot @CrossOrigin annotation, or API gateway settings.

The browser error message for both cases looks the same:

Access to fetch at 'https://auth.example.com/realms/myrealm/protocol/openid-connect/token'
from origin 'https://myapp.com' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.

Look at the URL in the error. If the blocked URL is under your Keycloak domain, fix it in Keycloak. If it is under your API domain, fix it in your API.

How Keycloak Builds Allowed Origins

Keycloak computes the allowed origins for its OIDC endpoints based on two fields in the client configuration:

Web Origins

Navigate to Clients in the Keycloak Admin Console, select your client, and open the Settings tab. The Web Origins field is what controls CORS. Keycloak reads this field and, when a preflight or credentialed request arrives, checks whether the request’s Origin header matches any entry. If it matches, Keycloak adds the corresponding Access-Control-Allow-Origin header to the response.

You can use three kinds of values:

Value Behavior
https://myapp.com Allows exactly that origin
+ Tells Keycloak to derive allowed origins from Valid Redirect URIs
* Allows any origin — see caveats below

The + shorthand is convenient for keeping redirect URIs and CORS origins in sync, but it means any domain in Valid Redirect URIs is also a CORS-allowed origin. Be deliberate about what you put in redirect URIs in production.

Valid Redirect URIs and Their Relationship to Web Origins

Keycloak does not automatically copy Valid Redirect URIs into the CORS allowlist unless you use +. The two fields serve different purposes: redirect URIs govern where Keycloak will redirect after login and logout; Web Origins govern which browser origins Keycloak will respond to with CORS headers. You must populate both explicitly unless you use +.

For more detail on how these fields work together, see the guide to configuring CORS with your Keycloak OIDC client.

Wildcard Caveats

Setting Web Origins to * makes Keycloak respond with Access-Control-Allow-Origin: *. The browser then refuses to include credentials (cookies, Authorization headers) in the response for any request that uses credentials: 'include' or withCredentials: true. This is a browser rule, not a Keycloak limitation. In practice, wildcard CORS breaks authenticated flows that rely on cookies and is never appropriate in production. Use explicit origins.

Preflight Requests: What They Are and Why They Fail

For requests that use methods other than GET/HEAD/POST, or that include custom headers like Authorization, the browser sends an OPTIONS request to the same URL before the actual request. This is called a preflight. The server must respond to the OPTIONS request with the appropriate CORS headers — Access-Control-Allow-Origin, Access-Control-Allow-Methods, and Access-Control-Allow-Headers — or the browser cancels the real request before it is ever sent.

Keycloak handles preflight automatically for its own endpoints once Web Origins is configured. If you see CORS errors on your Keycloak endpoints and you have already set Web Origins correctly, check whether a reverse proxy or CDN in front of Keycloak is stripping the CORS response headers or intercepting OPTIONS requests and returning a 404.

A valid preflight response from Keycloak looks like this:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: POST, PUT, DELETE, GET, HEAD, OPTIONS
Access-Control-Allow-Headers: authorization, content-type, x-requested-with
Access-Control-Max-Age: 3600
Vary: Origin

If the Access-Control-Allow-Origin header is absent from that response, the origin is not in Web Origins, or something upstream is stripping the header.

Step-by-Step Fix for a SPA Hitting Keycloak Token Endpoints

The following steps assume you have a single-page application — React, Vue, Angular, or similar — authenticating against Keycloak 26.x using the Authorization Code flow with PKCE. The approach is the same for any SPA; for a detailed React-specific walkthrough, see the guide on secure React API access using Keycloak OIDC PKCE.

Step 1: Identify the blocked origin.

Open your browser’s developer tools, go to the Network tab, and reproduce the error. Note the exact origin your app is running on. It will be something like https://myapp.com or http://localhost:3000. The scheme, hostname, and port all matter — http://localhost:3000 and http://localhost:3001 are different origins.

Step 2: Open Keycloak Admin Console.

Navigate to https://<your-keycloak-domain>/admin and log in. Go to the realm your application uses.

Step 3: Open the client settings.

Select Clients from the left sidebar, then click the client ID your application uses. If you are unsure, it is the value of the client_id parameter in your OIDC configuration.

Step 4: Add the origin to Web Origins.

On the Settings tab, scroll to the Web Origins field. Add the exact origin your SPA runs on, including the scheme and port if it is not 443 (HTTPS) or 80 (HTTP):

https://myapp.com

For local development:

http://localhost:3000

Do not include a path. Do not include a trailing slash. https://myapp.com/ is a different value from https://myapp.com in some Keycloak versions and will not match.

Step 5: Save and test.

Click Save. Return to your application, hard-refresh the browser (Ctrl+Shift+R / Cmd+Shift+R to bypass cache), and reproduce the login flow. Check the Network tab to confirm the token endpoint response now includes Access-Control-Allow-Origin.

Step 6: Verify in staging and production separately.

Do not carry http://localhost:3000 into your production client configuration. Maintain separate Keycloak clients for development, staging, and production, each with only the origins appropriate to that environment.

Fixing CORS on Your Own API

If the CORS error is on your resource server — the API your SPA calls with a Bearer token — you need to configure CORS headers there. Keycloak is not involved.

For an Express.js API:

const cors = require('cors');

app.use(cors({
  origin: 'https://myapp.com',
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowedHeaders: ['Authorization', 'Content-Type'],
  credentials: true,
}));

For a Spring Boot resource server:

@Configuration
public class CorsConfig implements WebMvcConfigurer {
  @Override
  public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping("/api/**")
      .allowedOrigins("https://myapp.com")
      .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
      .allowedHeaders("Authorization", "Content-Type")
      .allowCredentials(true);
  }
}

If you are using an API gateway such as AWS API Gateway, Kong, or nginx, you configure CORS at the gateway layer and your backend code does not need to emit CORS headers at all. Whichever layer handles CORS, make sure it handles OPTIONS preflight requests and does not forward them to the backend without a response.

For Next.js applications that integrate Keycloak in both client and server components, see the guide on Keycloak Next.js App Router authentication which covers how to handle CORS in Next.js API routes.

Common Gotchas

The following table covers the most frequent mistakes that cause CORS failures even after you believe the configuration is correct.

Symptom Likely Cause Fix
CORS error in browser, correct origin in Web Origins Trailing slash in Web Origins entry Remove the trailing slash: https://myapp.com not https://myapp.com/
Works on HTTP but not HTTPS Origin entered as http:// in production Update Web Origins to match the actual scheme: https://
Works on port 443 but fails on port 8443 Port not included in Web Origins Add origin with explicit port: https://myapp.com:8443
CORS error after switching from HTTP to HTTPS Old HTTP origin still in Web Origins but HTTPS origin missing Add the HTTPS origin and remove the HTTP one
Wildcard set, still getting CORS errors Using credentials: 'include' with * Switch to an explicit origin — browsers block credentialed wildcard CORS
Preflight returns 404 Reverse proxy stripping OPTIONS or Keycloak not reachable at that path Check proxy config; ensure OPTIONS is passed through
CORS error on your /api/* path Misconfigured resource server — Keycloak is not involved Configure CORS in your API or gateway
CORS error only in Safari Safari enforces CORS more strictly for some redirect flows Ensure redirect URIs and origins match exactly, no wildcards

If you are also encountering 403 Forbidden responses, those are an authorization issue, not a CORS issue — they require separate investigation. See the guide on Keycloak 403 Forbidden troubleshooting for that path.

Similarly, if your redirect after login is going to the wrong URL, that is a redirect URI configuration problem rather than a CORS problem. The guide on Keycloak redirect URI mismatch troubleshooting covers that in detail.

Keycloak 26.x Specifics

Keycloak 26.x (Quarkus-based) handles CORS headers natively on all OIDC endpoints without requiring any additional server configuration. The CORS behavior is entirely driven by the client’s Web Origins field in the admin console. There is no separate CORS filter to enable and no Quarkus-level configuration needed for standard OIDC CORS support.

One change from older Keycloak versions: in Keycloak 26.x, KC_PROXY is deprecated. If you run Keycloak behind a reverse proxy and you are setting KC_PROXY=edge, switch to KC_PROXY_HEADERS=xforwarded (or forwarded for Forwarded headers). Incorrect proxy configuration can cause Keycloak to compute the wrong base URL for its endpoints and issue incorrect redirect URIs, which can appear as CORS mismatches even when Web Origins is configured correctly.

You can verify what Keycloak reports as its own base URL by checking the OpenID Configuration endpoint:

https://<your-keycloak-domain>/realms/<realm>/.well-known/openid-configuration

The token_endpoint, userinfo_endpoint, and jwks_uri values in that response are what Keycloak will use. If they show an unexpected hostname or port, the proxy configuration is the problem.

Frequently Asked Questions

How do I enable CORS in Keycloak?

CORS support is enabled per client, not at the server level. In the Keycloak Admin Console, open the client your application uses, go to the Settings tab, and add your application’s origin to the Web Origins field. Keycloak automatically includes the correct Access-Control-Allow-Origin response header on its OIDC endpoints — token, userinfo, logout — for any origin you list there. No additional server-level CORS configuration is needed.

What is the Web Origins field in Keycloak?

Web Origins is a field in a Keycloak client’s settings that controls which browser origins are permitted to make cross-origin requests to Keycloak’s OIDC endpoints for that client. It accepts exact origins (https://myapp.com), the special value + (which derives allowed origins from the client’s Valid Redirect URIs), or * (which allows any origin but prevents credentialed requests). Web Origins has no effect on CORS for external APIs — only for Keycloak’s own endpoints.

Why does my Keycloak token request get blocked by CORS?

The token request is blocked because the client’s Web Origins field does not contain the exact origin of your application. The origin must match on scheme (http vs https), hostname, and port. Check for typos, trailing slashes, and port mismatches. Also verify that no reverse proxy or CDN between your browser and Keycloak is stripping the Access-Control-Allow-Origin response header before it reaches the browser.

Does Keycloak control CORS on my API or resource server?

No. Keycloak controls CORS only on its own endpoints. If your browser application calls your own API with a Keycloak-issued Bearer token and gets a CORS error, you must configure CORS in your API server, framework middleware, or API gateway. Adding origins to Keycloak’s Web Origins will not affect your API’s CORS behavior.

Can I use a wildcard in Keycloak Web Origins for local development?

You can set Web Origins to * during development, but be aware that browsers will not allow credentialed requests (cookies, Authorization headers sent by withCredentials) to a server that responds with Access-Control-Allow-Origin: *. Most Keycloak token flows use Authorization headers, so you may still see failures. It is usually simpler and more accurate to add http://localhost:3000 (or whichever port you use) as an explicit origin. Never use * in production.

Conclusion

Keycloak CORS errors almost always trace back to one of two root causes: the Web Origins field on the client is missing your app’s exact origin, or the error is not on Keycloak’s endpoints at all but on your own API. Determining which one applies — by looking at the blocked URL in the browser error — tells you immediately where to focus. Fix Keycloak client configuration for the first case; configure your API server or gateway for the second.

The most important things to remember:

  • Web Origins must contain the exact scheme, hostname, and port of your application’s origin.
  • No trailing slashes. HTTP and HTTPS are different origins.
  • The + shorthand is convenient but ties CORS to your redirect URI list — keep that list tight.
  • Wildcard CORS (*) breaks credentialed requests and is not appropriate for production.
  • Keycloak has no influence over your API’s CORS headers.

Managing CORS configuration across multiple environments — development, staging, production — is one of the operational details that adds up quickly. Skycloak’s managed Keycloak hosting keeps each environment’s client configuration isolated and version-controlled, so CORS origins don’t leak between environments. See plans and pricing.

Guilliano Molaire
Written by Guilliano Molaire Founder

Guilliano is the founder of Skycloak and a cloud infrastructure specialist with deep expertise in product development and scaling SaaS products. He discovered Keycloak while consulting on enterprise IAM and built Skycloak to make managed Keycloak accessible to teams of every size.

Ready to simplify your authentication?

Deploy production-ready Keycloak in minutes. Unlimited users, flat pricing, no SSO tax.

© 2026 Skycloak. All Rights Reserved. Design by Yasser Soliman