HS256 vs RS256 — Which JWT Algorithm Should You Use?
Choosing the wrong JWT signing algorithm is a common security mistake. This guide explains the exact differences between HS256 and RS256 and when to use each one.
The short answer
Use HS256 when:
- → Single server/service
- → You control both issuer and verifier
- → Simple monolithic application
- → Internal microservices (same team)
Use RS256 when:
- → Multiple services verify tokens
- → Third party services need to verify
- → Public API with external consumers
- → Microservices architecture
How HS256 works
HS256 stands for HMAC with SHA-256. It uses a single shared secret key to both sign and verify tokens. This means the same secret must be available to every service that needs to verify the token.
// HS256 — same secret for signing and verifying
const secret = "my-super-secret-key"
// Sign
const token = jwt.sign(payload, secret, {
algorithm: 'HS256'
})
// Verify (requires the SAME secret)
const decoded = jwt.verify(token, secret)
// Problem: every service that verifies
// needs access to the secret key!⚠️ Security risk: If multiple services share the same HS256 secret, any compromised service can create valid tokens — not just verify them.
How RS256 works
RS256 stands for RSA Signature with SHA-256. It uses asymmetric cryptography — a private key to sign tokens and a public key to verify them. Only the private key can create valid tokens. The public key can only verify.
// RS256 — different keys for signing and verifying
const privateKey = fs.readFileSync('private.pem')
const publicKey = fs.readFileSync('public.pem')
// Sign — only auth server has private key
const token = jwt.sign(payload, privateKey, {
algorithm: 'RS256'
})
// Verify — any service can have the public key
const decoded = jwt.verify(token, publicKey)
// Public key can verify but CANNOT create tokens
// Much safer for distributed systems!Side by side comparison
| Feature | HS256 | RS256 |
|---|---|---|
| Algorithm type | Symmetric (HMAC) | Asymmetric (RSA) |
| Keys required | 1 shared secret | Private + public key pair |
| Who can sign | Anyone with secret | Only private key holder |
| Who can verify | Anyone with secret | Anyone with public key |
| Performance | Faster | Slightly slower |
| Key distribution | Secret must stay secret | Public key can be shared freely |
| Best for | Single service | Distributed systems |
| Used by | Simple APIs | Auth0, Firebase, Okta |
Real world examples
Firebase uses RS256
Firebase Auth tokens are signed with RS256. Google publishes the public keys at a well-known URL so any service can verify Firebase tokens without needing Google's private key.
// Firebase public keys are published here https://www.googleapis.com/robot/v1/metadata/x509/ securetoken@system.gserviceaccount.com // Your backend fetches these to verify tokens // No shared secret needed
Supabase uses HS256
Supabase uses HS256 with your project's JWT secret. This works well because your backend and Supabase share the same secret. If you expose your Supabase JWT secret anyone can forge tokens.
How to check which algorithm your token uses
The algorithm is stored in the JWT header — the first part of the token. You can see it instantly using our JWT Decoder.
// JWT header (decoded)
{
"alg": "RS256", ← algorithm used
"typ": "JWT",
"kid": "abc123" ← key ID (RS256 only)
}
// For HS256 tokens
{
"alg": "HS256",
"typ": "JWT"
}Check your JWT algorithm instantly
Paste your JWT token to see the algorithm, header claims and payload.
Open JWT Decoder →