JWT Decoder
Free online JWT decoder — paste any JSON Web Token, bearer token, or access token to instantly decode the header, payload and claims. Inspect standard JWT claims (exp, iat, iss, sub), check token expiry, and decode JWT online free. Runs entirely in your browser — tokens never leave your device.
Timestamp display
Load example
JWT structure
Runs in your browser
Tokens are decoded locally in JavaScript and never sent to any server. Do not paste production tokens if they contain sensitive personal data.Bearer prefix stripped — decoding token only.
Token
Header
Payload
Standard claims
| expired valid |
Signature
The signature is a base64url-encoded cryptographic hash of the header and payload.
Verification requires the secret key (HS256) or public key (RS256/ES256).
This tool decodes only — use a library like jsonwebtoken, PyJWT, or firebase/php-jwt to verify signatures in production.
What is a JWT token?
A JWT (JSON Web Token) is an open standard (RFC 7519) for securely transmitting information between parties as a compact, URL-safe JSON object. JWTs are digitally signed — using HMAC-SHA256 (HS256) or RSA/ECDSA (RS256, ES256) — so the recipient can verify the token's authenticity and confirm it has not been tampered with.
JWTs are everywhere in modern authentication and authorization systems:
- API authentication — After login, the server issues a JWT. The client sends it in the
Authorization: Bearer <token>header of subsequent API requests. - Single Sign-On (SSO) — One JWT from an identity provider (Auth0, Okta, Google) grants access across multiple services without re-authentication.
- Stateless REST APIs — No server-side session storage needed. The JWT carries all the information the server needs, reducing database lookups.
- OAuth 2.0 access tokens — Bearer tokens in OAuth 2.0 flows are typically JWTs issued by the authorization server.
Because a JWT is self-contained and signed, any server that holds the verification key can validate it without calling a central session store — making JWTs ideal for distributed systems and microservices.
The three parts of a JWT: header, payload, signature
A JWT is three base64url-encoded JSON objects joined by dots: header.payload.signature. The encoding is URL-safe (uses - and _ instead of + and /, and padding = is omitted).
Header
Specifies the token type and the signing algorithm. The most common fields are:
alg— Algorithm:HS256(HMAC-SHA256, symmetric),RS256(RSA-SHA256, asymmetric),ES256(ECDSA, asymmetric)typ— Token type, almost alwaysJWTkid— Key ID: used to select which key to use when multiple keys are in rotation
Payload
Contains the claims — statements about the subject and additional metadata. Claims can be registered (standardised in RFC 7519), public (registered with IANA), or private (custom to your application).
{"sub":"user_123","name":"Alice","iat":1735689600,"exp":1735776000,"roles":["admin"]}
Signature
Computed as HMAC-SHA256(base64url(header) + "." + base64url(payload), secret) for HS256, or the equivalent RSA/ECDSA operation for RS256/ES256. Any change to the header or payload invalidates the signature, making tampering detectable.
This JWT decoder visualises each part in its conventional colour and decodes the header and payload. Signature verification is not possible without the private key or secret.
JWT claims explained
JWT claims are key-value pairs in the payload. RFC 7519 defines seven registered claims that carry standard meanings recognised by all JWT libraries:
| Claim | Name | Description |
|---|---|---|
| iss | Issuer | Who issued the token, e.g. https://accounts.google.com |
| sub | Subject | The principal the token represents, typically a user ID |
| aud | Audience | The intended recipient(s) — your API should reject tokens not addressed to it |
| exp | Expiration Time | Unix timestamp after which the token must not be accepted |
| nbf | Not Before | Unix timestamp before which the token is not valid |
| iat | Issued At | Unix timestamp when the token was issued — useful for calculating token age |
| jti | JWT ID | Unique identifier for this token, used to prevent replay attacks |
Beyond these, tokens commonly include public claims registered with IANA (like email, name) and private claims specific to your application (like roles, permissions, tenant_id). This decoder highlights the seven registered claims and shows human-readable timestamps for exp, iat, and nbf.
How to decode a JWT in JavaScript, Python, and PHP
Decoding a JWT only requires base64url-decoding the header and payload — no secret key needed. Here are minimal implementations in three languages. These decode the token; they do not verify the signature. Always verify signatures in production using a trusted library.
JavaScript — Browser
function decodeJWT(token) {
const [headerB64, payloadB64] = token.split('.');
function base64urlDecode(str) {
const b64 = str.replace(/-/g, '+').replace(/_/g, '/');
const padded = b64.padEnd(b64.length + (4 - b64.length % 4) % 4, '=');
return JSON.parse(atob(padded));
}
return {
header: base64urlDecode(headerB64),
payload: base64urlDecode(payloadB64),
};
}
const { header, payload } = decodeJWT('eyJhbGci...');
console.log(payload.sub); // Subject claim
console.log(new Date(payload.exp * 1000)); // Expiry as Date
JavaScript — Node.js 16+
// Node.js 16+ supports 'base64url' encoding natively via Buffer
const parts = token.split('.');
const header = JSON.parse(Buffer.from(parts[0], 'base64url').toString());
const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString());
console.log(payload.exp); // Expiry Unix timestamp
// Decode-only library — jwt-decode (100M+ weekly downloads, browser + Node.js):
// import { jwtDecode } from 'jwt-decode';
// const payload = jwtDecode(token);
// To VERIFY the signature in production, use jsonwebtoken:
// const jwt = require('jsonwebtoken');
// const verified = jwt.verify(token, process.env.JWT_SECRET);
Python
import base64, json
def decode_jwt(token: str) -> dict:
"""Decode a JWT without signature verification."""
header_b64, payload_b64, _ = token.split('.')
def b64url_decode(s: str):
s += '=' * (-len(s) % 4) # restore padding
return json.loads(base64.urlsafe_b64decode(s))
return {'header': b64url_decode(header_b64), 'payload': b64url_decode(payload_b64)}
# Usage
decoded = decode_jwt('eyJhbGci...')
print(decoded['payload']['sub']) # Subject
print(decoded['payload'].get('exp')) # Expiry Unix timestamp
# To also VERIFY the signature, use PyJWT (pip install PyJWT):
# import jwt
# payload = jwt.decode(token, key, algorithms=['HS256'])
# For decode-only without verification:
# payload = jwt.decode(token, options={'verify_signature': False}, algorithms=['HS256'])
PHP
function decodeJWT(string $token): array {
[$headerB64, $payloadB64] = explode('.', $token);
$decode = function (string $b64url): array {
$b64 = strtr($b64url, '-_', '+/');
$padded = str_pad($b64, strlen($b64) + (4 - strlen($b64) % 4) % 4, '=');
return json_decode(base64_decode($padded), true);
};
return ['header' => $decode($headerB64), 'payload' => $decode($payloadB64)];
}
// Usage
$decoded = decodeJWT($token);
echo $decoded['payload']['sub'];
echo date('c', $decoded['payload']['exp']); // ISO 8601 expiry
// To VERIFY signatures in production, use firebase/php-jwt (composer require firebase/php-jwt):
// use Firebase\JWT\JWT; use Firebase\JWT\Key;
// $decoded = JWT::decode($token, new Key($secret, 'HS256'));
Working with hex-encoded binary data alongside JWTs? Try our hex decoder to convert hexadecimal strings to readable text.
JWT security best practices
JWTs are powerful but misused ones create serious vulnerabilities. Key guidelines:
- Never store sensitive data in the payload. The payload is base64-encoded, not encrypted — anyone with the token can decode it instantly (as this tool demonstrates). Keep secrets, passwords, and PII out of JWTs.
- Always verify the signature server-side. Never trust a decoded JWT payload without first verifying the signature. Use a well-tested library rather than rolling your own verification.
- Set short expiry times (
exp). A stolen JWT is valid until it expires. Access tokens should expire in minutes (15m is common); use refresh tokens for long-lived sessions. - Validate
issandaudclaims. Ensure the token was issued by the expected authority and is intended for your service. Ignoring these allows confused deputy attacks. - Prefer RS256 over HS256 for public-facing APIs. RS256 uses an asymmetric key pair — the private key signs, the public key verifies. This means you can distribute the public key widely without compromising signing ability.
- Transmit over HTTPS only. JWTs in transit over plain HTTP can be intercepted and replayed.
- Consider token revocation. JWTs are stateless and cannot be invalidated before expiry without a blocklist. For sensitive operations, maintain a revocation list or use opaque tokens.
Frequently asked questions
exp claim (Expiration Time) specifies when the token expires. Its value is a NumericDate — the number of seconds elapsed since January 1 1970 UTC (Unix timestamp). For example, exp: 1735776000 means the token expired on January 2 2025 at 00:00:00 UTC. This decoder shows exp as a human-readable date and flags expired tokens with a red badge.
., take the payload part, replace - with + and _ with / (base64url → base64), add padding, then use atob() and JSON.parse(). This decodes the claims but does not verify the signature. In production, use jsonwebtoken (jwt.verify()) to both decode and verify.
Authorization: Bearer <token>. "Bearer" means that anyone who holds the token can use it — no additional proof of identity is required. JWTs are the most common format for bearer tokens in REST APIs and OAuth 2.0 flows. This decoder automatically strips the Bearer prefix when you paste a full Authorization header value.
In Node.js 16+ you can decode the payload natively with Buffer.from:
const payload = JSON.parse(Buffer.from(token.split('.')[1], 'base64url').toString());
For a lightweight decode-only library, use jwt-decode (100M+ weekly npm downloads): import { jwtDecode } from 'jwt-decode'; const payload = jwtDecode(token);
To also verify the signature, use jsonwebtoken: jwt.verify(token, secret). Decoding and verifying are different — always verify in production.
exp without a server-side blocklist; if a user logs out or is banned, the token stays valid until expiry. (2) Sensitive data is visible — the payload is only base64-encoded, not encrypted; anyone who holds the token can decode it (as this tool shows). (3) Algorithm confusion attacks — accepting multiple algorithms can allow attackers to forge tokens. (4) Token size — JWTs are larger than opaque session IDs, adding overhead to every request. For apps that need instant session revocation, traditional server-side sessions may be safer. JWTs shine in stateless REST APIs and distributed systems.