jwtk/jjwt · error · UnsupportedJwtException
The JWT header references compression algorithm '%s', but pa
Error message
The JWT header references compression algorithm '%s', but payload decompression for Unprotected JWTs (those with an alg header value of 'none') or Unencoded JWSs (those with a b64 header value of false) that also rely on a SigningKeyResolver are disallowed by default to protect against [Denial of Service attacks](https://www.usenix.org/system/files/conference/usenixsecurity15/sec15-paper-pellegrino.pdf). If you wish to enable Unsecure JWS or Unencoded JWS payload decompression, call the JwtParserBuilder.unsecuredDecompression() method, but please read the security considerations covered in that method's JavaDoc before doing so.
What it means
To mitigate decompression DoS (zip-bomb) attacks, JJWT blocks payload decompression for unverified tokens — unprotected JWTs (alg=none) or unencoded JWSs (b64=false) relying on a SigningKeyResolver — unless you explicitly opt in with JwtParserBuilder.unsecuredDecompression(). Hitting this means the token has a zip header but was not integrity-verified by a key-verified path.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java:589
integrityVerified = true; // AEAD performs integrity verification, so no exception = verified
} else if (hasDigest && this.signingKeyResolver == null) { //TODO: for 1.0, remove the == null check
// not using a signing key resolver, so we can verify the signature before reading the payload, which is
// always safer:
JwsHeader jwsHeader = Assert.stateIsInstance(JwsHeader.class, header, "Not a JwsHeader. ");
digest = verifySignature(tokenized, jwsHeader, alg, new LocatingKeyResolver(this.keyLocator), null, payload);
integrityVerified = true; // no exception means signature verified
}
final CompressionAlgorithm compressionAlgorithm = zipAlgs.apply(header);
if (compressionAlgorithm != null) {
if (!integrityVerified) {
if (!payloadBase64UrlEncoded) {
String msg = String.format(B64_DECOMPRESSION_MSG, compressionAlgorithm.getId());
throw new UnsupportedJwtException(msg);
} else if (!unsecuredDecompression) {
String msg = String.format(UNPROTECTED_DECOMPRESSION_MSG, compressionAlgorithm.getId());
throw new UnsupportedJwtException(msg);
}
}
payload = payload.decompress(compressionAlgorithm);
}
Claims claims = null;
byte[] payloadBytes = payload.getBytes();
if (payload.isConsumable()) {
InputStream in = null;
try {
in = payload.toInputStream();
if (!hasContentType(header)) { // If there is a content type set, then the application using JJWT is expected
// to convert the byte payload themselves based on this content type
// https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.10 :
//
// "This parameter is ignored by JWS implementations; any processing of this
// parameter is performed by the JWS application."View on GitHub (pinned to fb71496164)
Solutions
- Verify the token with a real key (verifyWith/keyLocator) so integrityVerified is true and decompression is allowed.
- If unsigned/unencoded compressed tokens are truly required, call .unsecuredDecompression() on the JwtParserBuilder after reading its security JavaDoc.
- Remove the zip header from token production for unsecured tokens.
- If you never expect compression, inspect the token producer adding a zip header and disable it there.
Example fix
// before Jwts.parser().build().parse(unsecuredCompressedJwt); // after Jwts.parser().unsecuredDecompression().build().parse(unsecuredCompressedJwt);
Defensive patterns
Strategy: try-catch
Validate before calling
Map<String,Object> h = getUntrustedHeader(token);
if ("none".equals(h.get("alg")) && h.containsKey("zip") && !unsecuredDecompressionEnabled) {
throw new IllegalArgumentException("compressed unsecured token rejected");
} Try / catch
try { parser.parse(token); } catch (UnsupportedJwtException e) { if (e.getMessage().contains("UNPROTECTED_DECOMPRESSION") || e.getMessage().contains("decompression")) { /* verify with a key or opt in deliberately */ } } Prevention
- Verify tokens with real keys wherever possible; reserve unsecuredDecompression() for legacy cases.
- Never accept alg=none tokens in production without explicit policy.
- Disable compression on unsigned tokens at the producer.
- Keep JJWT updated and review the security notes on the unsecuredDecompression() JavaDoc when opting in.
When it happens
Trigger: Parsing a compressed JWT with alg=none, or an unencoded (b64:false) compressed JWS parsed with a SigningKeyResolver, without calling unsecuredDecompression().
Common situations: Accepting unsigned tokens in dev/test environments with compression enabled, legacy unsecured token flows upgraded to newer JJWT versions where the safety gate was added, security-sensitive deployments where the default deny is intentional.
Related errors
- The JWT header references compression algorithm '%s', but pa
- 'unsecuredDecompression' is only relevant if 'unsecured' is
- Both 'zip()' and 'compressionCodecResolver' cannot be config
- Malformed or excessively complex ${name} JSON. If experience
- JWE Header ${param} value ${iterations} exceeds ${getId()} m
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/57ad7f898a11451e.
Report an issue: GitHub.