JWT Decoder
Header, payload, and expiry. No verify, nothing sent.
Blog

How to Read a JWT's Claims and Expiry (Safely)

Got a 401 and not sure if the token expired or the scope is wrong? Here is how to read a JWT's payload and exp without sending it anywhere.

A developer-style cover on a green background with the large words 'Decode a JWT' beside the header, payload, and signature segments and an expiry badge.

Your server returns a 401 Unauthorized. Did the token expire, or is the scope wrong? The answer is written inside the token, but it looks like a long, meaningless string, so it is tempting to just log in again and move on. Here is how to open that string and read it.

A JWT is encoding, not encryption

A JSON Web Token is three parts joined by dots: header.payload.signature, each one base64url-encoded.

This is where a common misconception starts. base64url is encoding, not encryption, so anyone can reverse it and read the contents without a key. The user ID and roles inside the payload are visible to anyone holding the token. That is exactly why you must never put secrets like passwords in the payload.

Reading the claims inside the payload

Each field inside the payload is called a claim. RFC 7519 defines a set of registered claims, so once you know the names, the meaning reads straight off.

ClaimMeaning
issIssuer
subSubject (usually the user ID)
audAudience
expExpiration time
iatIssued at
nbfNot valid before
jtiUnique token ID

The three time claims, exp, iat, and nbf, are the ones you reach for most often when debugging.

Checking expiry: exp is a Unix epoch

exp is the expiry, but its value looks like 1700003600. It was never meant to be read by a human: it is a Unix epoch, the number of seconds since 1970.

So a tool does the conversion for you. Drop a token into the PiPi Worlds JWT decoder and the time claims expand into ISO timestamps next to the payload. For example, iat: 1700000000 becomes 2023-11-14T22:13:20Z and exp: 1700003600 becomes 2023-11-14T23:13:20Z, and the gap between them tells you this token lives for one hour. When the expiry is in the past, an expired badge appears. After a 401, that single badge settles whether you simply need to refresh.

Decoding is not verifying

This is the distinction that matters most. Reading a token’s contents and confirming the token is genuine are two different things.

The third part, the signature, is a value the issuer created with a signing key. Confirming it has not been tampered with means recomputing the signature with the same secret or public key and comparing (RFC 7515). A decoder never receives or stores a key, so it does not verify anything. Treat the payload you can see as untrusted until your server checks the signature. Even if the payload says role: admin, believing that without verification is how privilege bugs happen.

Where are you pasting that production token?

This is where security incidents start. To debug a 401, people paste a production access token into whatever online decoder comes up first. The token is itself the credential, so any site that sends your input to its server has just handed that credential to a third party.

The PiPi Worlds JWT decoder decodes entirely in your browser. The token is never transmitted, logged, or stored, so production tokens are safe to inspect. Since the payload is JSON, a JSON formatter helps when you want to expand the structure further, and the Base64 tool is there if the base64url encoding underneath is what you want to explore.

Frequently asked questions

Does a JWT decoder check whether the token is valid?
No. A decoder reads the `exp` claim to show whether the token has expired, but it does not verify the cryptographic signature. Confirming the token has not been tampered with requires the signing secret or public key on your backend. Decoding and verifying are separate steps.
How do I read a token's expiry time?
The `exp` claim in the payload is the expiry, but its value is a Unix epoch in seconds, which is hard to read directly. A JWT decoder converts it to an ISO timestamp. Subtract `iat` (issued-at) from `exp` and you get the token's lifetime.
Is the payload encrypted?
No. A JWT's header and payload are just base64url-encoded JSON, so anyone can decode and read them. Because it is not encryption, never put secrets like passwords in the payload.
How do I tell if a 401 is caused by an expired token?
Decode the token and compare `exp` to the current time. If it is in the past, the token needs a refresh. If it is still in the future yet the request is rejected, the problem is more likely scope or signature verification.
Is it safe to paste a real production token into a decoder?
If the tool decodes entirely in your browser, your input is never sent to a server, so it is safe. The PiPi Worlds JWT decoder works that way. Even so, revoke any token you have shared elsewhere.

Sources

Written by the PiFl Labs content team from public sources and reviewed in-house before publishing.

Last reviewed:

Back to the tool →