JWT Decoder

Paste a JSON Web Token to read its header and payload. Decoding is not verification. Anyone holding the token can read what you see here, so type the secret into the secret field when you also need to check the signature. Token and secret both stay in the browser.

Encoded Token
Header
Payload

1 JWT Decoder example

Encoded Token

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Header

1
2
3
4
{
  "alg": "HS256",
  "typ": "JWT"
}

Payload

1
2
3
4
5
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

2 What it is good for

Checking claims

Read exp, iat, iss and aud to find out whether a token is expired, issued by who you think, and meant for your API. The payload shows exp and iat as Unix seconds; paste one into the Unix timestamp converter to read it as a date.

Debugging a 401

A token can have a valid signature and still fail because aud or iss does not match what the API expects. Compare those two claims with your server config first.

Verifying a signature

Type the secret and the tool checks HS256, HS384 or HS512 signatures in your browser. Invalid Signature means the secret is wrong or the token changed after signing.

How to use the JWT Decoder tool

  1. Paste the token into the input editor, or click Upload File to load it from a text file. Decoding happens as soon as the input changes.
  2. The Header and Payload panels show the decoded JSON. Timestamps such as exp and iat stay in Unix seconds; paste one into the Unix timestamp converter to see whether the token has expired.
  3. If the input does not have three dot-separated parts, the status shows Invalid Token.
  4. To check the signature, type the shared secret into the secret field. The status shows Signature Verified or Invalid Signature. The tool verifies HS256, HS384 and HS512 tokens only.
  5. Dummy Data loads a sample token signed with your-256-bit-secret; type that into the secret field to see the verified state. Clear removes the token.

How to decode and verify a JWT in JavaScript, PHP and Python

JavaScript / Node.js

const { createHmac, timingSafeEqual } = require("node:crypto");

const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";

// 1. Decode header and payload (no verification, works in the browser too)
const [h, p, s] = token.split(".");
const header = JSON.parse(Buffer.from(h, "base64url").toString());
const payload = JSON.parse(Buffer.from(p, "base64url").toString());
console.log(header, payload);

// 2. Verify an HS256 signature
const expected = createHmac("sha256", "your-256-bit-secret").update(`${h}.${p}`).digest("base64url");
const valid = expected.length === s.length && timingSafeEqual(Buffer.from(expected), Buffer.from(s));
console.log("signature valid:", valid);

// 3. Check expiry
if (payload.exp && payload.exp < Date.now() / 1000) console.log("token expired");

// Or with jsonwebtoken (npm i jsonwebtoken): jwt.verify(token, secret, { algorithms: ["HS256"] })

In the browser replace Buffer with atob() after swapping - and _ back to + and /.

PHP

<?php
$token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';

function b64url_decode(string $s): string {
    return base64_decode(strtr($s, '-_', '+/') . str_repeat('=', (4 - strlen($s) % 4) % 4));
}

[$h, $p, $s] = explode('.', $token);
$header  = json_decode(b64url_decode($h), true);
$payload = json_decode(b64url_decode($p), true);
print_r($payload);

// Verify HS256
$expected = hash_hmac('sha256', "$h.$p", 'your-256-bit-secret', true);
$valid = hash_equals($expected, b64url_decode($s));
var_dump($valid); // bool(true)

if (isset($payload['exp']) && $payload['exp'] < time()) {
    echo "Token expired\n";
}
// For RS256 and other algorithms use firebase/php-jwt: JWT::decode($token, new Key($secret, 'HS256'))

Python (PyJWT)

# pip install PyJWT
import jwt

token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"

# Inspect only, no verification
print(jwt.decode(token, options={"verify_signature": False}))

# Verify signature and standard claims (exp, nbf)
try:
    claims = jwt.decode(token, "your-256-bit-secret", algorithms=["HS256"])
    print(claims)
except jwt.ExpiredSignatureError:
    print("token expired")
except jwt.InvalidSignatureError:
    print("wrong secret or tampered token")

Always pass algorithms=[...] explicitly. Accepting whatever the header says is how alg=none and key confusion attacks work.

Common errors and how to fix them

Invalid Token / JsonWebTokenError: jwt malformed
The string does not have exactly three parts separated by dots. You probably copied the "Bearer " prefix, a trailing quote, or only part of the token from a log line.
Payload decodes but the status says Invalid Signature
Wrong secret, or the token is RS256 and needs a public key. Read alg in the header; this tool verifies only HS256, HS384 and HS512. Also check whether the issuer Base64-decodes the secret before signing.
Unexpected token in JSON at position 0 when decoding the payload
The Base64url was decoded with a standard Base64 function, which fails on - or _. Swap them for + and /, and restore the = padding before decoding.
The token says exp is in the future but the server still rejects it
Clock skew between issuer and verifier, or the server also checks nbf, aud or iss. Compare server time with date -u, and allow a small leeway in the verifier.

Good to know

Never let the header tell you which algorithm to use. Early libraries accepted alg: none and skipped verification entirely. Several others would take an RS256 public key and treat it as an HS256 secret, so an attacker could sign tokens with material that was published on purpose. Modern libraries fix both, but only if you pin the algorithm list in your verify call.

Decoding a token tells you nothing about whether to trust it. Anyone can read the payload, so use a decoder like this one for debugging only. Real verification checks the signature with the right key, then exp and nbf against the clock, then iss and aud against what your service expects.

Specifications and references

This tool follows the specifications below. Read them when you need the exact normative wording, for example to settle an edge case between two implementations.

Guides and tutorials

Frequently Asked Questions

What are the three parts of a JWT?

Header (algorithm and token type), payload (the claims) and signature, separated by dots. The first two are Base64URL-encoded JSON; the signature proves they were not altered.

Can it verify the signature?

Yes, for HS256, HS384 and HS512 tokens. Enter the secret and the tool recomputes the signature and shows Signature Verified or Invalid Signature. RS256 and ES256 tokens need a public key, which this tool does not accept.

How do I know if a token is expired?

Read the exp claim in the payload. It is a Unix timestamp in seconds, so paste it into the Unix timestamp converter, or run new Date(exp * 1000) in a console, to see the date. If that date has passed, the token is expired. iat and nbf use the same format.

Is it safe to paste a real token here?

Decoding happens in your browser and nothing is transmitted. However, a token works as a credential until it expires, so avoid pasting production tokens on shared computers.

Why does the payload show but the signature is invalid?

The payload is only Base64url-encoded, not encrypted, so it always decodes. An invalid signature means the secret is wrong or the token was modified after signing.

Related

JWT generator builds test tokens. The signature itself is an HMAC, and each segment is Base64URL, so use those two tools to take a token apart by hand.

More Security & Hashing Tools

Browse all security & hashing tools

Copied to clipboard!