JWT Header Decoder
Decode the JWT header to view the signing algorithm and token type. The header determines how the token signature is verified.
JWT Header Structure
The header is the first part of the JWT (before the first dot) and contains metadata about the token:
``json
{
"alg": "HS256",
"typ": "JWT"
}
`
Header Fields
| Field | Description | Common Values |
|---|---|---|
| alg | Signing algorithm | HS256, RS256, ES256 |
| typ | Token type | JWT |
| kid | Key ID | Used for key rotation |
| jku | JWK Set URL | URL to public keys |
| x5u | X.509 URL | URL to certificate |
| x5c | X.509 Certificate | Certificate chain |
JWT Header Decoder
`javascript
function decodeJWTHeader(token) {
const headerPart = token.split('.')[0];
// Base64URL decode
const base64 = headerPart.replace(/-/g, '+').replace(/_/g, '/');
const padded = base64 + '=='.slice(0, (4 - base64.length % 4) % 4);
const decoded = atob(padded);
const header = JSON.parse(decoded);
// Analyze algorithm security
const algorithmInfo = getAlgorithmInfo(header.alg);
return {
raw: headerPart,
decoded: header,
algorithm: algorithmInfo
};
}
function getAlgorithmInfo(alg) {
const algorithms = {
'HS256': { type: 'HMAC', hash: 'SHA-256', keyType: 'symmetric', secure: true },
'HS384': { type: 'HMAC', hash: 'SHA-384', keyType: 'symmetric', secure: true },
'HS512': { type: 'HMAC', hash: 'SHA-512', keyType: 'symmetric', secure: true },
'RS256': { type: 'RSA', hash: 'SHA-256', keyType: 'asymmetric', secure: true },
'RS384': { type: 'RSA', hash: 'SHA-384', keyType: 'asymmetric', secure: true },
'RS512': { type: 'RSA', hash: 'SHA-512', keyType: 'asymmetric', secure: true },
'ES256': { type: 'ECDSA', curve: 'P-256', keyType: 'asymmetric', secure: true },
'ES384': { type: 'ECDSA', curve: 'P-384', keyType: 'asymmetric', secure: true },
'ES512': { type: 'ECDSA', curve: 'P-521', keyType: 'asymmetric', secure: true },
'PS256': { type: 'RSA-PSS', hash: 'SHA-256', keyType: 'asymmetric', secure: true },
'none': { type: 'None', keyType: 'none', secure: false, warning: 'INSECURE!' }
};
return algorithms[alg] || { type: 'Unknown', secure: false };
}
`
Algorithm Comparison
| Algorithm | Type | Best For |
|---|---|---|
| HS256 | Symmetric | Single server, simple setup |
| RS256 | Asymmetric | Microservices, public verification |
| ES256 | Elliptic Curve | Mobile, smaller tokens |
The Attacks a Verifier Must Stop
alg: none. A token declaring no algorithm with an empty signature. A library that
trusts the header's alg will accept it as valid. Always specify the expected algorithm
when verifying rather than reading it from the token.
Algorithm confusion. A token signed with HMAC using the server's *public* RSA key as the
secret. If the verifier picks the algorithm from the header, an RS256 verifier can be tricked
into running HS256 with a key the attacker already has.
Weak secrets. HS256 with a short or dictionary secret is brute-forceable offline from a
single captured token. Use at least 256 bits of real entropy.
No expiry check. exp is a claim, not an enforcement. A library that decodes without
verifying, or code that reads claims from a decoded-but-unverified token, accepts expired and
forged tokens alike.
`javascript
// Specify the algorithm; never trust the header's
jwt.verify(token, secret, { algorithms: ['HS256'] });
`
Anyone Can Read the Payload
A JWT is signed, not encrypted. The payload is base64url — readable by anyone holding the
token, including the browser it is stored in. Never put anything in it you would not print
on a postcard: no passwords, no PII beyond an identifier, no internal keys.
Revocation Is the Hard Part
A signed token is valid until it expires, and there is no way to withdraw one. Options:
| Approach | Cost |
|---|---|
| Short expiry (5–15 min) plus refresh tokens | Standard; adds a refresh endpoint |
| A denylist of revoked JTIs | Reintroduces the state JWTs were meant to avoid |
| Rotate the signing key | Revokes every token at once |
| Version claim checked against the user record | A database read per request |
If you need immediate revocation for every session, a server-side session is a simpler and
more honest choice than a JWT.Where to Store One
| Location | XSS-safe | CSRF-safe |
|---|---|---|
localStorage | No | Yes |
| sessionStorage` | No | Yes |
| Cookie (HttpOnly, Secure, SameSite) | Yes | Yes, with SameSite |
| In-memory | Yes | Yes |
An HttpOnly cookie is the safest default. Any token reachable from JavaScript is reachable by
any script that gets injected.