Comparison
JWT Algorithms: HS256 vs RS256 vs ES256
Choosing a JWT signing algorithm sounds trivial — HS256 is the default in most tutorials — but the wrong choice bakes an architecture into your system that becomes painful to change later. The core split is whether the party that verifies the token needs to also be able to sign it.
HS256 uses one shared secret for both. RS256 and ES256 split signing (private key) from verification (public key), which lets you distribute verifiers freely without giving them the power to mint tokens.
TL;DR
| HS256 | RS256 | ES256 | |
|---|---|---|---|
| Type | Symmetric (HMAC) | Asymmetric (RSA) | Asymmetric (ECDSA) |
| Signer key | Shared secret | RSA private key | EC private key |
| Verifier key | Same shared secret | RSA public key | EC public key |
| Key size | ≥32 bytes recommended | 2048 or 4096 bits | P-256 curve (256 bits) |
| Signature size | 32 bytes | ~256 bytes | 64 bytes |
| Sign speed | Fastest | Slowest | Fast |
| Verify speed | Fastest | Fast | Fast |
| Multi-party verify without minting | No | Yes | Yes |
The options in depth
HS256 (HMAC-SHA-256)
Symmetric HMAC signing with a shared secret.
HS256 uses HMAC-SHA-256 with a shared secret to both sign and verify tokens. It is the simplest option and typically 2-5× faster than the asymmetric alternatives — a single hash operation per sign or verify.
The catch: any service that verifies also has the secret needed to mint tokens. If you deploy verification to 10 microservices, you have 10 places a leak can turn into a full-blown token forgery.
Good for
- ·A single service that both signs and verifies (monolith)
- ·Server-to-server tokens where the sender is fully trusted
- ·Fast local test tokens
Avoid when
- ·Multiple untrusted services must verify
- ·You need to publish keys via JWKS
RS256 (RSA-SHA-256)
The classic asymmetric choice — huge library support.
RS256 signs with an RSA private key and verifies with the matching public key. Because only the auth server holds the private key, you can safely publish the public key at a well-known JWKS endpoint and let every microservice, mobile client, and third party verify tokens on their own.
Downsides: keys are large (2048-4096 bits), signatures are ~256 bytes, and signing is the slowest of the three. Every OAuth provider (Auth0, Okta, Google) supports it, which is often the deciding factor.
Good for
- ·Public JWKS with many independent verifiers
- ·OAuth 2.0 / OIDC integrations
- ·Any scenario where signing is centralized but verification is distributed
Avoid when
- ·Bandwidth or token size matters (mobile with weak network)
- ·Signing throughput is a bottleneck
ES256 (ECDSA-SHA-256 with P-256)
Modern asymmetric — same security as RS256, tiny keys and signatures.
ES256 uses ECDSA with the P-256 curve. It offers the same “split key” property as RS256, but with dramatically smaller keys (~64 bytes) and signatures (~64 bytes) — roughly 4× smaller than RS256, at similar or better security.
Adoption is now essentially universal in modern OAuth providers, mobile SDKs, and browser Web Crypto API. Apple's Sign in with Apple mandates ES256.
Good for
- ·New systems — the modern default for asymmetric JWT
- ·Mobile and low-bandwidth environments
- ·Interop with modern OAuth providers (Apple, some Firebase flows)
Avoid when
- ·You need to interop with legacy systems that only speak RS256
Which one should you pick?
→ Will only one service ever verify these tokens?
Use HS256. It is simplest and fastest.
→ Will multiple services (or third parties) need to verify?
Use asymmetric. Prefer ES256 for new work, RS256 for compatibility.
→ Is this a new system without legacy constraints?
Use ES256. Same security as RS256 at ~1/4 the size.
→ Are you integrating with an existing OAuth provider?
Use whatever they support. RS256 is universal; ES256 is now widely available too.
Common pitfalls
- ⚠Never accept the `none` algorithm — a classic JWT vuln. Explicitly whitelist algorithms in your verifier.
- ⚠Confused-deputy attack: never verify with the wrong algorithm class. Some libraries in the past accepted `HS256` with an RSA public key as HMAC secret, letting attackers forge tokens with the public key.
- ⚠HS256 secrets < 32 bytes weaken the signature. Use 32/48/64 bytes for HS256/384/512.
- ⚠Rotating an HS256 secret requires simultaneous update on every service. RS256/ES256 lets you rotate keys via JWKS without downtime.
Frequently Asked Questions
Is HS256 less secure than RS256?
No — the cryptographic strength is similar. The difference is architectural: HS256 requires sharing the secret with every verifier, which increases exposure surface.
Why is ES256 signature 64 bytes if the curve is 256 bits?
An ECDSA signature is a pair (r, s), each 32 bytes for P-256, giving 64 bytes total. RSA signatures are the modulus size — 256 bytes for RSA-2048.
Can I switch from HS256 to RS256 later?
Yes, but you must reissue all in-flight tokens (they don't cross algorithms) and update every verifier's key material.
What about EdDSA / Ed25519?
EdDSA is even better than ES256 (faster, deterministic, no bad random number generator failure mode). Support is growing but not yet universal — check your library before adopting.