How big is an ML-DSA JWT?

Short answer

With a typical 279-byte header and claims, an ML-DSA-65 JWT is 4,692 bytes, an ML-DSA-44 JWT 3,507 bytes, and an ML-DSA-87 JWT 6,450 bytes. The same token signed with ES256 is 366 bytes.

ML-DSA-65 and ML-DSA-87 tokens no longer fit in a browser cookie (4,096 bytes), and that limit can't be raised.

JWT size by signing algorithm

A compact JWS is base64url(header).base64url(claims).base64url(signature). The signature segment dominates once you switch to a post-quantum algorithm. Base64url (no padding) turns n bytes into ⌈4n/3⌉ characters.

AlgorithmSignatureBase64url signatureWhole JWT (279 B header + claims)vs. ES256 token
EdDSA (Ed25519)64863661.0×
ES256 (P-256)64863661.0×
RS256 (RSA-2048)2563426221.7×
ML-DSA-442,4203,2273,5079.6×
ML-DSA-653,3094,4124,69213×
ML-DSA-874,6276,1706,45018×
SLH-DSA-128s7,85610,47510,75529×

Bytes. Header and claims vary by issuer: add or subtract the difference from your own tokens. The signature segment is exact.

Will it fit?

Bearer tokens travel in Authorization headers (22 bytes of prefix) and session tokens in cookies. Defaults for common servers:

LimitBytesTypeML-DSA-44ML-DSA-65ML-DSA-87
Browser cookie session=<JWT> (RFC 6265)4,096hardfits+604+2,362
nginx large_client_header_buffers 4 8k (per header line)8,192softfitsfitsfits
Apache LimitRequestFieldSize (per header)8,190softfitsfitsfits
Node.js --max-http-header-size (all headers)16,384softfitsfitsfits

Overflow in bytes: the token plus its session= cookie name or Authorization: Bearer prefix. nginx first tries client_header_buffer_size (1k by default) and falls back to the large buffers, so that smaller setting doesn't reject long tokens. Node.js counts every header on the request against one budget, so real headroom there is smaller than shown.

What to do about it

Find every limit these sizes break in your stack

Paste a migration, an nginx.conf or an mbedTLS header into the checker, or ask us for a free scan of the whole repository.

Related