JWT Decoder — Free Online Tool on Toolpile

Pricing

Every tool you need, one place

PDF, Image, AI, Dev, and Business tools — free, private, no signup.

PDF

20

Image & Media

17

AI & Prompts

21

Text

13

Developer

26

Converters

6

SEO & Marketing

8

Generators

12

Productivity

7

Calculators

4

Business

9

Privacy & Security

5

Design

4

Content

5

Education

4

About JWT Decoder

A JWT (JSON Web Token) looks like an opaque blob until you see what's actually inside — three Base64URL chunks, separated by dots, holding a header, a payload, and a signature. Decoding reveals the contents in plaintext (yes, anyone can read them). Verifying takes a key. This tool decodes; what follows is when each part matters and what people most often get wrong about token security.

What's actually in a JWT

A JWT is three Base64URL-encoded JSON objects joined by `.` characters: `header.payload.signature`. Decode each part separately. The **header** says how the token is signed (`alg: HS256` for HMAC-SHA256, `RS256` for RSA, `ES256` for ECDSA, etc.) and what type it is (`typ: JWT`). The **payload** holds the actual data — the `claims` — which can be anything but conventionally include `sub` (subject, usually a user ID), `iat` (issued at, Unix timestamp), `exp` (expires at, Unix timestamp), `iss` (issuer), `aud` (audience). The **signature** is computed over the first two parts using the secret/private key — verifying it proves the token wasn't tampered with.

The single most-misunderstood thing about JWTs: **the payload is not encrypted**. Anyone can Base64-decode it. Putting passwords, credit cards, or anything sensitive in a JWT payload is a leak — assume every party that touches the token can read everything inside. The signature only proves the token came from someone with the secret key; it doesn't hide the contents. If you need encryption, use JWE (JSON Web Encryption) which is a different format, or encrypt sensitive fields before placing them in the JWT.

JWTs are designed for **short-lived authentication**: the issuer signs a token saying "this user is authenticated until time X", the receiver verifies the signature without contacting the issuer, the user re-authenticates after X. Long-lived JWTs (multi-day, multi-month) defeat the design — once issued, you can't revoke a JWT short of maintaining a server-side blocklist (which gives back the round-trip you saved by using JWT). The 2026 norm: 5-15 minute access tokens + a separate revocable refresh token in a server-side store.

Standard claims and what each one does

RFC 7519 defines seven "registered claims" that have specific meanings — most JWT libraries enforce them automatically:

  • `iss` (issuer) — who created the token. Usually a URL like `https://auth.example.com`. Verify on receive: only accept tokens from issuers you trust.
  • `sub` (subject) — who the token is about. Usually a user ID or service account.
  • `aud` (audience) — who the token is for. Receivers should verify their own identifier appears here; rejecting tokens issued for other audiences prevents token reuse across services.
  • `exp` (expiration time) — Unix timestamp after which the token is invalid. Required in practice; libraries reject expired tokens automatically.
  • `nbf` (not before) — Unix timestamp before which the token is invalid. Useful for tokens that should activate later (rare).
  • `iat` (issued at) — Unix timestamp of issuance. Useful for logging and for short-lived-token policies ("reject anything older than 1 hour even if exp is later").
  • `jti` (JWT ID) — unique identifier for the token, used for one-time tokens and replay prevention.
Common JWT vulnerabilities — the alg=none disaster

The most famous JWT vulnerability is **alg=none**. The original spec allowed `alg: none` (no signature) for some use cases. Many JWT libraries in 2015-2017 verified the signature using whatever algorithm the token claimed in its own header — so an attacker could change `alg` from `RS256` to `none`, blank out the signature, and the library would accept the token as valid. Fixed in modern libraries by requiring the verifier to specify accepted algorithms upfront, but old code (and some custom implementations) still has this bug.

**alg=HS256 with the public key as the secret**: another classic. If the verifier trusts the `alg` field, an attacker can change a token from `RS256` (asymmetric, server has private key) to `HS256` (symmetric, expects a shared secret) and sign the new token using the issuer's public key as the HMAC secret. The verifier, accepting `alg` from the header, validates with the public key — and it passes. Fix: never let the `alg` from the token header decide which key to use; the verifier must hard-code or look up the algorithm separately.

**Hardcoded weak secrets**: developers picking `secret`, `password`, or short strings as the HMAC key. Token cracking tools like `jwt-cracker` and `hashcat` can brute-force common HMAC secrets in minutes if exposed tokens are available. For HS256, use ≥32 bytes of random data — produced by a CSPRNG, not typed by hand.

**Storing tokens insecurely client-side**: putting JWTs in localStorage means any XSS vulnerability leaks every user's session. Putting them in `httpOnly` cookies means JS can't read them but also can't easily attach them to fetch calls without extra setup. The 2026 best practice is a combination — short-lived access token in memory (never persisted), refresh token in `httpOnly` SameSite=Strict cookie, refresh on demand.

How to use this decoder
  1. Paste your JWT into the input. Format: `eyJ...header....eyJ...payload....signature`.
  2. Header and payload decode automatically. The signature is shown as Base64URL — verifying it requires the secret/key, which this tool deliberately does not handle (verification belongs in your application, not a third-party site, even a local one).
  3. Inspect the payload: claim names (sub, iss, exp), values, expiration time decoded as a human-readable date. If the token is expired, this is where you'll see it.
  4. Spot common issues: missing `exp` (token never expires — bad for refresh tokens, very bad for access tokens), `alg: none` (don't accept this token anywhere), unfamiliar `iss` (token from an unexpected issuer).
  5. Everything runs in your browser. The JWT is not transmitted, logged, or stored — even though it's base64-decodable by anyone, treating tokens as sensitive while debugging is the right habit. Verify in DevTools Network tab — only the page itself loads.
FAQ

Is decoding a JWT the same as verifying it?

No — and conflating them is dangerous. Decoding is reversible Base64URL — anyone can see the contents. Verification requires the signing key (HMAC secret for HS256, public key for RS256/ES256) and proves the token came from someone with the matching key. A decoded-but-not-verified JWT is just JSON someone sent you; trust nothing in it until verified.

Are JWTs encrypted?

JWTs as commonly used (JWS — JSON Web Signature) are signed but not encrypted. The payload is plaintext (Base64-encoded). For encryption, the spec defines JWE (JSON Web Encryption), which is a different format — it has 5 dot-separated parts instead of 3 and the payload is opaque ciphertext. Most production systems use JWS over HTTPS for confidentiality in transit and never persist tokens unencrypted.

Where should I store JWTs in a browser?

For SPAs in 2026: access tokens in memory (a JS variable, not localStorage — XSS risk). Refresh tokens in `httpOnly`, `SameSite=Strict`, `Secure` cookies — JS can't read them, the browser sends them automatically on same-site requests, no XSS leak. The flow: app loads → calls /refresh → gets new access token → uses it → repeats every 15 min. Avoid putting JWTs in localStorage even though it's the path-of-least-resistance — every XSS vuln becomes a session hijack.

How long should a JWT live?

Access tokens: 5-15 minutes is the modern norm. Long enough that you don't refresh on every request; short enough that a stolen token has limited damage. Refresh tokens: hours to days, but stored server-side as well so they can be revoked when a user signs out or you detect compromise. Never issue a JWT without `exp` — even week-long tokens are safer than non-expiring ones.

Why does my JWT look like garbage when I just see eyJ...?

It is garbage to read directly — Base64URL-encoded JSON. The `eyJ` prefix is the Base64URL of `{"`, which is how every JWT starts (a JSON object opens with `{`). To read the contents, paste into this decoder, or decode each dot-separated part with a Base64 tool. Don't be impressed by the opaque look — JWTs aren't meant to be human-readable in their wire form, only programmatically parseable.

Related tools

Related tools

See all developer tools →
Looking for something else? Browse all 155 tools.