A JWT (JSON Web Token) is a compact, URL-safe string of three base64url parts separated by dots: a header naming the algorithm, a payload of claims such as sub and exp, and a signature. Anyone can decode and read the payload; only the signature, checked with the right key, proves nobody changed it.
| Claim | Name | Meaning |
|---|---|---|
| iss | Issuer | Who issued the token |
| sub | Subject | Who the token is about, usually a user id |
| aud | Audience | Which recipients should accept it |
| exp | Expiration time | Reject on or after this time |
| nbf | Not before | Reject before this time |
| iat | Issued at | When the token was created |
| jti | JWT ID | A unique id, useful for blocking reuse |
Updated
Paste a token into the decoder below to see its header and payload, with each registered claim explained and every timestamp turned into a readable date. Decoding happens in this page's own JavaScript, so the token is never uploaded. That matters, because a live token is a key to someone's account.
Paste a token to read its header and payload. Decoding runs in your browser; the token is not sent, stored or logged.
Reads the claims without verifying them. Fine for showing a username in the UI, never for deciding access.
function decodeJwtPart(part) {
const b64 = part.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(part.length / 4) * 4, "=");
const bytes = Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
return JSON.parse(new TextDecoder().decode(bytes));
}
const [header, payload] = token.split(".").slice(0, 2).map(decodeJwtPart);
const expired = payload.exp * 1000 < Date.now();jwtVerify checks the signature, exp and nbf, and with these options also the issuer, audience and the allowed algorithm.
import { jwtVerify } from "jose";
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
const { payload } = await jwtVerify(token, secret, {
algorithms: ["HS256"],
issuer: "https://auth.example.com",
audience: "my-api",
});
console.log(payload.sub);
The header is a small JSON object such as {"alg":"HS256","typ":"JWT"}. The payload holds the claims. Each is base64url encoded, which is base64 with - and _ instead of + and / and no = padding, so the result is safe in URLs and headers.
The signature is computed over the first two encoded parts joined by a dot. Change one character of the payload and the signature no longer matches.
HS256 uses one shared secret both to sign and to verify, so every service that checks tokens can also mint them. RS256 and ES256 sign with a private key and verify with a public key, which suits setups where many services verify but only one issues.
Unsecured tokens with alg none carry no signature at all. A library that trusts the header can be tricked into accepting one, or into treating a public RSA key as an HMAC secret. RFC 8725 tells libraries to let the caller fix the allowed algorithms, and you should.
Only if decoding happens in your browser. The token is a credential: anyone holding a valid one can call the API as that user until it expires. This page's decoder runs locally and sends nothing, but for production tokens the safest habit is to decode test tokens only.
A normal signed JWT (a JWS) is not. Its payload is only base64url encoded, which anyone can reverse. Encrypted tokens exist as JWE, with five dot-separated parts instead of three, but most login and API tokens are signed, not encrypted.
NumericDate: whole seconds since 1970-01-01T00:00:00Z, ignoring leap seconds. JavaScript's Date.now() returns milliseconds, so divide by 1000 before comparing, a very common bug.
Any script running on the page can read localStorage, so one cross-site scripting bug can steal the token. An HttpOnly, Secure, SameSite cookie keeps it away from scripts, at the cost of handling CSRF. Many teams choose the cookie for browser sessions.
Not by itself, because the token stays valid until exp. Use short expiry plus refresh tokens you can revoke, or keep a deny list of jti values for tokens that must die early.
Related: JWT generator, OAuth explained, Website authentication guide, JSON formatter, Next.js authentication.
Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.
We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.