Skip to content

JWT Decoder

Paste a token to read its header, payload and claims, with expiry and issue times shown as UTC dates. The token is decoded in your browser and is not sent anywhere.

Runs in your browserFree, no sign-upLast reviewed

Settings

Results update automatically as you type.

Fill in the settings to see your result here.

Your result appears here instantly — nothing is uploaded.

Quick answers

How do I decode a JWT?
Split the token at the dots, take the first two parts, Base64URL-decode each and parse the results as JSON. The first is the header and the second is the payload. Paste a token above to have that done for you, with time claims turned into readable UTC dates.
Can you decode a JWT without the secret key?
Yes. The header and payload of a signed JWT are only Base64URL-encoded, so anyone holding the token can read them. The secret or public key is needed to verify the signature, not to read the contents.
How do I check when a JWT expires?
Read the exp claim in the payload. It is a Unix timestamp in seconds, so 1791455400 means 2026-10-08 10:30:00 UTC. The Claims panel shows it as Expires. If that moment is in the past, a server that validates tokens correctly will reject it.
Does decoding a JWT verify it?
No. Decoding only reads the token. Verifying means recomputing the signature with the secret or public key and checking exp, iss and aud, and that belongs on your server. A token with a forged payload decodes exactly as cleanly as a real one.
Is a JWT encrypted?
A normal signed JWT (a JWS) is not. Its payload is readable by anyone, so it must never carry passwords or other secrets. Encrypted tokens exist as JWE, which have five dot-separated parts instead of three and cannot be read without the decryption key.
What are the three parts of a JWT?
Header, payload and signature, joined by dots. The header is JSON naming the signing algorithm; the payload is JSON holding the claims; the signature is computed over the encoded header, a dot and the encoded payload.

Why use JWT Decoder

This JWT decoder splits a JSON Web Token at its dots, Base64URL-decodes the first two parts and shows the header and payload as formatted JSON. A Claims panel pulls out the algorithm, subject, issuer and audience, and converts the iat, nbf and exp timestamps from Unix seconds into UTC date and time, so exp 1700000000 reads as 2023-11-14 22:13:20 UTC. No secret or key is needed, because the header and payload of a signed token are encoded, not encrypted. The signature is not verified, so a decoded token tells you what it claims, not whether it is genuine.

Decoded JWT examples

The first two are complete test tokens; paste either into the box above to reproduce the result. The third describes the decoded contents of a typical access token.

The widely used HS256 sample token

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Header {"alg":"HS256","typ":"JWT"}. Payload {"sub":"1234567890","name":"John Doe","iat":1516239022}. Claims: Algorithm HS256, Subject 1234567890, Issued at 2018-01-18 01:30:22 UTC.

There is no exp claim, so no Expires line appears. A token without exp never expires on its own.

A token with an expiry one hour after issue

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTcwMDAwMDAwMCwiaWF0IjoxNjk5OTk2NDAwfQ.c2lnbmF0dXJl

Header {"alg":"HS256","typ":"JWT"}. Payload {"sub":"42","exp":1700000000,"iat":1699996400}. Claims: Algorithm HS256, Subject 42, Issued at 2023-11-14 21:13:20 UTC, Expires 2023-11-14 22:13:20 UTC.

exp minus iat is 3600 seconds, a one-hour lifetime. The signature part of this token is a placeholder, and it still decodes, which shows that decoding proves nothing about authenticity.

An access token from an identity provider

Header {"alg":"RS256","typ":"JWT","kid":"2026-key-1"} and a payload with iss https://auth.example.com, sub user_8421, aud https://api.example.com, iat and nbf 1791451800, exp 1791455400, scope "read:orders write:orders"

Claims: Algorithm RS256, Subject user_8421, Issuer https://auth.example.com, Audience https://api.example.com, Issued at 2026-10-08 09:30:00 UTC, Not before 2026-10-08 09:30:00 UTC, Expires 2026-10-08 10:30:00 UTC.

scope and kid are not in the Claims panel; read them in the Payload and Header JSON.

JWT registered claims and what they mean

The seven claims registered in RFC 7519, section 4.1. All are optional in the specification; the issuer decides which to include.

ClaimNameValueShown in the Claims panel as
issIssuerString or URL identifying who created the tokenIssuer
subSubjectWho the token is about, usually a user or client IDSubject
audAudienceThe recipient the token is meant for; a string or an array of stringsAudience (array items joined by commas)
expExpiration timeUnix time in seconds on or after which the token must be rejectedExpires, as a UTC date and time
nbfNot beforeUnix time in seconds before which the token must be rejectedNot before, as a UTC date and time
iatIssued atUnix time in seconds when the token was createdIssued at, as a UTC date and time
jtiJWT IDUnique identifier for the token, used to detect replaysNot listed; read it in the Payload

Other claims you will often see are not part of this list: scope, azp, nonce, name, email, roles and anything else the issuer adds. They appear in the Payload JSON exactly as issued.

Reading exp and iat: token lifetimes in seconds

Subtract iat from exp to get the lifetime the issuer chose. Common values:

exp minus iat (seconds)LifetimeTypical use
3005 minutesShort-lived access tokens, one-time links
90015 minutesAccess tokens paired with a refresh token
36001 hourA common default lifetime for access tokens
288008 hoursA working-day session
8640024 hoursDaily sign-in
6048007 daysRemember-me sessions
259200030 daysLong-lived refresh tokens

Time claims are in seconds, not milliseconds. A 13-digit exp such as 1791455400000 was produced with a millisecond clock by mistake; the decoder then shows a garbled Expires value beginning +058738, a year tens of thousands of years in the future. A time claim written as a string, such as "1791451800", is not converted at all.

What the alg value in the header means

algAlgorithmKey used to verify
HS256HMAC with SHA-256The same shared secret that signed the token
HS384 / HS512HMAC with SHA-384 or SHA-512Shared secret
RS256RSA PKCS#1 v1.5 signature with SHA-256The issuer's RSA public key
PS256RSA-PSS signature with SHA-256The issuer's RSA public key
ES256ECDSA on the P-256 curve with SHA-256The issuer's elliptic-curve public key
EdDSAEdwards-curve signature, usually Ed25519The issuer's Ed25519 public key
noneNo signature at allNothing to verify; servers should refuse these

The header often also carries typ, usually JWT, and kid, the ID of the key that signed the token, which a server uses to pick the right public key from the issuer's key set. A server must decide which algorithms it accepts and never take that decision from the token's own header.

Why a token will not decode

What you pastedMessageFix
Bearer eyJhbGci... (the whole Authorization header value)The header is not valid Base64URL-encoded JSON.Delete Bearer and the space so the text starts with eyJ
Text with no dots: a cut-off token, or an opaque access token or API keyA JWT has three dot-separated parts: header.payload.signature.Copy the full token; if it never had dots it is not a JWT and only its issuer can say what it represents
A five-part encrypted token (JWE)The payload is not valid Base64URL-encoded JSON.It cannot be read without the decryption key; decrypt it where the key lives
Dotted text that is not a token, such as abc.def.ghiThe header is not valid Base64URL-encoded JSON.Check you copied the token and not a hostname, version string or file name
A token truncated in the middle of the payloadThe payload is not valid Base64URL-encoded JSON.Copy it again from the source, checking for a trailing ellipsis

A token with only header and payload and no signature part is still decoded, as is one whose alg is none.

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

A JWT used as a bearer token is a credential: whoever holds it can act as its subject until it expires. This page decodes the token with JavaScript in your browser and makes no request containing it, so it does not leave your device. That is the property to look for in any decoder you use.

Even so, a sensible habit is to decode expired tokens or tokens from a test environment where you can, and to treat a live production token that has been pasted into any tool, shared in a chat or written to a log as one to revoke.

Never paste a signing secret or private key into a web page. This decoder has no field for one, and that is deliberate: verification belongs in your server or in a local script using a maintained JWT library, where the key never leaves your control.

How to get your result

  1. 1

    Paste the token into JWT. Remove the word Bearer and the space after it if you copied an Authorization header; line breaks inside the token are fine.

  2. 2

    Read the Claims panel for Algorithm, Subject, Issuer, Audience, Issued at, Not before and Expires. Only claims present in the token are listed.

  3. 3

    Compare Expires with the current UTC time to see whether the token has lapsed. The decoder shows the time but does not label the token expired or valid.

  4. 4

    Scroll to Header and Payload for the full JSON, including custom claims such as scope or roles, and copy either block.

Common questions

Does this decoder tell me whether the token is expired?
It shows the expiry as a UTC date and time but does not compare it with the clock or print an expired or valid label. Check the Expires line against the current UTC time. UTC matches your local time only if your time zone has no offset from it.
Why are the times shown in UTC?
NumericDate values in a JWT count seconds from 1970-01-01 00:00:00 UTC and carry no time zone, so UTC is the unambiguous way to print them. Add or subtract your offset to get local time: 10:30:00 UTC is 06:30 in New York during daylight time.
What is Base64URL?
A variant of Base64 that replaces + with - and / with _ and drops the = padding, so the result can sit in a URL or HTTP header without escaping. It is an encoding, not encryption, and reversing it needs no key.
Why do JWTs start with eyJ?
Because the header is a JSON object whose first key starts with a letter, so it begins with {"a (as in alg) or similar, and Base64-encoding those opening bytes yields eyJ. The payload usually starts with eyJ too for the same reason.
What is the difference between an ID token and an access token?
An ID token, defined by OpenID Connect, is always a JWT and tells the client application who signed in; its aud is the client ID. An access token is presented to an API and may be a JWT or an opaque string. Check aud to see which API or client a token was issued for.
Can I edit the payload and use the token again?
You can change the payload and re-encode it, but the signature will no longer match, so any server that verifies signatures rejects it. That is the whole purpose of the third part. This tool only reads tokens; it does not edit or sign them.
Why did I get a 401 when the token decodes fine?
Decoding only shows what the token says. Common causes of a 401 are an Expires time in the past, an aud that names a different API, an iss the server does not trust, a Not before time still in the future on a server whose clock is behind, or a missing scope.
How do I decode a JWT in code?
In JavaScript, split the token on dots, replace - with + and _ with / in the second part, pass it to atob and then JSON.parse. That is fine for reading claims in a browser. On a server, use a JWT library's verify function so the signature and expiry are checked in the same step.

Looking for something else? Browse all developer tools or see every tool.