JWT Verifier
Check whether a JWT's signature is genuine using its secret (HS256/384/512) or a public key (RS256/384/512, ES256/384). Verification runs entirely via the browser's Web Crypto API — the key never leaves the page.
Paste a token, choose the algorithm (or let it auto-detect from the header), enter the matching secret or PEM public key, then click Verify. The check runs locally using SubtleCrypto — nothing is sent to a server.
Token
Learn more
What a JWT is and how it's structured
A JWT (JSON Web Token) is a compact, signed string of three Base64URL segments separated by dots: header.payload.signature. The header names the algorithm and token type; the payload holds claims — key/value statements about the token (who it's for, when it expires, what it's allowed to do); the signature proves the first two segments haven't been tampered with. Everything is readable by anyone — the format is deliberately transparent — which is why payloads must never contain secrets.
The claims are standardized: iss (issuer), sub (subject), aud (audience), exp (expiration), nbf (not before), iat (issued at) and jti (unique token ID), plus any custom claims your application defines. Decoding a JWT requires no key at all — it's just Base64URL-decoding two JSON objects — which is why "the JWT payload shows user: admin" means nothing until the signature is verified.
How verification actually works
Verification recomputes the signature over the header and payload using the key you provide, and compares it to the signature segment in the token — a constant-time comparison, so a mismatch doesn't leak timing information about how close a forged signature was. For HMAC algorithms (HS256/384/512) the key is a shared secret known to both issuer and verifier. For RSA (RS*) and ECDSA (ES*) the issuer signs with a private key and verifiers check with the matching public key, so only the signer can mint valid tokens while anyone holding the public key can check them.
Two checks matter beyond the signature: the algorithm must be one you actually expect — a token saying alg: none, or switching from RS256 to HS256 and signing with the public key as the "secret," are classic attacks the verifier must reject explicitly — and the claims must still be valid: an expired token (exp in the past) that has a perfect signature is still worthless. Verification that stops at the signature check misses half the job.
Common JWT attacks and how to prevent them
The alg:none attack sends a token whose header declares no algorithm and whose signature segment is empty — some old libraries accepted it. Algorithm confusion (RS256 → HS256) tricks a verifier into using the public key as an HMAC secret, which the attacker can compute. Brute-force of weak HMAC secrets succeeds when the "secret" is a short, low-entropy string. Each has a precise fix: pin the expected algorithm, never trust the header's algorithm claim, and use high-entropy keys (256 bits minimum for HS256).
On the payload side, tokens are readable by design, so anything sensitive in them is public — don't store passwords, PII or internal IDs there. And because tokens are bearer credentials, any channel that logs URLs (browser history, Referer headers, server logs) can leak them; keep tokens out of URLs, use short lifetimes, and pair long-lived refresh tokens with rotation and revocation so a stolen token expires quickly.
Client-side vs server-side verification
Client-side verification is appropriate for the Web Crypto API's natural habitat: proving a token's signature to the user's own browser, where the key is public (asymmetric) or the check is explicitly local-only. It's genuinely useful for tooling and demos, and for apps where the token itself is the product — but it can never enforce anything. A user can always skip the check, edit the payload, and send the request anyway; the client is an adversary you don't control.
Server-side verification is where security actually happens: every API request carries the token, and the server re-verifies the signature and claims on each request before doing anything with the token's contents. That verification must happen in the authentication layer — middleware that rejects invalid tokens before handlers run — not scattered through business logic, or the first route that forgets to check becomes the vulnerability.
Best practices for JWT handling
Use the right algorithm for the job: asymmetric keys (RS/ES) when verifiers are many or untrusted (SSO, public APIs), symmetric (HS*) when a small set of first-party services share a secret. Keep token lifetimes short (minutes, not days) for access tokens, store them in memory rather than localStorage (XSS-resistant), and use the Authorization: Bearer header rather than cookies or URLs unless you need CSRF protection specifically.
Verify everything, not just the signature: the issuer (iss), audience (aud) — a token issued for a different service must not be accepted here — and the expiry and not-before times. Add a jti check against a revocation list for high-sensitivity operations. And treat the verifier as one layer: it answers "is this token genuine," not "should this request be allowed" — authorization is a separate question that comes after.
Frequently asked questions
Is my secret or private key sent anywhere?
No — verification uses the browser's built-in Web Crypto API (SubtleCrypto), running entirely on your device. Only ever paste a public key here, never a private signing key.
Which algorithms are supported?
HS256/384/512 (HMAC, needs the shared secret), RS256/384/512 (RSA, needs the public key in PEM/SPKI format) and ES256/384 (ECDSA, also PEM/SPKI). Other algorithms are explicitly rejected rather than silently mismatched.
What format should the public key be in?
Standard PEM SPKI format, starting with -----BEGIN PUBLIC KEY-----. If you only have a private key or a certificate, you'll need to extract the public key in that format first.
Want the background? Read the guide: What is a JWT?