JWT Structure and Expiry, Explained

A JSON Web Token looks like random text, but two of its three parts are readable by anyone. Here is what each part holds, how expiry works, why tokens expire too early or never, and the mistakes that turn a JWT into a security hole.

By Deresaw Tools · Updated 3 October 2026

JSON Web Tokens (JWTs, often pronounced “jots”) are how many websites and APIs remember who you are between requests. After you log in, the server gives your browser or app a token, and every later request carries it, usually in an Authorization: Bearer … header. The server checks the token instead of looking you up in a database each time.

When something goes wrong, such as a 401 error, a user logged out too early, or a token that never seems to expire, you need to read what is inside it. This guide explains the structure, then expiry, then the common mistakes.

Decode one as you read: paste a token into our JWT Decoder. It shows the header and payload, explains each claim, converts the dates and tells you whether the token has expired. It runs in your browser, so the token is not sent anywhere.

The three parts of a JWT

A JWT is three pieces of text joined by dots: header.payload.signature. Here is a real, harmless example token:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzg0MjEiLCJuYW1lIjoiQWRhIExvdmVsYWNlIiwicm9sZSI6ImVkaXRvciIsImlhdCI6MTc5MTA0MzIwMCwiZXhwIjoxNzkxMDQ0MTAwfQ.MpXRpuRa0eRpO39xvaI98humYBUym6-VzioWht2DInY

1. The header

The first part decodes to a small JSON object saying how the token is signed:

{"alg":"HS256","typ":"JWT"}

alg is the signing algorithm. HS256 means HMAC with SHA-256, using one shared secret. RS256 and ES256 use a private key to sign and a public key to verify. The header can also include kid, a key ID telling the server which of its keys to use.

2. The payload

The second part holds the claims, which are statements about the user and the token:

{"sub":"user_8421","name":"Ada Lovelace","role":"editor","iat":1791043200,"exp":1791044100}

This token says it belongs to user 8421, an editor, was issued at 16:00 UTC on 3 October 2026, and expires 15 minutes later.

3. The signature

The third part is a cryptographic signature over the first two. If anyone changes a single character of the header or payload, for example turning "role":"editor" into "role":"admin", the signature no longer matches and a correctly written server rejects the token. Our example was signed with the secret demo-secret-not-for-production, which you can enter in the JWT Decoder to see the signature check pass.

Encoded is not encrypted. The header and payload use base64url encoding, which anyone can reverse. Never put passwords, API keys or personal data you would not show the user into a JWT payload.

Why base64url and not base64?

Standard base64 uses +, / and = padding, which have special meanings in URLs. Base64url replaces + with - and / with _, and drops the padding. If you decode a JWT part with an ordinary base64 decoder such as our Base64 tool, swap those characters back and add = until the length is a multiple of 4.

The standard claims

RFC 7519, the JWT standard, defines seven registered claim names. All are optional, but most real tokens use several:

ClaimNameWhat it means
issIssuerWho created the token, such as your login server’s URL
subSubjectWho the token is about, usually a user ID
audAudienceWhich service the token is meant for. A server should reject tokens meant for another service.
expExpiration timeThe token must not be accepted at or after this moment
nbfNot beforeThe token must not be accepted before this moment
iatIssued atWhen the token was created
jtiJWT IDA unique ID, useful for blocking a specific token or preventing replay

Anything else, such as name, role, email or scope, is a custom claim agreed between the issuer and the services that read the token.

How expiry works

exp, iat and nbf are Unix timestamps in seconds: the number of seconds since 00:00:00 UTC on 1 January 1970. In the example, 1791044100 is 16:15:00 UTC on 3 October 2026. You can convert any of them with our Unix Timestamp Converter.

When a request arrives, the server compares exp with its own clock. If the current time is at or past exp, the token is rejected, usually with HTTP 401. The client is then expected to get a new token, either by using a refresh token or by asking the user to log in again.

Most libraries allow a small leeway, often 30 to 60 seconds, so that a slightly fast or slow clock does not reject a token that is technically still valid.

Why tokens are short-lived

A signed JWT is self-contained: the server trusts it without checking a database. That is what makes it fast, and it is also its biggest weakness. A JWT cannot be cancelled before it expires, unless the server also keeps a list of revoked token IDs, which removes much of the benefit. If an access token is stolen, it works until exp.

The usual design therefore pairs a short-lived access token (minutes) with a longer-lived refresh token (days or weeks) that is stored more carefully, sent only to the login server, and can be revoked there.

Why a token says it has expired when it should not

If the JWT Decoder or your server says a fresh token is expired, or an old token is still valid, the cause is almost always one of these:

  1. Milliseconds instead of seconds. JavaScript’s Date.now() returns milliseconds. Put that into exp and the token expires in the year 58,725. Read a correct seconds value as milliseconds and you get a date in January 1970, so the token looks expired immediately. A 13-digit timestamp is milliseconds; a 10-digit one is seconds.
  2. A wrong clock. If the server issuing tokens or the server checking them has a clock that is minutes off, tokens are rejected early or accepted late. Servers should sync their time with NTP. A phone or laptop with a wrong clock causes the same problem for checks made on the device.
  3. A lifetime that is too short. Some setups issue tokens valid for 60 seconds. Combined with a little clock difference, they can expire before the first request arrives. Compare iat and exp to see the real lifetime.
  4. nbf in the future. If a server stamps nbf using a clock that is ahead, the token is “not yet valid” for a while. This produces errors that look like expiry.
  5. A cached token. The app is still sending an old token from storage after getting a new one.

Time zones are never the cause. A Unix timestamp is the same moment everywhere. A token that seems to be an exact number of hours off is a sign that some code converted a local time to a timestamp incorrectly.

Security mistakes to avoid

  • Accepting "alg": "none". The standard allows unsigned tokens. Early libraries that trusted the header’s algorithm would accept a token with the signature removed and alg set to none. Servers must decide which algorithms they accept and reject everything else.
  • Algorithm confusion. If a server expects RS256 but lets the header choose, an attacker can sign a token with HS256 using the server’s public key as the HMAC secret. Again: fix the algorithm on the server side.
  • Weak HMAC secrets. An HS256 token can be brute-forced offline if the secret is short or a dictionary word. Use a long random secret of at least 256 bits.
  • Not checking aud and iss. A token issued for one of your services should not be accepted by another.
  • Decoding without verifying. Reading the payload is fine for debugging. Making access decisions on it without checking the signature is not.
  • Storing tokens where scripts can reach them. A token in localStorage can be stolen by any injected script (XSS). An HttpOnly, Secure cookie cannot be read by scripts, but then the site needs protection against cross-site request forgery. Each choice has a cost; know which one you made.
  • Pasting live tokens into tools that upload them. Many online decoders are fine, but a token pasted from a production log is a credential. Use a decoder that runs locally, and treat any token you have shared as compromised.

Frequently asked questions

Can anyone read a JWT?

Anyone who has the token can read its header and payload, because they are only base64url-encoded, not encrypted. The signature stops them changing it, not reading it. If the contents must be secret, use an encrypted token (JWE) or keep the data on the server.

What time zone is exp in?

None. exp, iat and nbf are Unix timestamps: seconds since 1 January 1970 UTC. The same number means the same moment everywhere, so time zones cannot cause an expiry problem, although a wrong clock can.

How long should a JWT be valid?

Access tokens are usually kept short, from 5 to 60 minutes, and renewed with a refresh token. The shorter the lifetime, the less damage a stolen token can do, because a JWT cannot normally be cancelled before it expires.

Is it safe to paste a token into an online decoder?

Only if the decoder does not send it anywhere. A live token is a credential. Our JWT Decoder works entirely in your browser, but it is still good practice to use expired or test tokens when you can.

Tools used in this guide