Skip to content
Spellkit

JWT Decoder

Decode a JWT into header, payload and claims — check expiry. In your browser.

Decoded locally — your token never leaves this browser.

What is JWT Decoder?

JWT Decoder splits a JSON Web Token into its header, payload, and claims so you can see exactly what a token carries. Time claims like exp, iat, and nbf are shown as readable local dates, with a badge telling you whether the token is valid, expired, or not yet valid. Decode a JWT online for free — the token never leaves your browser.

Key features

  • Decodes the header and payload to formatted JSON
  • Claims table with exp, iat, and nbf as local dates
  • Valid, expired, or not-yet-valid badge at a glance
  • Copy buttons for header and payload JSON
  • Sample token to try it instantly

JWT Decoder — read a token's header, payload & claims

A JSON Web Token is three chunks of base64url text joined by dots — header.payload.signature. It looks encrypted, but the first two parts are only encoded, not encrypted: anyone can read them. This tool splits a token and decodes the header and payload so you can see exactly what it carries. Everything happens in your browser; the token is never sent anywhere.

How to use it

  1. Paste a token (it starts with eyJ, which is {" in base64url), or click Load sample to try a standard HS256 example.
  2. Read the Header — the signing algorithm (alg, e.g. HS256, RS256) and type — and the Payload, the JSON of claims. Each panel has a copy button.
  3. Scan the Claims table. Every claim is listed with its raw value, and the time claims are also rendered as readable local dates.

The status badge

At the top you get a valid / expired / not-yet-valid badge, worked out from two registered claims:

  • exp (expiration) — if the current time is at or past it, the token is expired.
  • nbf (not before) — if the current time is earlier than it, the token is not yet valid.

If a token carries neither, there's nothing to judge and no badge is shown. Note there's no clock-skew grace period: a token is marked expired the instant the current second reaches exp, which is stricter than servers that allow a few seconds of leeway.

Time claims are seconds, not milliseconds

The tool reads exp, iat, nbf, and auth_time as NumericDate values — seconds since the Unix epoch — and multiplies by 1000 to build the date, exactly as the JWT spec requires. If a token was built incorrectly with millisecond timestamps, the decoded date lands thousands of years in the future — a quick tell that whoever minted it used Date.now() instead of Date.now() / 1000.

What it deliberately does not do

This tool decodes only — it does not verify the signature. Verification proves a token wasn't tampered with, and it needs the signing secret (for HS256) or the public key (for RS256/ES256). You should never paste a signing secret into a web page, so that step belongs on your backend, not here. Reading the payload tells you what a token claims; it says nothing about whether that claim is trustworthy.

A security reminder

Because the payload is readable by anyone who has the token, never put secrets in it — passwords, API keys, private data. Base64url is encoding, not encryption; treat everything in a JWT payload as public. If you can decode sensitive fields here in one paste, so can anyone who intercepts the token.

Troubleshooting

A token must be exactly three dot-separated parts. A truncated copy-paste (missing the signature, or with a stray line break) fails the structure check. If the header or payload isn't valid JSON after decoding, that segment is flagged rather than showing garbage.

Frequently asked questions

Does this verify the token's signature?
No, it decodes the header and payload only. Verifying the signature needs the HS256 secret or the RS256/ES256 public key, which you should never paste into a website — do that on your backend. Decoding shows what a token claims, not whether it's authentic.
Is it safe to paste a real token here?
The decoding is pure local base64url in your browser, and the token is never uploaded. That said, a JWT payload is readable by anyone who holds the token anyway — base64 is encoding, not encryption — so you should never store secrets in a payload in the first place.
Why does the expiry show a year thousands of years away?
The exp, iat, and nbf claims must be in seconds since the epoch (NumericDate). If the token was minted with millisecond timestamps by mistake, the tool multiplies by 1000 and the date lands far in the future — a sign the issuer used Date.now() instead of Date.now()/1000.
It shows "expired" but my server still accepts the token.
The badge marks a token expired the moment the clock reaches exp, with no grace period. Many servers allow a few seconds of clock-skew leeway, so a token can read as expired here yet still pass validation server-side.
I pasted a token and got a structure error.
A JWT must be exactly three parts separated by dots — header.payload.signature. A truncated copy missing the signature, or one with a stray line break, fails that check. Re-copy the whole token, including the third segment.

Privacy

Decoding the token's header and payload is plain local Base64 decoding — the token you paste, which may contain sensitive claims, is never sent to a server.