Layerbit
Security

JWT Signing Algorithms Compared: HS256, RS256, PS256, ES256 and EdDSA

The alg header is a three-to-five-letter decision that determines who can mint tokens, who can verify them, how big they are, how fast verification runs and which mistakes your verifier can make. This guide compares the algorithms the JOSE registry actually sees in production, explains the key formats each one uses, and gives a straightforward rule for choosing.

The short version

  • HS256 is symmetric: the same secret signs and verifies, so every verifier can also forge. Fine inside one service; wrong across trust boundaries.
  • RS256 is the asymmetric default of the identity world: a private key signs, a public key (published as a JWKS) verifies. Large signatures, universal support.
  • ES256 gives equivalent security to RS256 with 64-byte signatures and far faster signing; verification is slower than RSA. Signatures are raw r||s, not DER.
  • EdDSA (Ed25519) is the modern choice: tiny keys, tiny signatures, fast both ways, no nonce to get wrong. Support is now broad but not yet universal.
  • Whatever you choose, the verifier must pin the algorithm. The token's alg header is a claim by the sender, not an instruction to the receiver.

What the signature is actually over

A JWS signature covers the string base64url(header) + "." + base64url(payload). Not the decoded JSON — the exact encoded bytes. This is why re-serialising a payload with different whitespace or key order invalidates the signature even though the data is "the same", and why a verifier must check the signature against the token as received, before parsing anything. The algorithm named in alg determines how those bytes are signed; the kid header, if present, says which key.

The algorithms side by side

algKindSigning keyVerification keySignature sizeNotes
HS256/384/512HMACshared secretsame secret32 / 48 / 64 bytesFastest. Anyone who can verify can forge.
RS256/384/512RSA PKCS#1 v1.5RSA private (2048+)RSA public256 bytes at 2048-bitThe identity-provider default. Deterministic.
PS256/384/512RSA-PSSRSA privateRSA public256 bytes at 2048-bitRandomised padding; preferred by newer standards over RS*.
ES256/384/512ECDSAEC private (P-256 / P-384 / P-521)EC public64 / 96 / 132 bytesSmall, fast signing. Needs a good random nonce per signature.
EdDSAEd25519 (or Ed448)32-byte private32-byte public64 bytesDeterministic, fast, misuse-resistant. RFC 8037.
none———0Unsigned. Must be rejected by every verifier.

HMAC: shared secrets and their blast radius

HS256 computes HMAC-SHA-256 over the signing input with a secret. It is simple, fast and has no key format to get wrong — the secret is a byte string. Its property that matters is symmetry: verifying requires the secret, and holding the secret means being able to sign. A token verified by five microservices with HS256 has five services that could each mint a token for any user. A compromise of the least important of them compromises the whole scheme.

The secret also needs to be as strong as the hash. RFC 7518 requires at least 256 bits of key for HS256; a human-memorable passphrase is far weaker than that and can be recovered offline by anyone holding a single token. Generate the secret from a cryptographic random source, store it in a secrets manager, and never let it be a word.

Where HS256 is right: a single service issuing and verifying its own session tokens, or a tightly coupled pair that already share a secret for another reason.

RSA: RS256 and PS256

RS256 signs with an RSA private key and verifies with the corresponding public key. The public key can be published — identity providers expose it at a jwks_uri — so any number of services can verify without being able to forge. That asymmetry is why nearly every OAuth 2.0 and OpenID Connect deployment uses it.

The costs are size and speed. A 2048-bit RSA signature is 256 bytes, which base64url-encodes to 342 characters, so an RS256 token is routinely over a kilobyte before the payload is counted. Signing is slow (milliseconds); verification is fast, which suits the common pattern of one issuer and many verifiers. Keys below 2048 bits are no longer acceptable; 3072 or 4096 are reasonable for long-lived keys.

PS256 is RSA with PSS padding instead of PKCS#1 v1.5. PSS has a security proof and randomised padding; v1.5 has decades of deployment and a history of padding-oracle issues in encryption (not signing). Both are fine in practice for signatures. New specifications tend to prefer PS256; existing ecosystems overwhelmingly use RS256. If your verifier library offers both and you are starting fresh, PS256 is the slightly more conservative choice.

ECDSA: ES256 and the signature format trap

ES256 signs with a P-256 elliptic-curve key. A 256-bit EC key gives roughly the strength of a 3072-bit RSA key, with 64-byte signatures and sub-millisecond signing. Verification is slower than RSA's, which is the one place RSA keeps an edge. Tokens are much smaller; keys are much smaller.

Two things bite. First, ECDSA needs a fresh, unpredictable nonce for every signature. A repeated or biased nonce leaks the private key — this is how a well-known games console was broken — so the signing library must use a sound random source or the deterministic nonce construction of RFC 6979. Use a maintained library; do not implement it.

Second, the signature format. JWS mandates the raw concatenation r || s, each padded to the curve size: exactly 64 bytes for P-256. OpenSSL and most crypto APIs produce DER-encoded ECDSA signatures, which are 70 to 72 bytes and begin with 0x30. A token carrying a DER signature will fail verification everywhere, and the error message rarely explains why. If your ES256 tokens "never verify", check the signature length first.

EdDSA: Ed25519

Ed25519 is a signature scheme on the Edwards form of Curve25519. It is deterministic (no nonce to generate, so no nonce to get wrong), fast in both directions, resistant to several implementation pitfalls that affect ECDSA, and uses 32-byte keys and 64-byte signatures. In JOSE it is alg: EdDSA with an OKP key type and crv: Ed25519, specified in RFC 8037.

The only argument against it is reach. Browser Web Crypto gained Ed25519 in 2024–2025, major JWT libraries support it, and most identity providers can issue with it, but some older gateways and hardware security modules cannot verify it. If every verifier in your system is under your control and up to date, EdDSA is the best choice available. If a third party must verify your tokens, confirm their support before committing.

Key formats you will meet

  • PEM — Base64 DER between -----BEGIN … ----- lines. Public keys should be SPKI (BEGIN PUBLIC KEY); private keys PKCS#8 (BEGIN PRIVATE KEY). The older BEGIN RSA PRIVATE KEY and BEGIN EC PRIVATE KEY forms are PKCS#1 and SEC1, which some APIs, including browser Web Crypto, do not accept; convert with openssl pkcs8 -topk8 -nocrypt.
  • JWK — the key as JSON: {"kty":"RSA","n":"…","e":"AQAB"} or {"kty":"EC","crv":"P-256","x":"…","y":"…"} or {"kty":"OKP","crv":"Ed25519","x":"…"}. Private keys add d (and the CRT parameters for RSA).
  • JWKS — {"keys":[ …JWKs… ]}, each with a kid. The verifier fetches it once, caches it, and selects the key matching the token's kid. Publishing two keys for a while is how rotation works without downtime.
  • X.509 certificate — contains a public key but is not one. Extract it with openssl x509 -pubkey -noout before use.

What every verifier must do regardless of algorithm

The algorithm protects the token only if the verifier is strict about which algorithm it accepts. Configure the verifier with an explicit list — algorithms: ["RS256"] — rather than letting it read alg from the token and dispatch. A verifier that dispatches on the header can be steered: a token marked none is accepted without a signature, and a token marked HS256 is checked with HMAC using whatever key material the verifier holds, which for an RSA deployment is the public key. Modern libraries refuse both by default; older ones and hand-rolled verifiers do not.

Then select the key from configuration, never from the token: jwk, jku and x5u headers are informational at best. Then check exp, nbf, iss and aud. The signature proves who issued the token; the claims decide whether it is for you, now.

Choosing

  • One service, its own sessions, no other verifier: HS256 with a 256-bit random secret.
  • Identity provider with many relying parties, or any token crossing a trust boundary: RS256 for compatibility, PS256 if starting fresh and every verifier supports it.
  • Token size or signing throughput matters (mobile, IoT, high-volume issuance): ES256.
  • All verifiers are yours and current: EdDSA.
  • Never: none, HS256 with a passphrase, RSA under 2048 bits, or a verifier that trusts the header.
Try every algorithm in your browser The JWT tool signs with HS, RS, PS, ES and EdDSA, generates key pairs with PEM, JWK and JWKS output, verifies against a pasted key or JWKS, and lints the claims — with nothing sent anywhere.
Open the JWT tool

Frequently asked questions

Use HS256 only when the same service both issues and verifies the token, because the shared secret lets every verifier forge tokens. The moment a second service needs to verify, switch to an asymmetric algorithm: RS256 for the broadest compatibility, ES256 for smaller tokens, EdDSA if all your verifiers are modern. The secret for HS256 must be at least 256 random bits, never a passphrase.

Most often because the signature is DER-encoded. JWS requires the raw r and s values concatenated, exactly 64 bytes for P-256, whereas OpenSSL and many crypto APIs emit a DER structure of 70 to 72 bytes starting with 0x30. Convert to the raw form before base64url-encoding. The second common cause is a key on the wrong curve: ES256 requires P-256, ES384 P-384 and ES512 P-521.

Yes, and it is arguably the best-designed option: deterministic signatures, no nonce to mismanage, small keys and fast operations. The only consideration is support. Current browsers, major JWT libraries and most identity providers handle it; some older API gateways and hardware modules do not. Confirm every verifier in the path supports alg EdDSA with an OKP Ed25519 key before adopting it.

Both use RSA keys; they differ in padding. RS256 uses PKCS#1 v1.5, deterministic and universally supported. PS256 uses PSS, which is randomised and has a formal security proof. For signatures both are considered secure. New specifications lean towards PS256; the installed base is overwhelmingly RS256. Either is a reasonable choice, and the keys are interchangeable.

Because the header is written by whoever made the token. If the verifier does whatever the header says, a token can declare itself unsigned, or declare an HMAC algorithm so that the verifier's public key is used as the HMAC secret. A verifier configured with an explicit allowlist of algorithms and its own key material ignores the header's suggestion, which closes both problems.