JWT Debugger

Real signatures, checked on your device.

  • Real signatures
  • Claims checked
  • Keys stay local
Three blank rounded tiles in violet, amber and teal in a row on a desk, joined by two small soft dots, with a small rounded brass key beside them.

The guide

A JSON Web Token is a signed note, not a secret, and only the signature makes it worth trusting

Anyone can read a JWT. What a server must check is the signature, the algorithm behind it and the clock, and each of those has failed in real systems.

  • 256 bits the smallest key RFC 7518 allows for HS256
  • 2048 bits the smallest RSA key for RS256 and PS256
  • 64 bytes the exact length of an ES256 signature
  • a few minutes the usual most leeway RFC 7519 suggests for clock skew
Read the full guide Fold the guide 7 min · 13 sources

Paste a JSON Web Token into almost any decoder and its contents appear at once: a user ID, a role, an expiry date. That surprises people who assumed the long string was scrambled. It was never scrambled. A JWT is a note written in plain JSON and wrapped in an encoding that survives a URL, with a seal at the end. The seal is the only part that carries any weight, and it is the part most often handled badly.

Three parts and one seal

RFC 7519, published in May 2015, defines the token and even suggests how to say it: like the English word “jot”. The common form is the compact serialization of JSON Web Signature, RFC 7515. It has three pieces joined by two periods. The first is the header, a small JSON object that names the signing algorithm in a member called “alg”. The second is the payload, the set of claims. The third is the signature, computed over the first two exactly as they appear in the token.

Each piece is written in base64url, the variant of base64 in RFC 4648 that swaps the plus sign and the slash for a hyphen and an underscore so the text can sit in a web address. JWS also drops the trailing equals signs. Base64url is an encoding, not a cipher. Anyone can reverse it with no key at all.

So a signed JWT is readable by everyone who holds it. RFC 7519 does allow the claims to be encrypted instead, by placing them inside a JSON Web Encryption structure, but that is a different token with five parts rather than three. If a token in your logs has three parts, assume that whoever saw the log saw the claims. Put nothing in a payload that you would not print on a badge.

What the signature buys is integrity. It says that someone holding the right key produced exactly these bytes, and that nobody has changed a single character since. Change “reader” to “admin” in the payload, keep the old signature, and a correct verifier refuses the token. That refusal is the whole point of the format.

Signatures, and the shortcut that was never one

RFC 7518 lists the algorithms. HS256, HS384 and HS512 use HMAC, the keyed hash of RFC 2104, with a secret shared by the issuer and the checker. RS256 and its siblings use RSA signatures in the PKCS #1 version 1.5 form; PS256 and its siblings use RSA-PSS; ES256, ES384 and ES512 use ECDSA on the P-256, P-384 and P-521 curves. Every one of them takes the header and payload as input and a key as the second input. Without the key, the output cannot be produced or checked.

The specification also sets floors. An HMAC key must be at least as long as the hash output, so 256 bits for HS256. RSA keys must be 2048 bits or more. An ES256 signature is exactly 64 bytes, the two numbers R and S at 32 bytes each, padded with leading zeros where needed; ES512 signatures are 132 bytes because each P-521 number takes 66. A verifier that sees any other length should stop.

The tool that used to live at this address did something else. Its signature was the secret itself, run through base64url, with a code comment calling it simplified for demonstration. That is worse than no signature. It depends on nothing in the header or payload, so it cannot detect a change, and it publishes the secret to anyone who decodes the third part. Every token it produced failed a real verifier, which is the only mercy in the story.

The browser can do the real thing. The Web Cryptography API, specified by the W3C and documented on MDN, offers importKey to load a raw secret, a key in the SPKI or PKCS #8 forms that PEM files wrap, or a JSON Web Key, then sign to make a signature and verify, which resolves to true or false. Its ECDSA output is already the raw R and S pair that JWS wants, not the DER structure that OpenSSL writes, so no reformatting is needed on the way out. A DER signature pasted in from elsewhere fails for that reason alone, and its length gives it away: it is never the exact 64, 96 or 132 bytes JWS expects.

Trust the key, not the header

The header names the algorithm, and that convenience has been turned into an attack more than once. RFC 8725, the 2020 best current practice for JWTs, records two. In the first, an attacker sets “alg” to “none”, the value for an unsecured token with an empty signature, and some libraries accepted that as a token needing no check at all. RFC 7518 had already said that such tokens must not be accepted by default; libraries accepted them anyway.

The second is subtler. A server that verifies RS256 tokens holds an RSA public key, which by design is public. An attacker rewrites the header to say HS256 and computes an HMAC using the text of that public key as the shared secret. A library that lets the header pick the algorithm will run HMAC with the same key text and get a match. RFC 8725 cites CVE-2015-9235 for exactly this flaw.

The fix is a rule, not a patch. RFC 8725 says each key must be used with exactly one algorithm, and the library must check that the header names that algorithm. The algorithm belongs to the key, decided by the person who configured it. The header is only a claim to be compared against it. This tool follows that rule: you say what the key is for, and a header that disagrees is rejected before any signature arithmetic runs. In its test suite, a copy that took the algorithm from the header instead accepted the forged HS256 token as valid, which is the clearest argument for the rule there is.

The same document asks for checks beyond the signature. If a token carries an issuer, the keys must belong to that issuer. If one issuer serves several applications, the audience claim must be present and must name the application reading it. Key IDs and key URLs in the header are untrusted input. A token can pass its signature check and still be meant for someone else.

Expiry, clocks and a worked example

Three claims carry time. “exp” is the moment after which the token must be refused, “nbf” the moment before which it is not yet valid, and “iat” when it was issued. All three are NumericDate values, a count of seconds since 1970-01-01 at midnight UTC. RFC 7519 says the current time must be before exp and at or after nbf, and allows a small leeway for clock skew, usually no more than a few minutes.

Skew is real. Two servers that disagree by thirty seconds will reject each other’s fresh tokens at the edges unless someone allows for it. Too much allowance quietly extends every token’s life. This page starts at one minute, and the slider goes to ten.

Here is a check you can repeat on this page. The first example is an HS256 token signed with a 32-byte secret given in base64. Its header is 27 bytes, which base64url turns into 36 characters; its payload is 155 bytes and becomes characters 38 to 244; its signature is 32 bytes, 43 characters, ending the 288-character token. The payload sets exp to 1893456000, which is 2030-01-01 00:00:00 UTC, so today the expiry row passes. Edit one character of the payload inside the token box and the verdict turns to an invalid signature at once. Edit the payload in its own editor instead, and the tool signs it again with the secret, and it verifies.

One more number. That 32-byte secret also encodes to 43 characters, exactly the length of a real HS256 signature. The old fake looked right. Only a verifier tells them apart.

What this tool cannot tell you

It checks a signature against the key you give it and claims against the clock and the values you type. It cannot tell you whether that key really belongs to the issuer, whether a token has been revoked, or whether your server will accept it, because those answers live in systems this page never contacts. It does not decrypt five-part encrypted tokens. It covers the twelve HS, RS, PS and ES algorithms, not EdDSA.

Two habits matter more than any tool. First, RFC 8725 says a password a person can remember must never be used directly as an HMAC key; draw the secret at random instead, as the test-key button here does. Second, keep live secrets and production tokens off shared tools. This one sends nothing typed into it anywhere, but a secret pasted into any web page has still left the vault it lived in. Test with test keys, and verify real tokens where they are used.

ECDSA has one more trap worth knowing. Each signature needs a fresh random number, and RFC 8725 warns that even a little predictability there can leak the private key. RFC 6979 describes a way to derive that number deterministically from the key and the message, and libraries that sign at scale should prefer it. A JWT is a small format with a long tail of details, and the details are where it breaks.

Sources

Every source consulted for this guide. Links checked October 20, 2026.

  1. RFC 7519: JSON Web Token (JWT) IETF RFC Editor
  2. RFC 7515: JSON Web Signature (JWS) IETF RFC Editor
  3. RFC 7518: JSON Web Algorithms (JWA) IETF RFC Editor
  4. RFC 7517: JSON Web Key (JWK) IETF RFC Editor
  5. RFC 8725: JSON Web Token Best Current Practices IETF RFC Editor
  6. RFC 4648: The Base16, Base32, and Base64 Data Encodings IETF RFC Editor
  7. RFC 2104: HMAC: Keyed-Hashing for Message Authentication IETF RFC Editor
  8. RFC 7638: JSON Web Key (JWK) Thumbprint IETF RFC Editor
  9. RFC 6979: Deterministic Usage of the Digital Signature Algorithm (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA) IETF RFC Editor
  10. SubtleCrypto: sign() method MDN Web Docs
  11. SubtleCrypto: verify() method MDN Web Docs
  12. SubtleCrypto: importKey() method MDN Web Docs
  13. Web Cryptography Level 2 World Wide Web Consortium (W3C)