Skip to content
so-yi tools

JWT Decoder

Decode JWT headers and payloads and read expiry claims in human-readable form.

Signatures are not verified. Verifying requires your signing key, and prompting users to paste keys into a web tool is a bad practice. Decoding successfully does not mean the token is valid. Decoding happens entirely in your browser. Even so, avoid pasting live production tokens into any online tool.

What a JWT looks like

A JSON Web Token has three parts separated by dots:

eyJhbGciOiJIUzI1NiJ9 . eyJzdWIiOiIxMjM0In0 . 4pcPyMD09olPSyXnrXCjTw
      header                 payload              signature

The first two parts are JSON encoded with URL-safe Base64. That is encoding, not encryption — anyone can read them.

The header carries the signing algorithm (alg) and token type (typ).

The payload carries claims: user ID, permissions, expiry, and anything else the service needs.

The signature is the first two parts signed with a key. It proves the contents were not tampered with.

Registered claims

Defined by RFC 7519:

| Claim | Meaning | |---|---| | iss | Issuer | | sub | Subject — usually the user ID | | aud | Audience — who the token is for | | exp | Expiration time | | nbf | Not valid before this time | | iat | Issued at | | jti | Unique token ID |

Beyond these you can add whatever your service needs — name, role, tenant ID.

exp is in seconds, not milliseconds

This is the single most common bug. exp, iat, and nbf are all UNIX timestamps in seconds since 1970-01-01 UTC.

JavaScript's Date.now() returns milliseconds. Passing it straight through makes the value 1000× too large, producing dates in the year 50,000.

Math.floor(Date.now() / 1000)   ← correct
Date.now()                       ← 1000x off

This decoder flags values that look like milliseconds.

Why no signature verification?

Verifying a signature requires the secret key (HS256) or public key (RS256).

Prompting people to paste a signing key into a web tool is a genuinely bad practice — that one key lets anyone forge valid tokens. So this tool decodes only.

Remember: a token that decodes is not necessarily valid. The header and payload read fine even when the signature is complete nonsense. Real verification belongs on your server.

Never put secrets in a JWT

The payload is not encrypted. Reverse the Base64 and anyone reads it.

So never include:

  • Passwords
  • National ID or card numbers
  • Detailed internal system information

The signature prevents forgery; it does not provide confidentiality. If the contents must stay hidden, you need JWE (encrypted JWT).

The alg: none attack

A serious vulnerability that affected many JWT libraries. Set the header's alg to none, strip the signature, and some implementations would skip verification and accept the token.

An attacker simply edits the payload's user ID to an administrator and walks in.

The defense is to pin the algorithm your server expects. Never trust the alg the token itself declares.

When a token expires

Servers reject tokens past exp. The usual flow is to obtain a new access token using a refresh token.

Access tokens are typically short-lived (15 minutes to an hour) while refresh tokens last weeks, so that a leaked access token has a limited blast radius.

One caveat: JWTs are hard to revoke immediately, because the server holds no state. If you need forced logout, keep a blocklist or keep token lifetimes short.

Related tools

To work with Base64 directly, see the Base64 encoder and decoder. To tidy up the payload JSON, use the JSON formatter.

Frequently asked questions

Last updated August 27, 2026