How to Verify a JWT Signature
Decoding a JSON Web Token is easy — anyone can read it. Verifying it is what proves the token was issued by someone holding the right key and hasn't been tampered with. Here's how verification works for HS256 and RS256 tokens, how to test it online, and how to do it correctly in code.
Split the token into header.payload.signature, recompute the signature over “header.payload” with the algorithm in the header, and compare. HS256 uses the shared secret; RS256 and ES256 use the issuer's public key, usually from its JWKS endpoint (/.well-known/jwks.json) matched by the kid header. Then also check exp, nbf, iss and aud, and only accept the algorithms you expect. In Node.js, use jose's jwtVerify or jsonwebtoken's jwt.verify.
Decode vs verify
A JWT's header and payload are only base64url-encoded, so decoding reveals the claims without any key — try it with the JWT Decoder. Verification recomputes the signature with the key; only then can you trust the claims. Never make authorisation decisions from a decoded but unverified token.
HS256: verify with a shared secret
HS256 is HMAC-SHA256 over “base64url(header).base64url(payload)” with a secret both issuer and verifier know. Use the exact secret bytes — a common bug is treating a Base64-encoded secret as plain text. RFC 7518 requires a secret at least 256 bits long.
RS256 / ES256: verify with a public key or JWKS
The issuer signs with a private key and publishes the public key, usually as a JSON Web Key Set at https://issuer/.well-known/jwks.json. Pick the key whose kid matches the token's header, and verify with it. Identity providers rotate keys, so cache the JWKS but refresh it when you see an unknown kid.
Test a token and key online with the JWT Signature Verifier — it accepts a secret, a PEM public key, a JWK or a whole JWKS, and runs entirely in your browser.
Verify in Node.js
With the jose library: const JWKS = createRemoteJWKSet(new URL("https://issuer/.well-known/jwks.json")); const { payload } = await jwtVerify(token, JWKS, { issuer: "https://issuer/", audience: "my-api", algorithms: ["RS256"] }). With jsonwebtoken and a secret: jwt.verify(token, secret, { algorithms: ["HS256"] }).
Always pass an explicit algorithms list — it blocks the “alg: none” attack and HS/RS key confusion.
Check the claims too
A valid signature on an expired token is still invalid. Check exp (expiry) and nbf (not before) with a small clock-skew allowance, and iss and aud to make sure the token was meant for your service. See when any token expires with the JWT Expiration Checker, and create test tokens with the JWT Generator.
Try Free Web Tools Mentioned in This Guide
JWT Signature Verifier
Verify a JWT's signature with a secret, public key, JWK or JWKS — and check its claims.
JWT Decoder
Decode and inspect JSON Web Tokens in your browser.
JWT Generator
Create and sign JSON Web Tokens with HS256, RS256, ES256 and more — in your browser.
JWT Expiration Checker
See when a JWT expires, how long it has left, and convert exp/iat timestamps to dates.
Frequently Asked Questions
Can I verify a JWT without the secret or key?↓
No. Without the key you can only decode it. That's why the payload must never contain secrets and why servers must always verify.
Why does my JWT signature fail to verify?↓
Usually the wrong key (test vs production, or a rotated key — check the kid), a Base64 secret entered as text, or a token that was modified or truncated when copied.
Is it safe to paste a JWT into an online verifier?↓
Only into one that runs in your browser, like ours. Never paste production tokens or private keys into tools that send them to a server.