Fixing Keycloak’s 431 ‘Request Header Fields Too Large’

Guilliano Molaire Guilliano Molaire 11 min read

Last updated: July 2026

HTTP 431 “Request Header Fields Too Large” surfaces in Keycloak environments when the Authorization header — carrying a JWT access token or ID token — exceeds the size limit enforced by Keycloak’s Quarkus HTTP layer, your reverse proxy, or your downstream application server. The root cause is almost always a bloated token: too many realm roles, client roles, group memberships, or heavy protocol mapper payloads crammed into a single JWT. You fix it from both ends: raise the header-size limit on whichever component is rejecting the request, and shrink the token itself by pruning the claims and roles it carries.

What HTTP 431 means and who emits it

HTTP 431 is a standard status code defined in RFC 6585 that tells the client the server refuses to process the request because one or more request headers are too large. In a Keycloak deployment, the 431 can come from three different places, and diagnosing which one is emitting it determines your fix.

Keycloak’s own Quarkus HTTP layer — Since Keycloak 17, the server runs on Quarkus with Vert.x as the underlying HTTP engine. Vert.x imposes a default maximum of 8 KB per individual header and 8 KB for the combined header list. When a client sends a large Authorization: Bearer <token> header, Vert.x rejects it with a 431 before Keycloak’s application logic even runs.

Your reverse proxy — Nginx, Traefik, HAProxy, and AWS ALB all have their own header-buffer limits. Nginx’s default large_client_header_buffers allows 4 buffers of 8 KB each. If your token exceeds that, Nginx returns a 431 (or sometimes a 400) before the request reaches Keycloak.

Your application server — If the token makes it past Keycloak and the proxy but your API backend uses Spring Boot, Node/Express, or another framework, that server also has header limits. Spring Boot’s embedded Tomcat defaults to 8 KB for individual headers.

How to tell which layer is rejecting the request

Check the response body and headers carefully:

  • A Keycloak-originated 431 typically has no body or a minimal Quarkus/Vert.x error body, and the response comes from the Keycloak port directly.
  • A proxy-originated 431 usually has the proxy’s error page (Nginx shows its own HTML) and may appear as a 400 Bad Request in some proxy versions rather than a true 431.
  • An application-server 431 appears after successful Keycloak authentication, only when the token is forwarded to the backend.

Use curl -v with a known large token to pinpoint the layer:

# Test Keycloak directly (bypassing proxy)
curl -v -H "Authorization: Bearer $LARGE_TOKEN" 
  https://keycloak.example.com:8443/realms/myrealm/protocol/openid-connect/userinfo

# Test through your proxy
curl -v -H "Authorization: Bearer $LARGE_TOKEN" 
  https://app.example.com/api/protected-resource

If the first command fails with 431 and the second does not, Keycloak is the emitter. If the first succeeds but the second fails, check your proxy or app server.

Why Keycloak tokens grow so large

Understanding the causes of token bloat is essential before choosing which fix to apply. For a deeper look at what goes into a JWT, see JWT Token Lifecycle Management: Expiration, Refresh, and Revocation Strategies.

Full Scope Allowed is enabled on the client — This is the most common culprit. When “Full Scope Allowed” is checked on a Keycloak client, every realm role and every client role the user holds gets included in the token. For users with many roles — common in RBAC-heavy enterprise applications — this can add several kilobytes to the token payload alone.

Large realm role sets — In organizations that model fine-grained permissions as Keycloak realm roles rather than application-level permissions, a single power user may hold dozens or hundreds of roles. Each role name is a string in the realm_access.roles claim, and the sizes add up quickly.

Group memberships — If you use a group mapper to embed group paths in the token, deep group hierarchies or many group memberships translate directly into a larger token. A user in ten nested groups can easily add 1-2 KB to the token.

Heavy custom protocol mappers — Mappers that pull large attributes from user storage (LDAP attributes, custom user attributes) and inject them into the token body increase token size proportionally. A mapper pulling a base64-encoded certificate or a large JSON blob from a user attribute is a frequent offender.

Multiple audience claims — Each audience entry added via the Audience mapper contributes to token size, though this is usually a minor contributor compared to roles and groups.

You can inspect your current token size using the JWT Token Analyzer to see exactly which claims are consuming the most space before making changes.

Symptom-cause-fix reference table

Symptom Likely cause Fix
431 on all requests for a specific user User has many roles or group memberships Shrink token: disable Full Scope Allowed, prune mappers
431 on all requests for all users Quarkus or proxy limit too low for your token baseline Raise Quarkus max-header-size and max-header-list-size
431 only on the proxy, not on Keycloak directly Proxy header buffer too small Raise proxy buffer config
431 only on the application server App server header limit Raise app server limit; also shrink token
431 appears after adding a new mapper New mapper added large claims Remove or scope the mapper
431 appears after adding users to a new group Group mapper is embedding paths in token Scope group mapper or move groups to userinfo

Fix path 1: Raise header size limits

Raising limits is a valid short-term fix and sometimes a necessary complement to token shrinking. It does not address the underlying bloat, but it stops the immediate breakage while you implement the structural fixes.

Keycloak / Quarkus configuration

Keycloak 17+ (Quarkus) exposes two separate properties that both need to be set. This is the most common mistake: administrators set max-header-size but omit max-header-list-size, and the 431 persists.

  • quarkus.http.limits.max-header-size — Maximum size of a single HTTP header value. Default: 8192 (8 KB).
  • quarkus.http.limits.max-header-list-size — Maximum combined size of all request headers. Default: 8192 (8 KB).

Set both in conf/quarkus.properties (recommended for production):

# conf/quarkus.properties
quarkus.http.limits.max-header-size=48K
quarkus.http.limits.max-header-list-size=48K

Or pass them as environment variables, which is useful for Docker and Kubernetes:

# Docker / docker-compose
KC_QUARKUS_HTTP_LIMITS_MAX_HEADER_SIZE=49152
KC_QUARKUS_HTTP_LIMITS_MAX_HEADER_LIST_SIZE=49152
# Kubernetes Deployment env block
env:
  - name: QUARKUS_HTTP_LIMITS_MAX_HEADER_SIZE
    value: "49152"
  - name: QUARKUS_HTTP_LIMITS_MAX_HEADER_LIST_SIZE
    value: "49152"

Or pass them directly to the kc.sh start command:

bin/kc.sh start 
  --quarkus-http-limits-max-header-size=49152 
  --quarkus-http-limits-max-header-list-size=49152

A value of 48 KB (49152 bytes) is a reasonable ceiling for most deployments. Going beyond 64 KB signals that token shrinking is overdue.

Note: On Keycloak 24+, the KC_PROXY environment variable is deprecated. If you are still using it, review the Keycloak reverse proxy guide for the current KC_PROXY_HEADERS approach.

Nginx reverse proxy

Nginx uses large_client_header_buffers to control the number and size of buffers allocated for large request headers. The syntax is large_client_header_buffers <count> <size>:

# nginx.conf or your server block
http {
    large_client_header_buffers 4 32k;
    ...
}

This allocates 4 buffers of 32 KB each, giving a total capacity of 128 KB for request headers. Place this in the http block so it applies globally, or inside a specific server block if you only need it for the Keycloak upstream.

Also check client_header_buffer_size, which controls the initial buffer allocated before Nginx falls back to large_client_header_buffers:

client_header_buffer_size 4k;
large_client_header_buffers 4 32k;

Traefik reverse proxy

Traefik does not expose a direct header-size limit equivalent, but it inherits Go’s net/http defaults. If you are hitting limits in Traefik, the practical fix is to shrink the token. However, you can increase the response header map size at the transport level by configuring the maxResponseHeaderBytes on the ServersTransport:

# traefik.yml or dynamic config
http:
  serversTransports:
    keycloak-transport:
      maxResponseHeaderBytes: 65536

Application server limits

For Spring Boot (embedded Tomcat), set server.max-http-request-header-size in application.properties:

server.max-http-request-header-size=48KB

For Node.js/Express with the built-in HTTP server:

const http = require('http');
const app = require('./app');

const server = http.createServer({ maxHeaderSize: 49152 }, app);
server.listen(3000);

Fix path 2: Shrink the token

Raising limits treats the symptom. Shrinking the token treats the cause and is always the right long-term fix. Smaller tokens are faster to parse, cheaper to transmit, and harder to attack. For the full conceptual background on Keycloak roles and scopes, see Fine-Grained Authorization in Keycloak Explained.

Step 1: Turn off Full Scope Allowed

This is the highest-impact change for most deployments.

Navigate to: Clients > your-client > Client scopes tab, then go to Clients > your-client > Settings and find the Full Scope Allowed toggle. Disable it.

With Full Scope Allowed off, Keycloak only includes roles and scopes that are explicitly assigned to the client via its client scopes. This alone often reduces token size by 60-80% for users with many realm roles.

After disabling Full Scope Allowed, you need to explicitly assign the scopes the client needs:

  1. Go to Clients > your-client > Client scopes.
  2. Click Add client scope.
  3. Add only the scopes this client legitimately needs.

Step 2: Move roles to a dedicated client scope

Rather than including all realm roles in every token, create a dedicated client scope that maps only the roles relevant to a given service:

  1. Go to Client scopes > Create client scope.
  2. Name it (e.g., api-roles), set the type to Optional.
  3. Under the scope’s Mappers tab, add a User Realm Role mapper. Set Token Claim Name to roles and configure Multivalued to true.
  4. Set Add to userinfo to ON and Add to access token to ON only if the downstream service truly needs roles in the token.
  5. Assign the scope to only the clients that need it.

Step 3: Move group memberships to the userinfo endpoint

If your application only needs group information for display purposes or non-critical authorization, move the group mapper from the token to the userinfo endpoint:

  1. Go to Clients > your-client > Client scopes > (scope name) > Mappers.
  2. Find your group mapper (or create one: Add mapper > By configuration > Group Membership).
  3. Set Add to access token: OFF.
  4. Set Add to ID token: OFF.
  5. Set Add to userinfo: ON.

Your application then calls the userinfo endpoint when it needs group data, rather than receiving it in every token. This is especially effective when groups are large or the access token is used frequently by stateless API clients.

For a practical walkthrough of using Keycloak for API token validation, see Keycloak Token Validation for APIs.

Step 4: Audit and prune protocol mappers

Go to Clients > your-client > Client scopes > (assigned scopes) > Mappers and review every mapper:

  • Remove any mapper that adds claims the application does not actually read.
  • For user attribute mappers, check whether the attribute value is large (base64-encoded data, JSON blobs). If so, move those to the userinfo endpoint.
  • Check the Realm default client scopes (under Client scopes > Default client scopes) — these apply to all clients and may include mappers that were added globally without considering token size.

Step 5: Use audience-scoped tokens

If you use a single access token to call multiple downstream APIs, consider issuing separate, audience-scoped tokens for each service. Each token carries only the claims and roles needed for that specific audience:

  1. Create a client scope named after the target API (e.g., api-inventory).
  2. Add an Audience mapper pointing to the api-inventory client.
  3. Add only the role mappers needed for that API.
  4. In your application, request this scope explicitly: scope=openid api-inventory.

Audience-scoped tokens are smaller, more auditable, and limit the blast radius of a compromised token. For a deeper dive, see Keycloak Client Scopes vs Roles Explained and JWT Token Lifecycle Management.

Measuring your token size before and after

After making changes, generate a new token and measure it:

# Get a token
TOKEN=$(curl -s -X POST 
  "https://keycloak.example.com/realms/myrealm/protocol/openid-connect/token" 
  -H "Content-Type: application/x-www-form-urlencoded" 
  -d "client_id=myclient&client_secret=mysecret&grant_type=client_credentials" 
  | jq -r '.access_token')

# Measure the token length in bytes
echo -n "$TOKEN" | wc -c

# Decode and inspect the payload
echo $TOKEN | cut -d'.' -f2 | base64 -d 2>/dev/null | jq .

A well-tuned access token for most applications should be under 2 KB. Tokens above 8 KB indicate significant bloat that warrants a full mapper audit.

You can also paste the token into the JWT Token Analyzer to get a visual breakdown of which claims consume the most space.

Frequently asked questions

What causes HTTP 431 in Keycloak?

HTTP 431 in Keycloak is caused by an HTTP request carrying headers — most commonly the Authorization: Bearer <token> header — that exceed the maximum size allowed by the receiving component. The receiving component may be Keycloak’s own Quarkus/Vert.x HTTP layer (which defaults to 8 KB per header and 8 KB for the combined header list), a reverse proxy such as Nginx or Traefik, or a downstream application server. The token grows large when Full Scope Allowed is enabled on the Keycloak client, when the user holds many realm or client roles, when group membership mappers embed long group paths in the token, or when custom protocol mappers add large user attributes to the token payload.

How do I increase Keycloak’s max header size?

Set both quarkus.http.limits.max-header-size and quarkus.http.limits.max-header-list-size in conf/quarkus.properties, or pass them as the environment variables QUARKUS_HTTP_LIMITS_MAX_HEADER_SIZE and QUARKUS_HTTP_LIMITS_MAX_HEADER_LIST_SIZE. Setting only one of the two is a common mistake that leaves the 431 in place because Vert.x enforces both limits independently. A value of 48576 (48 KB) handles most real-world token sizes while staying conservative.

How do I make Keycloak tokens smaller?

The most impactful steps are: disable Full Scope Allowed on the client so only explicitly assigned roles are included; move group and role mappers to the userinfo endpoint rather than the token; audit all protocol mappers and remove any that add claims the application does not use; and create dedicated client scopes so each service’s token carries only the claims that service needs. For detailed instructions on scoping roles and clients, see the Fine-Grained Authorization in Keycloak Explained guide.

Does the 431 also affect refresh token requests?

Yes, if refresh tokens are large enough. Refresh token requests send the refresh token in the request body rather than in a header, so they are not subject to header-size limits. However, if your application caches large access tokens in a cookie and sends that cookie on every request, cookie headers can also trigger a 431. The fix is the same: shrink the token, and avoid storing full JWTs in cookies — use short-lived session IDs and server-side session storage instead.

Can this be fixed without restarting Keycloak?

Quarkus configuration changes (the header-size properties) require a server restart because they are server-level settings. However, mapper and scope changes in the Keycloak admin console take effect immediately for new token issuance without restarting — only tokens already issued retain their old size until they expire. This means you can deploy the token-shrinking fixes during business hours by making admin console changes, and reserve the Quarkus configuration restart for a maintenance window.

Conclusion

HTTP 431 in Keycloak is a size problem with two levers: the limit on the receiving side, and the payload on the sending side. Raising the Quarkus header limits — remembering to set both max-header-size and max-header-list-size — stops the immediate breakage. Disabling Full Scope Allowed, moving non-critical claims to the userinfo endpoint, and auditing protocol mappers produces tokens that are smaller, faster, and safer long-term.

If you are running Keycloak yourself and spending time on this kind of configuration work, Skycloak handles the infrastructure and tuning for you. See Skycloak’s pricing and plans to run managed Keycloak without the operational overhead.

Tired of running Keycloak yourself?

Skycloak runs real upstream Keycloak for you with a 99.99% SLA. No fork, no lock-in, just managed Keycloak that stays patched and on call so you don't have to.

Guilliano Molaire
Written by
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.

Start Free Trial Talk to Sales
© 2026 Skycloak. All Rights Reserved. Design by Yasser Soliman