Privatool
Guide5 min read

JWT Decoder — How to Decode and Inspect JSON Web Tokens

Learn what JWT tokens are, how they work, and how to decode them to inspect headers, payloads, and expiry times. Free online JWT decoder included.

By Privatool Team·

What is a JWT token?

A JSON Web Token (JWT) is a compact, URL-safe way to represent claims (information) between two parties. JWTs are widely used for authentication — when you log in to a web application, the server often returns a JWT that your browser stores and sends with every request to prove you're authenticated.

JWT structure

A JWT consists of 3 parts separated by dots:

eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2VyMTIzIn0.signature
     ^HEADER^               ^PAYLOAD^           ^SIGNATURE^

Each part is Base64URL encoded.

Header

Contains the algorithm and token type:

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

Payload

Contains the claims (data):

{
  "sub": "user123",
  "email": "user@example.com",
  "iat": 1712000000,
  "exp": 1712086400
}

Signature

Verifies the token hasn't been tampered with. Cannot be verified without the secret key.

Common JWT claims explained

Claim Full name Meaning
sub Subject User ID or identifier
iat Issued At When the token was created (Unix timestamp)
exp Expiration When the token expires (Unix timestamp)
nbf Not Before Token not valid before this time
iss Issuer Who created the token
aud Audience Who the token is intended for

JWT security warnings

  • Never paste production JWTs into any online tool — JWT tokens grant access to accounts
  • JWTs are encoded, not encrypted — anyone can decode the payload
  • The signature cannot be verified without the secret key
  • Expired tokens should always be rejected by the server

Why Base64URL, not plain Base64?

JWTs travel in URLs, query strings, and HTTP headers, where standard Base64 breaks. Base64URL swaps the two problem characters and drops the padding:

Standard Base64 Base64URL Why
+ - + means a space in query strings
/ _ / is a path separator
= padding removed = separates key/value pairs

This is why a JWT decodes correctly in a JWT tool but produces garbage in a generic Base64 decoder that does not handle the URL-safe alphabet.

Decoding by hand

Each segment is just Base64URL. In a terminal:

# payload is the 2nd dot-separated segment.
# Base64 requires a length divisible by 4, and JWTs strip the padding — so
# re-add it, or `base64 -d` fails silently and prints nothing.
p='eyJzdWIiOiJ1c2VyMTIzIn0'
printf '%s' "$p$(printf '=%.0s' $(seq $(( (4 - ${#p} % 4) % 4 ))))" | base64 -d

That padding arithmetic is exactly the fiddly step a decoder handles for you. More importantly, note what is not in the command: no secret, no key, no verification. Anyone holding the token can read the payload.

The alg: none attack

Early JWT libraries honored a header of {"alg": "none"} and skipped signature verification entirely. An attacker could take a valid token, change "role": "user" to "role": "admin", set alg to none, strip the signature, and be trusted.

Modern libraries reject this by default, but the lesson generalizes: the server must decide which algorithm to accept, never the token. A server that reads alg from the header and trusts it is vulnerable to this and to RS256→HS256 confusion, where an attacker signs a token using the public key as an HMAC secret.

Reading exp and iat

Both are Unix timestamps — seconds since 1970-01-01 UTC, not milliseconds. A frequent bug is comparing them against JavaScript's Date.now(), which returns milliseconds, making every token look ~50,000 years expired:

// wrong
if (payload.exp < Date.now()) { /* always true */ }

// right
if (payload.exp < Math.floor(Date.now() / 1000)) { /* expired */ }

Frequently asked questions

Can I decode a JWT without the secret key?

Yes, completely. The header and payload are only encoded, not encrypted. The secret is required to verify the signature — to prove the token was not altered — but never to read it.

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

Only if the decoding happens in your browser. A JWT is a live credential; pasting one into a server-side tool hands over account access for as long as the token is valid. This decoder runs entirely client-side and never transmits the token. For a production token, the safest option is still to decode it locally.

Can I edit a JWT payload?

You can change it, but the signature will no longer match and any correctly configured server will reject it. That is precisely what the signature exists to prevent.

What is the difference between a JWT and a session cookie?

A session cookie is an opaque ID; the server looks up the real data in its own store. A JWT carries the data inside itself, so the server needs no lookup — which makes it fast to scale but hard to revoke. A stolen JWT stays valid until it expires.

Why is my token so large?

Every claim is stored inside the token, so payloads grow quickly. Since the token is sent on every request, large JWTs add measurable overhead. Keep claims minimal and rely on short expiry plus a refresh token.

What does "token expired" mean if the time looks correct?

Check for clock skew between the issuing and validating servers. A few seconds of drift can reject freshly issued tokens. Most libraries expose a leeway or clockTolerance setting for this.

How to decode JWT for free

  1. Go to JWT Decoder
  2. Paste your JWT token
  3. View decoded header and payload instantly
  4. Check expiry time in human-readable format
  5. Your token is never sent to any server

Decode a JWT free →

Share this article
#jwt decoder#json web token#jwt explained#decode jwt#jwt security

Try our free tools

All tools run in your browser. Files never leave your device.

Explore free tools →