Skip to content
100% in your browser. Nothing you paste is uploaded — all processing runs locally. Read more →

What is a JWT?

On this page
  1. A complete JWT
  2. What’s in each part
    1. Header
    2. Payload
    3. Signature
  3. What JWTs are NOT
  4. Where JWTs fit well
  5. Where JWTs fit poorly
  6. Common security mistakes
    1. 1. Trusting alg without pinning
    2. 2. Accepting alg: none
    3. 3. Weak HMAC secrets
    4. 4. Storing JWTs in localStorage
    5. 5. Long expiration with no revocation strategy
    6. 6. Putting too much in the payload
  7. How big is a JWT?
  8. Algorithms compared
  9. Try the tools
  10. Further reading

A JWT (JSON Web Token, pronounced “jot”) is a signed bundle of JSON used for authentication and authorization. The signature lets the receiver verify the contents weren’t tampered with, without calling back to the issuer to check.

That last property — stateless verification — is what makes JWTs popular and also what makes them dangerous when misused. This page covers both.

A complete JWT

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIEV4YW1wbGUiLCJpYXQiOjE1MTYyMzkwMjJ9.
6CjSLjOygL01Z5yY2LkZTtkrfZRhzmnGGc5n7QqJEQg

Three parts separated by dots:

  1. Header (base64url-encoded JSON) — what algorithm signed it
  2. Payload (base64url-encoded JSON) — claims about the user / session
  3. Signature — proof the first two haven’t been modified

You can paste any JWT into the decoder and see all three parts. Decoding is just base64 + JSON.parse — no secret needed.

What’s in each part

{
  "alg": "HS256",
  "typ": "JWT"
}

Tells the receiver which algorithm to expect. HS256 is HMAC-SHA256. Other common algorithms: RS256 (RSA-SHA256), ES256 (ECDSA-SHA256), EdDSA.

Payload

{
  "sub": "user-12345",
  "name": "Alice Example",
  "iat": 1516239022
}

Contains “claims” — assertions about the subject. The JWT spec defines seven registered claims (iss, sub, aud, exp, nbf, iat, jti), but you can include any JSON.

Signature

The hard part. For HS256:

HMAC-SHA256(
  base64url(header) + "." + base64url(payload),
  secret
)

Then base64url-encode the result. The receiver re-computes this with the same secret; if it matches, the JWT is authentic. If it doesn’t, either the token was modified or the secret is wrong.

What JWTs are NOT

Three confusions worth clearing up:

Where JWTs fit well

Where JWTs fit poorly

See JWT vs session cookies for the full comparison.

Common security mistakes

1. Trusting alg without pinning

Old JWT libraries had a vulnerability: the receiver would read the algorithm from the JWT itself, then verify with a key matched to that algorithm. An attacker could forge a token with alg: HS256 and the public key as the HMAC secret — the server accepted it.

Fix: always specify the expected algorithm explicitly:

jwt.verify(token, secret, { algorithms: ["HS256"] });

2. Accepting alg: none

The JWT spec defines an none algorithm that means “no signature.” Some old libraries treated this as “signature passed.” Catastrophic.

Fix: modern libraries reject none by default. Verify your library does, or pin algorithms explicitly.

3. Weak HMAC secrets

A 6-character secret can be brute-forced offline. HS256 needs at least 256 bits of entropy.

Fix: generate secrets with a CSPRNG:

import { randomBytes } from "crypto";
const secret = randomBytes(32).toString("base64");

4. Storing JWTs in localStorage

localStorage is accessible to any JavaScript on the page, including malicious script injected via XSS. A stolen JWT is a stolen session.

Fix: store in HttpOnly cookies (inaccessible to JS) or use shorter expiry + secure refresh-token flow.

5. Long expiration with no revocation strategy

A 30-day JWT with no revocation means a stolen token gives 30 days of access. Even after password change.

Fix: short access token (15 min), long refresh token (days/weeks), revocation list keyed by user or jti.

6. Putting too much in the payload

JWTs travel in headers. Some HTTP infrastructure caps headers at 8KB. A bloated JWT can break requests in production.

Fix: include only what’s needed for authorization decisions; look up the rest from the database.

How big is a JWT?

A typical signed JWT is 300–800 bytes. With many claims and a long audience list it can grow past 1 KB. Beyond ~4 KB you start to risk hitting infrastructure limits.

Algorithms compared

AlgorithmTypeSpeedKeyUse case
HS256HMACFastestShared secretInternal APIs where both sides share a key
HS384 / HS512HMACSlightly slowerShared secretHigher-security HMAC
RS256RSA-SHA256SlowPublic/private keypairPublic verification (server signs, many can verify)
RS384 / RS512RSASlowerPublic/private keypairHigher-security RSA
ES256ECDSA-SHA256Faster than RSAEC keypairModern public-key auth
EdDSA (Ed25519)EdDSAFastEC keypairNewest, recommended for new systems

For symmetric setups (your service signs and your service verifies), HS256 is fine. For “anyone can verify with the public key,” use RS256 or ES256.

Try the tools

Further reading