JWT Debugger

HS256 vs RS256 - which JWT signing algorithm should you use?

The two most common JWT algorithms make opposite trade-offs. The right one depends on how many parties need to verify your tokens and who you trust with the key.

The one-sentence difference

HS256 signs with a secret and verifies with the same secret. RS256 signs with a private key and verifies with the matching public key. Everything else follows from that.

What HS256 actually is

HS256 is HMAC using SHA-256. Take the token's header.payload bytes, take a secret, run them through HMAC, and the 32-byte output is the signature. To verify, do the same thing and compare.

The secret is a shared secret. Whoever verifies must hold it, and holding it means they can also sign. There is no way to hand out "verify only" access.

The secret must also be long. RFC 7518 says at least 256 bits for HS256, which means 32 random bytes. A short or human-memorable secret can be brute-forced offline by anyone holding a single valid token, since they can test guesses as fast as their hardware allows with no rate limit. The encoder will sign with any string you give it, which is useful for experiments and a bad idea in production.

What RS256 actually is

RS256 is RSASSA-PKCS1-v1_5 with SHA-256. The issuer holds an RSA private key, usually 2048 bits or more. Signing produces a 256-byte signature for a 2048-bit key. Anyone with the public key can check it, and the public key gives them no ability to sign.

That split is the point. An identity provider signs tokens; a dozen microservices verify them. Each service fetches the public key, typically from a JWKS endpoint at /.well-known/jwks.json, caches it, and never sees the private key at all. If one service is compromised, the attacker gets a public key and nothing else.

Side by side

HS256 RS256
Key material One shared secret Private key to sign, public key to verify
Who can forge tokens Anyone who can verify Only the private key holder
Signature size 32 bytes (43 chars base64url) 256 bytes for RSA-2048 (342 chars)
Signing speed Microseconds Milliseconds
Verification speed Microseconds Fast, though slower than HMAC
Key rotation Redistribute the secret to every verifier Publish a new public key; verifiers pick it up by kid
Good fit One service issues and verifies its own tokens Any setup with separate issuer and verifiers

Where each one goes wrong

HS256 with a weak secret. Covered above. Tools like hashcat have a JWT mode for exactly this. If the secret is a dictionary word, it is already broken.

HS256 across trust boundaries. Sharing the secret with a partner or a third-party service means trusting them not to mint tokens. Once shared, you also cannot rotate it without coordinating with everyone.

RS256 with algorithm confusion. This is the famous one. A verifier that trusts the token's alg header and uses one key variable for everything can be tricked: the attacker takes the public key, which is public, signs a forged token with HS256 using that PEM string as the HMAC secret, and sets alg: HS256. A naive verifier runs HMAC with the public key as the secret and it matches. The fix is to pin the expected algorithm in the verifier and never let the token choose. Every mainstream library now requires an explicit algorithms list for this reason, and this site's decoder verifies with the algorithm you select, not the one in the header.

RS256 with unverified JWKS. Fetching keys over plain HTTP, or from a URL taken from the token's own header (jku), lets an attacker supply their own key. Fetch JWKS over TLS from a URL you configured, never one the token tells you.

Token size matters more than you think

An RS256 token is around 300 characters longer than the same HS256 token. In a cookie sent on every request, or a header on every API call, that adds up. If you need asymmetric verification and size is a concern, ES256 gives you a 64-byte signature with the same public/private split. EdDSA (Ed25519) is smaller and faster still, though library support is less universal.

A decision rule

Use HS256 when a single service issues and verifies its own tokens, nobody else ever needs to check them, and you can generate a 32-byte random secret and keep it in a secrets manager.

Use RS256 (or ES256) the moment a second party needs to verify. That includes a separate API behind an identity provider, a mobile app, or any third party. Publish the public key through JWKS with a kid, and rotate by adding a new key before retiring the old one.

If you are choosing today for a new system and your libraries support it, ES256 is the better asymmetric default. If you are integrating with Auth0, Cognito, Firebase, or most commercial identity providers, RS256 is what they issue, and you verify what you are given.

You can see the difference for yourself: build a token in the encoder with HS256, switch the algorithm to RS256, and watch the signature triple in length.

Frequently asked questions

Neither is weaker as a primitive. RS256 is safer in practice because verifiers only hold the public key, so a compromised API server cannot forge tokens. HS256 is fine when a single service both issues and verifies, and the secret is long and random.

Yes, if the verifier accepts both algorithms during the transition and pins each one to its own key. Reject any token whose alg does not match the key you look up for it, or you open the door to the key confusion attack.

ES256 is ECDSA over the P-256 curve. It has the same public/private split as RS256 with much smaller keys and signatures, and faster signing. Verification is a little slower than RSA. If your libraries support it, it is a good default for new systems.