JWT decoder
The signature is shown but never verified, that needs the signing key, which this page does not have and should not ask for.
Processed in your browser · nothing is uploaded
Splits a JWT into its three parts and decodes the header and payload, showing the expiry in readable form when the token carries one. The signature is displayed but not verified.
How to use the jwt decoder
Not verifying is the only defensible position for a browser tool: verification needs the signing key, and pasting a production signing key into a web page is precisely the mistake nobody should make. Which leads to the point that matters about JWTs generally. A decoded payload proves nothing. Anyone can craft a token with any claims in it, only the signature makes it trustworthy, and only your server holds what is needed to check that. The classic attack is a token whose header says alg: none with an empty third part: it decodes perfectly here, and a server that trusts the header rather than its own configuration will accept it. Two related habits follow. Never put anything confidential in a payload, because it is plain Base64URL and readable by anyone holding the token. And treat a token you have seen in a URL, a log line or a screenshot as compromised, this page included.
The payload is re-serialised as formatted JSON on the way to the screen, which makes it readable and costs two things. Duplicate keys collapse to the last one, so a payload carrying a twice shows a single a. And any number beyond about 9 quadrillion loses precision, because JSON numbers are read as doubles: a sub of 12345678901234567890 comes back as 12345678901234567000. If your identifiers are large integers, compare them against the raw token rather than against what is displayed here.
The sample token this page opens with is the one everybody uses, and it makes a useful point by omission: it carries iat but no exp, so no expiry line appears at all. A missing EXPIRES row means the token has no expiry claim, not that it is fresh. When a row is there, the time is written in UTC as an ISO 8601 string and the expired or valid flag is judged against your own device clock, so a badly set clock will misjudge a token near its boundary. The spec anticipates that and lets a server allow a few minutes of leeway either way.
What people use it for
- Checking whether an access token has already expired
- Seeing which claims a login actually issued
- Finding which user or scope a token from a log belonged to
- Confirming which algorithm and key id a token names
- Checking that nothing confidential ended up in a payload
Questions
No, deliberately. Verification needs the signing key, which should never be pasted into a web page.
Reading. Its length tells you the algorithm family at a glance: 43 characters is a 32-byte HMAC-SHA256, 86 is HS512 or an ES256 pair, and 342 is a 2048-bit RSA signature.
No. The header and payload are Base64URL and readable by anyone with the token. Never put secrets in a payload.
Base64 with - and _ in place of + and /, and the trailing = padding removed, so the result is safe in a URL. This decoder accepts it with or without padding.
Header, payload and signature, joined by dots and each Base64URL encoded. The signature covers the first two parts exactly as they appear, so re-encoding a payload breaks it.
Five dot-separated parts is a JWE, an encrypted token rather than a signed one. There is nothing to read without the decryption key, so it is rejected rather than half-decoded.
Expiry and issued-at. Both are NumericDate values: seconds since 1 January 1970 UTC.
Because the token has no exp claim. A token without one never expires by itself and has to be revoked some other way.
No. The spec requires exp to be a number, so a quoted timestamp is ignored rather than guessed at. Whatever issued the token is not following RFC 7519.
It is in milliseconds. A NumericDate is in seconds, so the issuer has multiplied by a thousand and the date will read as some time in the year 50,000.
UTC, written as an ISO 8601 string. The expired or still-valid flag compares it against your device clock, so check your clock before trusting a verdict on a token near its boundary.
RFC 7519 registers seven: iss for the issuer, sub for the subject, aud for the intended audience, exp, nbf for not-before, iat, and jti for a unique token id. Everything else in a payload is custom.
A token claiming it needs no signature. It decodes here like any other and should be refused by every server, so a server has to decide the acceptable algorithm itself rather than read it out of the header.
Naming which key signed the token, so an issuer can rotate keys without breaking anything in flight. It is a hint to the verifier, not a secret.
No. Changing a single byte invalidates the signature, and producing a new one needs the key. Editing without the key is exactly the forgery a verifier exists to catch.
The middle part decoded to something that is not a JSON object. Usually the token was truncated in a copy, or a line break landed inside it.
A Base64 block can never be one character over a multiple of four. That length means characters are missing, again almost always a truncated copy.
Replacement characters mean the bytes were not valid UTF-8. The decoder does not stop for that, so what you are seeing is the token’s own content rather than an error here.
Short for an access token, minutes to an hour, with a separate refresh token doing the long-lived work. A long-lived access token cannot be withdrawn once issued.
Not from the token. Revocation lives in whatever issued it, and nothing inside the payload records it.
No, it is decoded entirely in the page. Even so, rotate any token you have pasted into a tool, including this one.
A cookie is an opaque reference the server looks up; a JWT carries its claims with it and is checked by signature alone. The trade is that a JWT needs no lookup and cannot be un-issued.