JWT Decoder
Paste a JSON Web Token to inspect its header, payload claims, and signature. Decoding runs entirely in your browser — the token is never uploaded.
{
"alg": "HS256",
"typ": "JWT"
}{
"sub": "1234567890",
"name": "Jane Doe",
"iat": 1700000000
}Signature verification requires the secret/public key and is not performed here for security reasons. Use the JWT Validator to check your secret's strength, or the JWT Encoder to re-sign a payload.
How to decode a JWT
- 1. Copy the full token — three dot-separated Base64URL segments (
header.payload.signature). - 2. Paste it into the Encoded box on the left. The header and payload update instantly.
- 3. Read the claims:
iss(issuer),sub(subject),exp(expiry, a Unix timestamp),iat(issued at). - 4. To verify the signature, hand the token and secret to a server library — see the examples below.
Verification examples
import jwt from "jsonwebtoken";
try {
const claims = jwt.verify(token, process.env.JWT_SECRET);
console.log(claims);
} catch (err) {
console.error("Invalid token", err.message);
}import jwt, os
try:
claims = jwt.decode(token, os.environ["JWT_SECRET"], algorithms=["HS256"])
print(claims)
except jwt.InvalidTokenError as e:
print("Invalid token:", e)import "github.com/golang-jwt/jwt/v5"
token, err := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {
return []byte(os.Getenv("JWT_SECRET")), nil
})
if err != nil || !token.Valid {
log.Fatal("invalid token:", err)
}
claims := token.Claims.(jwt.MapClaims)
fmt.Println(claims["sub"])Related tools
Need a token to practice on? Generate one with the JWT secret generator, then sign a custom payload in the JWT encoder before decoding it here. If a decoded token looks fine but your API still rejects it, the problem is usually the signing key — check its strength with the JWT secret validator. For multiple algorithm/format variants, browse the secret generator variants page. New to the format entirely? Start with What is a JWT? for a plain-English introduction to header, payload, and signature. We also cover decoding pitfalls and real-world debugging stories on the blog, and answer more general questions on the site-wide FAQ page. Once you're comfortable decoding tokens, come back and re-sign a modified payload in the encoder to see how a single changed claim produces an entirely different signature.
What is a JWT decoder?
A JWT decoder takes the compact string format of a JSON Web Token — three Base64URL segments joined by dots — and turns the first two segments back into readable JSON. It does not check whether the token is authentic; it simply reverses the encoding step that every JWT library performs when it issues a token. Developers reach for a decoder when debugging an API integration, checking why an access token was rejected, or auditing what claims a third-party service embeds in the tokens it hands out. Because the header and payload of a JWT are only encoded (not encrypted), anyone who has the token can already read its contents — a decoder just makes that readable without writing code.
How JWT decoding works, step by step
- 1. Split on dots. A JWT is
header.payload.signature. Splitting on.must yield exactly three parts for a signed JWT (JWS). - 2. Base64URL-decode the header. Base64URL swaps
+//for-/_and drops padding, so URL and header fields don't need escaping. Decoding it yields JSON like{"alg":"HS256","typ":"JWT"}. - 3. Base64URL-decode the payload. Same transform on the second segment produces the claims object —
sub,iat,exp, and any app-specific fields. - 4. Leave the signature as opaque bytes. The third segment is not decoded into JSON — it is raw HMAC or RSA/ECDSA signature bytes, displayed here as Base64URL text for reference only.
- 5. (Optional) Verify. A real client library then recomputes the signature over
header.payloadusing the expected key and algorithm, and rejects the token if it doesn't match exactly.
Worked example
Take the sample token pre-loaded above:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbmUgRG9lIiwiaWF0IjoxNzAwMDAwMDAwfQ.dummy_signature_value
The first segment decodes to {"alg":"HS256","typ":"JWT"}, telling verifiers to use HMAC-SHA256. The second segment decodes to {"sub":"1234567890","name":"Jane Doe","iat":1700000000}. The iat value 1700000000 is a Unix timestamp — run new Date(1700000000 * 1000) in a JS console and you get November 14, 2023, 22:13:20 UTC. The third segment, dummy_signature_value, isn't a real HMAC output, which is why passing this exact token to jwt.verify() with any secret will throw an "invalid signature" error — decoding succeeds, verification fails.
Common mistakes when decoding JWTs
- Pasting the whole
Authorization: Bearer <token>header instead of just the token, which adds a stray "Bearer " prefix and breaks the three-segment split. - Assuming a decoded payload means the token is valid — an attacker can craft any payload they like; only a signature check proves origin and integrity.
- Trusting the
algfield from the header at face value in custom verification code, which enables algorithm-confusion attacks (e.g. an attacker setsalg: noneor swaps RS256 for HS256 using the public key as an HMAC secret). - Confusing
exp(seconds) with milliseconds — comparing it directly toDate.now()without multiplying by 1000 makes every token look expired. - Decoding a refresh token or opaque access token that was never a JWT in the first place, then getting confused when it isn't valid Base64URL/JSON.
Security notes
Because JWT header and payload are only encoded, never encrypted, treat every claim inside a token as public information visible to the holder of that token and anyone it passes through (proxies, logs, browser extensions). Never put secrets, passwords, or sensitive PII directly in a JWT payload — use an opaque reference (like a user ID) and look up sensitive data server-side instead. When building your own verification logic, always pin the expected algorithm and key server-side rather than reading it from the token's own header, and always check exp/nbf server-side even if a client-side decoder shows the claims — a decoder is a debugging aid, not a security boundary.
JWT Decoder FAQ
Does this JWT decoder verify the signature?
No. Decoding only Base64URL-decodes the header and payload so you can inspect claims. Signature verification requires the secret or public key — use the JWT Validator or your server library for that.
Is it safe to paste a production JWT here?
The decoder runs entirely in your browser — nothing is uploaded. That said, a JWT often grants access, so avoid pasting live production tokens on any public tool.
Why are 'iat' and 'exp' just numbers?
They are Unix timestamps (seconds since 1970). Multiply by 1000 to convert to a JavaScript Date, or use new Date(exp * 1000).
Why does decoding still work if I change random characters in the payload?
Base64URL decoding never checks integrity — it just maps characters back to bytes. Any well-formed segment decodes to something. Only signature verification (comparing an HMAC or checking a public-key signature) can tell you whether the payload was tampered with.
What is the difference between decoding and verifying?
Decoding is a one-way, unauthenticated transform: Base64URL text to JSON. Verifying additionally recomputes the signature with a known key and compares it byte-for-byte to the signature segment, proving the token was issued by someone holding that key and hasn't been altered.
Why does my token show 'Invalid token' when I paste it here?
The decoder expects exactly three dot-separated Base64URL segments. Common causes are: trailing whitespace, the 'Bearer ' prefix left in, a truncated copy-paste, or a token that isn't a JWT at all (e.g. an opaque OAuth access token).
Can I decode an encrypted JWT (JWE)?
No. This decoder handles JWS (signed JWTs), which are the vast majority in practice. A JWE has five dot-separated segments and its payload is encrypted, not just encoded — you need the recipient's private key to decrypt it, which no browser tool should ask you for.
Does the algorithm in the header guarantee how the token was actually signed?
No — the alg header is just a claim about intent, and it is exactly why the notorious 'alg:none' and RS256/HS256 confusion attacks exist. Servers must pin the expected algorithm in code, never trust the header to choose it at verification time.
Related tools
Recent articles
Guides on JWT secrets, signing algorithms, and token security.