In the HS256 vs RS256 choice, the deciding question is who needs to verify your tokens. Use HS256 when the same service signs and checks its own tokens, because one shared secret is simplest. Use RS256 when other services, clients or third parties must verify tokens, because they only need a public key and can't forge new tokens with it.
What HS256 and RS256 actually are
HS256 is an HMAC with SHA-256, a symmetric algorithm: one secret both signs and verifies. RS256 is an RSA signature with SHA-256, an asymmetric algorithm: a private key signs and a public key verifies.
The Auth0 blog puts it plainly. HS256 "is a symmetric keyed hashing algorithm that uses one secret key," while RS256 "is an asymmetric algorithm that uses a public/private key pair." The JWA spec, RFC 7518, lists HS256 as Required, RS256 as Recommended and ES256 as Recommended+.
Both are equally readable. A JWT's header and payload are only base64url-encoded, whichever algorithm signs them, so neither one hides the claims. If that surprises you, see why a JWT can be decoded without the secret.
HS256 vs RS256 at a glance
The main difference between HS256 vs RS256 is what a verifier can do with its key. With HS256, anyone who can verify a token can also sign one.
| HS256 | RS256 | |
|---|---|---|
| Key type | One shared secret | Private key + public key |
| Who can sign | Anyone holding the secret | Only the private key holder |
| Who can verify | Anyone holding the secret | Anyone with the public key |
| Minimum key size (RFC 7518) | 256 bits | 2048 bits |
| Key distribution | Secret copied to each verifier | Public key published, often as a JWK Set |
| Key rotation | Redeploy the new secret everywhere | Publish a new key with a new kid |
| Main risk | Secret leaks from any verifier | Algorithm confusion if verifiers don't pin alg |
Which one to pick for your setup
Pick the algorithm by counting the parties that verify tokens. The more of them there are, and the less you control them, the stronger the case for RS256.
| Your setup | Pick | Why |
|---|---|---|
| One backend signs and verifies its own session tokens | HS256 | One secret, one place to protect it |
| A few internal services you deploy together | Either; RS256 if a service only needs to verify | HS256 gives every service signing power |
| Many services or teams verify tokens | RS256 | Only the auth service holds the private key |
| Third parties or customers verify your tokens | RS256 | You can't hand out a shared secret |
| You run an OpenID Connect provider | RS256 | The spec requires RS256 support |
That last row is a hard rule. OpenID Connect Discovery says of the ID token signing algorithms a provider lists: "The algorithm RS256 MUST be included."
My take: start new systems on RS256 unless one service will be the only verifier forever. Systems grow a second consumer sooner than you expect, and moving from a shared secret to key pairs later means touching every service at once.
Why HS256 gets risky as you add services
HS256 gets riskier with each service you add because every verifier holds the signing secret. If any one of them leaks it through a log, a config dump or a compromised container, the attacker can mint valid tokens for all of them.
Auth0 puts the consequence bluntly: every place that verifies must be secured as tightly as the issuer. Fine for one backend. A real cost when ten services, a cron job and a staging environment all hold the same string.
HS256 still has its place. Auth0 lists legacy apps that can't support RS256, and high request volumes, since HMAC is the more efficient of the two.
Key size and secret rules most guides skip
Both algorithms have minimum key sizes in the spec, and a weak key breaks an otherwise correct setup.
- HS256 secret: RFC 7518 says "A key of the same size as the hash output (for instance, 256 bits for HS256) or larger MUST be used." A short string like
secret123falls far below that. - No passwords as secrets: the JWT best-practices RFC, RFC 8725, says "human-memorizable passwords MUST NOT be directly used as the key" for an HMAC. Use a cryptographic random generator.
- RS256 key: for RSA, "A key of size 2048 bits or larger MUST be used" (RFC 7518). The node-jsonwebtoken README enforces this: when signing with RSA, "the minimum modulus length is 2048" unless you set
allowInsecureKeySizes.
A weak HS256 secret is worse than it looks. Anyone holding one valid token can guess secrets offline until the signature matches, without ever touching your server.
Pin the algorithm when you verify
Whichever algorithm you pick, the verifier must accept only that algorithm. RFC 8725 says "Libraries MUST enable the caller to specify a supported set of algorithms," and it describes the attack that happens when they don't.
In that attack, someone takes an RS256 token, changes the header to an HMAC algorithm, and signs it with your public key as the secret. RFC 8725 notes that "some libraries would try to validate the signature using HMAC-SHA256 and using the RSA public key as the HMAC shared secret." Since the public key is public, the forged token passes.
Pass an explicit list instead of trusting the token's header:
// RS256 verifier: accept nothing else
jwt.verify(token, publicKey, { algorithms: ["RS256"] });
Leave algorithms out and node-jsonwebtoken picks defaults from the key type, for example ["HS256", "HS384", "HS512"] for a secret. That beats accepting anything, but the explicit list documents your intent and survives a key-type change.
Rotating keys with RS256 and JWKS
RS256 makes key rotation additive: you publish a new public key next to the old one and switch signing over. Verifiers find the right key through the kid (key ID) in the token header.
RFC 7517 says kid "is used to match a specific key," for instance "to choose among a set of keys within a JWK Set during key rollover." A JWK Set is just a JSON object with a keys array. OpenID Connect providers publish theirs at a jwks_uri, and the spec says that set "MUST NOT contain private or symmetric key values."
With HS256 there's no public half to publish. Rotation means getting the new secret into every verifier, usually with a window where both secrets are accepted.
How to check which algorithm a token uses
The algorithm is in the token's header, so you can read it without any key. Decode the token and look at alg, which will say HS256, RS256 or another value.
A browser-based JWT decoder shows the header, payload and signature parts. The limitation: a decoder reads alg but doesn't verify the signature, so it tells you which algorithm a token claims, not whether the claim is true. JWT decode vs verify covers why that gap matters.
If a token you just signed fails verification, the algorithm is rarely the cause. Timing usually is: see why a JWT shows as expired when exp looks right. For where to keep tokens in a browser, read JWT in localStorage vs cookies.
FAQ
Is RS256 more secure than HS256?
Not as an algorithm. Both are fine when used correctly. RS256 is safer in multi-service setups because verifiers only get a public key, so a leak on a verifier can't be used to sign new tokens.
Can I use a password as an HS256 secret?
No. RFC 8725 says human-memorizable passwords must not be used directly as an HMAC key, and RFC 7518 requires an HS256 key of at least 256 bits. Use a random secret from a cryptographic generator.
Should I use ES256 instead of RS256?
ES256 is a good choice if every verifier supports it. RFC 7518 rates ES256 as Recommended+, one step above RS256. OpenID Connect providers must still support RS256, so RS256 is the safer pick for broad compatibility.
Can I tell which algorithm a JWT uses?
Yes. Decode the token and read the alg field in the header, such as HS256 or RS256. Decoding needs no key, but it also doesn't prove the signature is valid.