Skip to content
Back to Blog

HS256 vs RS256: Which JWT Algorithm to Use

HS256 uses one shared secret; RS256 uses a private key to sign and a public key to verify. The right choice hinges on who needs to verify your tokens. Here is a clear decision framework.

SZ
Founder, Molixa
13 min read
Share
HS256 vs RS256: Which JWT Algorithm to Use
Table of contents10 sections

The choice of HS256 vs RS256 comes down to one question: who needs to verify your tokens? HS256 uses a single shared secret to both sign and verify, which is simple and fast but means every verifier also holds the power to mint tokens. RS256 uses a private key to sign and a separate public key to verify, so you can hand the public key to anyone without giving them the ability to forge. Get that distinction right and the rest of the decision falls into place.

Most comparisons stop at "symmetric versus asymmetric" and leave you to figure out which one your system actually needs. This guide gives you a decision rule tied to your architecture, real code-level differences, the performance trade-off that rarely matters, and the one configuration mistake that turns either algorithm into a forgery hole.

HS256 vs RS256 at a Glance#

Both are JSON Web Token signing algorithms defined in the JWA spec (RFC 7518). They protect the same thing: the integrity of a token, so a server can trust that the claims inside (user id, role, expiry) were not tampered with. They differ entirely in how the signing key is structured.

PropertyHS256RS256
Full nameHMAC with SHA-256RSA signature with SHA-256
Key typeOne shared secret (symmetric)Private/public key pair (asymmetric)
Who can signAnyone with the secretOnly the private key holder
Who can verifyAnyone with the secretAnyone with the public key
Token sizeSmaller signatureLarger signature
SpeedVery fastSlower to sign, fast to verify
Best forSingle trusted serviceMany verifiers, third parties

Key tip: with HS256 the verifier and the signer use the same key, so verification capability and forgery capability are inseparable. With RS256 they are split. That single fact drives almost every architecture decision below.

How HS256 Works (Symmetric, Shared Secret)#

HS256 stands for HMAC using SHA-256. HMAC is a keyed hash: you feed it the token header plus payload and a secret string, and it produces a fixed signature. To verify, the receiving service runs the exact same HMAC with the exact same secret and checks that the signatures match.

Because signing and verifying use one identical secret, there is no concept of a "public" side. Anyone who can verify a token can also create a valid one. That is fine when a single service issues tokens and verifies them itself.

A typical HS256 setup in Node looks like this:

import jwt from "jsonwebtoken";

// Sign
const token = jwt.sign({ sub: "user_123", role: "admin" }, SHARED_SECRET, {
  algorithm: "HS256",
  expiresIn: "15m",
});

// Verify (same secret)
const claims = jwt.verify(token, SHARED_SECRET, { algorithms: ["HS256"] });

The secret should be a long, random string (at least 32 bytes for SHA-256). A weak or guessable secret is the most common way HS256 deployments get broken, because an attacker who recovers the secret can sign anything.

When HS256 is the right call#

  • A single monolith or one backend service signs and verifies its own tokens.
  • An internal service-to-service link where you fully control both ends and can rotate a shared secret safely.
  • You want the smallest possible token and the fastest possible signing, with no need to expose verification to outside parties.

How RS256 Works (Asymmetric, Key Pair)#

RS256 is an RSA signature using SHA-256. You generate a key pair: a private key that signs and a public key that verifies. The private key never leaves your auth server. The public key can be published openly, because holding it lets a party verify tokens but never create them.

This split is the whole point. You can distribute the public key to a dozen microservices, a mobile app, or an external partner, and none of them can forge a token even if their copy of the key leaks.

import jwt from "jsonwebtoken";

// Sign with the private key (auth server only)
const token = jwt.sign({ sub: "user_123", role: "admin" }, PRIVATE_KEY, {
  algorithm: "RS256",
  expiresIn: "15m",
});

// Verify with the public key (any service)
const claims = jwt.verify(token, PUBLIC_KEY, { algorithms: ["RS256"] });

Most large identity providers (Auth0, AWS Cognito, Okta, Google, and others using OpenID Connect) issue RS256 tokens and publish their public keys at a JWKS endpoint (a .well-known/jwks.json URL). Your services fetch the public key from there and verify locally without ever contacting the auth server per request.

Warning: RS256 only protects you if you keep the private key secret and verify with the public key. A surprising number of bugs come from accidentally configuring a service to "verify" with the private key, or mishandling key formats. When in doubt, inspect the token header to confirm the alg is what you expect.

When RS256 is the right call#

  • Microservices: many services need to verify tokens but only one should issue them.
  • Third-party verification: a partner or client app must validate your tokens without being able to mint them.
  • You use an external identity provider or OpenID Connect, where RS256 plus JWKS is the standard.
  • You want to rotate signing keys without redistributing a secret to every verifier (publish a new public key, retire the old one).

ES256 and the Wider Algorithm Family#

HS256 and RS256 are the two you will meet most, but they are not the only options. ES256 (ECDSA using P-256 and SHA-256) is also asymmetric, like RS256, but uses elliptic-curve cryptography. It gives you the same private-sign/public-verify model with much smaller keys and signatures, which keeps tokens compact.

AlgorithmTypeNotes
HS256Symmetric (HMAC)Shared secret, fast, single-trust
RS256Asymmetric (RSA)Widely supported default for OIDC, larger signatures
ES256Asymmetric (ECDSA)Smaller keys/signatures, modern, slightly less universal library support

If you need asymmetric signing and care about token size or performance at scale, ES256 is worth a look. If you need maximum compatibility with existing tooling and identity providers, RS256 remains the safest default. The decision logic for ES256 versus RS256 is the same as RS256 versus HS256 plus a compatibility check on your libraries.

The Decision Rule: Who Verifies Your Tokens?#

This is the part most articles skip. Forget speed and key length for a moment and answer one question: how many distinct parties verify your tokens, and do you trust all of them to also be able to sign?

Use HS256 if every verifier is also a trusted signer#

If the only thing verifying your tokens is the same service (or a small set of fully trusted internal services) that issues them, the shared-secret model is simpler and faster. There is no key distribution problem because there is one secret and you control everywhere it lives. A classic monolith with a single backend is the textbook HS256 case.

Use RS256 (or ES256) the moment verification spreads out#

As soon as a token needs to be verified by something you do not want to be able to sign, you need asymmetric keys. Examples:

  • A frontend or mobile client checking a token's validity.
  • A fleet of microservices, where compromising any one verifier should not let an attacker forge tokens for the whole system.
  • An external API consumer or partner who must trust tokens you issue.
  • Any OpenID Connect / OAuth flow with an external identity provider.

The rule in one sentence: if the set of verifiers is larger than the set of parties you trust to sign, use RS256. With a shared secret, every verifier is a potential forger, and that risk grows with each service holding the secret.

Performance: Real, But Usually Not the Deciding Factor#

HMAC (HS256) is dramatically faster than RSA (RS256) for the signing step, often by an order of magnitude in raw benchmarks. RSA verification is reasonably fast, but RSA signing is the expensive operation. So if you sign enormous volumes of tokens on one box, HS256 wins on CPU.

For most applications this difference is noise. You typically sign a token once at login and verify it many times over its short life. Verification with RS256 is fast enough that the per-request cost is negligible next to a database query or network hop. Choose your algorithm on the trust model first, and only let performance break a tie if you are signing at extreme scale.

Tip: token size is a more practical concern than CPU. RS256 signatures are larger, which inflates every request that carries the token in a header. If you are bandwidth-sensitive and need asymmetric keys, ES256 produces noticeably smaller tokens than RS256.

The Mistake That Breaks Both: Trusting the alg Header#

Whichever algorithm you pick, the single most dangerous configuration error is letting the token itself decide how it gets verified. A JWT carries an alg field in its header, and naive verification code reads that field and verifies accordingly. Attackers exploit this in two well-known ways.

First, the alg: none trick: an attacker sets the algorithm to none, strips the signature, and some libraries happily accept the unsigned token as valid. Second, the RS256-to-HS256 confusion attack: a server set up for RS256 publishes its public key, an attacker changes the header to HS256, and signs a forged token using that public key as if it were an HMAC secret. If the server then verifies HS256 with the public key it knows, the forged token passes.

The fix is the same in both cases and it is one line of intent: pin the accepted algorithm on the verifier. Never let the token choose.

// Good: the server dictates the algorithm, the token does not
jwt.verify(token, PUBLIC_KEY, { algorithms: ["RS256"] });

Always pass an explicit allowlist of algorithms to your verify call and make sure it does not include none. Do not mix HS256 and RS256 on the same verification path, because that ambiguity is exactly what the confusion attack needs. If you want to understand why merely decoding a token tells you nothing about whether it is genuine, our guide on decoding versus verifying a JWT walks through the difference that catches a lot of developers.

When you are debugging a token in either algorithm, paste it into our free JWT decoder to read the header and confirm the alg value, expiry, and claims before you trust it. Seeing the actual header is the fastest way to catch a mismatched or tampered algorithm.

Key Management: The Practical Difference Day to Day#

The algorithm choice quietly decides how much key-management work you sign up for.

With HS256 you have one secret. That is easy to store, but rotating it means updating every place that holds it at the same time, and a leak anywhere is a full compromise. Keep it in a secrets manager, never in source control, and treat any service that holds it as part of your trusted core.

With RS256 you manage a key pair. Rotation is gentler: publish a new public key (often via a JWKS endpoint with a key id), start signing with the new private key, and retire the old key once outstanding tokens expire. Verifiers pick up the new public key automatically. The trade-off is more moving parts up front, which is why a single small service rarely needs it.

For a broader tour of safe token handling, including storage, expiry, and what to check on every verify, see our walkthrough on JWT decoder and token security.

HS256 vs RS256: The Bottom Line#

The HS256 vs RS256 decision is not really about cryptography strength, since both are secure when configured correctly. It is about trust boundaries. Use HS256 when one trusted service signs and verifies its own tokens and you want simplicity and speed. Reach for RS256 (or ES256 for smaller tokens) the moment more parties need to verify than you trust to sign, which describes nearly every microservice, mobile, third-party, and OpenID Connect setup.

Whatever you choose, lock the algorithm on the verifier with an explicit allowlist, never accept none, and never verify one algorithm's tokens with another's key. When something looks off, decode the token and read its header so you know exactly what you are dealing with.

Frequently Asked Questions#

Is RS256 more secure than HS256? Not inherently. Both are cryptographically sound when configured correctly. RS256 is safer for distributed systems because the public verification key cannot be used to forge tokens, so leaking it to many verifiers is low risk. HS256 concentrates all power in one shared secret, which is fine for a single trusted service but riskier the more places hold the secret.

Can I switch from HS256 to RS256 later? Yes, but plan for a transition window. You will generate a key pair, start signing new tokens with the RS256 private key, and update verifiers to accept RS256. Avoid configuring a service to accept both algorithms at once, since that ambiguity enables the algorithm confusion attack. Migrate cleanly and retire HS256 verification once old tokens have expired.

Which algorithm do identity providers like Auth0 and Cognito use? Most major identity providers default to RS256 and publish their public keys at a JWKS endpoint (a .well-known/jwks.json URL). Your services fetch the public key and verify tokens locally. This is why RS256 is the practical default for any OpenID Connect or OAuth integration with an external provider.

Why is RS256 slower than HS256? RSA signing is mathematically heavier than the HMAC operation HS256 uses, so signing is noticeably slower, often by an order of magnitude in raw benchmarks. Verification is fast for both. For most apps this gap does not matter because you verify far more often than you sign, and verification cost is small next to network and database time.

What is the algorithm confusion attack? It is an exploit where a server expecting RS256 is tricked into verifying an HS256 token using its own public key as the HMAC secret. Because the public key is, by design, known to attackers, they can forge a valid token. The fix is to pin the accepted algorithm on the verifier with an allowlist and never let the token's alg header decide. You can confirm a token's declared algorithm with a free JWT decoder while debugging.

Should I ever use ES256 instead of RS256? Consider ES256 when you need asymmetric signing but want smaller tokens and keys, which helps bandwidth and mobile clients. It uses the same private-sign, public-verify model as RS256. The main caveat is slightly less universal library and provider support, so verify your stack handles ES256 before committing.

More from Molixa

Try Molixa Tools

50+ free AI tools for content creation, SEO, coding, and more. No signup, no watermark.

Explore all tools