How to decode a JWT and check whether it is valid

The short answer. Decoding a JWT only reads it: split it at the two dots and Base64URL-decode the first two parts to see the header and the claims. Checking it is valid means verifying the signature with the issuer's secret or public key, then checking that exp has not passed, nbf has, and iss and aud are the values your service expects.

A JSON Web Token is a small signed note. A login server writes some claims about a user, signs them, and hands the result to the browser or app, which sends it back with each request. Anyone holding the token can read it; only someone with the key can make a new one that verifies. That difference, readable but not forgeable, is the whole point, and it is why "decode" and "validate" are separate steps.

What is inside a JWT

A token looks like this, three parts joined by dots:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyLTQyIiwiaWF0IjoxNzkxMjEyNDAwLCJleHAiOjE3OTEyMTYwMDB9.LNV6F4X2KYqv02h8_uZOlWecsEu9NHOIlhjnkMd-VgY
Part Decodes to What it says
Header {"alg":"HS256","typ":"JWT"} signed with HMAC-SHA256
Payload {"sub":"user-42","iat":1791212400,"exp":1791216000} the claims: who, issued when, expires when
Signature 32 bytes of binary proof that the first two parts were signed with the key

The first two parts are Base64URL, which is Base64 with - and _ in place of + and / and the = padding removed. They are encoded, not encrypted: anyone can read them. Never put a password or anything secret in a JWT's payload.

The claims that decide validity

Claim Name Check
exp expiration time the current time must be before it
nbf not before the current time must be at or after it
iat issued at informational; some services reject tokens issued too long ago
iss issuer must equal the issuer you trust, such as your auth server's URL
aud audience must include your service's identifier
sub subject the user or client the token is about

exp, nbf and iat are NumericDate values: seconds since 1 January 1970 UTC, not milliseconds. In the example, exp 1791216000 is 16:00 UTC on 5 October 2026, one hour after iat. Servers usually allow a minute or two of clock skew on these checks.

How to decode and check a JWT

  1. Copy the whole token, all three parts. In a browser's developer tools it is usually in the Authorization: Bearer ... request header, or in local storage or a cookie.
  2. Paste it into the JWT Decoder. The header and payload appear as JSON, each claim with a plain-English line, and the expiry as a date with a valid or expired badge.
  3. Read alg in the header. That tells you what you need to verify it: a shared secret for HS256, HS384 or HS512; the issuer's public key for RS256 or ES256.
  4. Verify the signature: paste the secret, or the public key as PEM or JWK. A token that fails here must be rejected, whatever its claims say.
  5. Check the claims against what your service expects: not expired, nbf passed, iss and aud correct.

A worked example

Take the token above and suppose it is 15:30 UTC on 5 October 2026.

  • Header: HS256, so verification needs the shared secret.
  • Signature: with the secret example-secret, it verifies. With any other secret, it fails.
  • exp: 16:00 UTC, which is 30 minutes away, so the token is still in date.
  • iss and aud: absent. A service that requires them must reject this token even though the signature is good.

At 16:00:01 the same token is expired, and a correct server rejects it even though nothing about the token has changed.

Why decoding is not validating

Because the payload is only encoded, anyone can write a token that decodes to {"sub":"admin"}. What they cannot do without the key is make the signature match. Code that reads claims from a token without verifying the signature trusts whatever the user sends. Three well-known mistakes follow from that:

  • Trusting alg: none. An unsigned token says "alg":"none" and has an empty signature. Libraries must refuse it unless explicitly configured otherwise.
  • Letting the token choose the algorithm. If a server verifies with whatever alg the header names, an attacker can switch an RS256 token to HS256 and sign it with the public key as an HMAC secret. Fix the expected algorithm on the server.
  • Skipping aud. A token issued for one service can be replayed to another that trusts the same issuer. Check the audience.

These and other pitfalls are set out in RFC 8725, the IETF's best current practice for JWTs.

If it doesn't work

  • "Invalid token" or nothing decodes. The token was cut short or has extra characters. Copy it again; it must have exactly two dots and no spaces or quotes. Drop the Bearer prefix.
  • The signature fails with the right secret. Check for a trailing space or newline in the secret, and whether the issuer expects it Base64-decoded first. For RS256 or ES256, you need the public key, not the private key or a certificate fingerprint.
  • It shows expired but was just issued. The clock on the server or your machine is wrong, or a value in milliseconds was written into exp. A 13-digit exp is a bug in the issuer.
  • The payload is unreadable binary. That is a JWE, an encrypted token with five parts rather than three. It can only be read with the recipient's decryption key.
  • The API still rejects a valid-looking token. Check aud and iss, the scopes or roles claim, and whether the token was revoked; a JWT cannot show revocation by itself.

Questions people ask

How do I check if a JWT is valid?

Verify its signature with the issuer's secret or public key, then check the claims: exp must be in the future, nbf in the past, and iss and aud must match what your service expects. Only a token that passes all of these is valid. Decoding it, which anyone can do, shows its contents but proves nothing about whether to trust it.

Can a JWT be decoded without the secret key?

Yes. The header and payload are Base64URL-encoded JSON, not encrypted, so anyone holding the token can read them without any key. The key is needed only to verify the signature or to create a new token. For that reason a JWT payload should never contain a password, a card number or anything else that must stay secret.

What does exp mean in a JWT?

exp is the expiration time: the moment after which the token must not be accepted. Its value is a NumericDate, the number of seconds since 1 January 1970 UTC, so 1791216000 means 16:00 UTC on 5 October 2026. Servers usually allow a minute or two of leeway for clocks that disagree slightly.

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

Only into one that runs in your browser. A live access token works like a temporary password until it expires, so a site that uploads it could use it. The JWT decoder here decodes and verifies with your browser's WebCrypto, and neither the token nor the secret is sent anywhere or stored. For production keys, verify on your own server.

What is the difference between HS256 and RS256?

HS256 signs and verifies with one shared secret, so every service that checks the token can also create tokens. RS256 signs with a private key that only the issuer holds and verifies with a public key that anyone may have. RS256 or ES256 suit tokens checked by many services; HS256 suits a single service that issues and checks its own.

Sources

Checked 5 Oct 2026:

The tools for this