What Is a JWT? JSON Web Tokens Explained
A plain-language guide to JSON Web Tokens — what they contain, how they are structured, and where they are used in modern authentication systems.
ProfitTool Team
Security & Dev
What a JWT Is
A JSON Web Token (JWT, pronounced "jot") is a compact, URL-safe way to represent a set of claims — statements about a subject — that a recipient can verify was not tampered with. It is defined by RFC 7519.
A JWT looks like three Base64URL-encoded segments joined by dots:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NSIsIm5hbWUiOiJKYW5lIiwiZXhwIjoxNzY5OTk5OTk5fQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXkSplit on the dots and decode the first two segments and you get ordinary JSON:
// Header
{ "alg": "HS256", "typ": "JWT" }
// Payload
{ "sub": "12345", "name": "Jane", "exp": 1769999999 }The third segment is a signature over the first two.
The Single Most Important Property
A JWT is signed, not encrypted. Anyone holding the token can read the payload — no key required, no attack required. Base64URL is an encoding, not a cipher.
The signature guarantees integrity and authenticity: it proves the token was issued by someone holding the signing key and has not been altered since. It guarantees nothing about confidentiality.
This single fact determines what you may put in a token. Never place passwords, API keys, full payment details, medical information, or anything else you would not print in a log line. If you genuinely need a confidential payload, that is JWE (RFC 7516), a different and considerably heavier construction. Plain JWTs — the kind almost everyone means — are JWS (RFC 7515) and are readable by design.
The Three Segments
Header
Declares how the token is signed.
| Field | Purpose |
|---|---|
alg | Signing algorithm — HS256, RS256, ES256, EdDSA |
typ | Media type, conventionally JWT |
kid | Key ID, so the verifier can pick the right key from a set |
Payload
Carries the claims. RFC 7519 registers seven, all optional but all with fixed meaning:
| Claim | Name | Meaning |
|---|---|---|
iss | Issuer | Who minted the token |
sub | Subject | Who or what the token is about — typically a user ID |
aud | Audience | Who the token is intended for |
exp | Expiration Time | Reject at or after this time |
nbf | Not Before | Reject before this time |
iat | Issued At | When it was minted |
jti | JWT ID | Unique identifier, used for replay and revocation tracking |
exp, nbf and iat are NumericDate values: seconds since the Unix epoch, not milliseconds. Passing Date.now() from JavaScript directly into exp produces a token that appears to expire roughly fifty thousand years from now. This is the most common JWT bug in existence.
Beyond the registered set you may add your own claims — role, tenant_id, permissions. Keep them small: JWTs usually travel in an Authorization header, and servers cap header size (nginx defaults to 8 KB across all headers). A token stuffed with a full user profile and a permission matrix will eventually be rejected by a proxy rather than by your application.
Signature
Computed over base64url(header) + "." + base64url(payload) using the algorithm named in the header.
Signing Algorithms
| Algorithm | Type | Key model | Use when |
|---|---|---|---|
HS256 | HMAC-SHA256 | One shared secret signs and verifies | One service issues and verifies its own tokens |
RS256 | RSA + SHA-256 | Private key signs, public key verifies | Many services verify tokens from one issuer |
ES256 | ECDSA P-256 + SHA-256 | Same split, much smaller keys and signatures | Same as RS256, where token size matters |
EdDSA | Ed25519 | Same split, fast and misuse-resistant | Greenfield systems with modern library support |
The choice is really about key distribution. HMAC requires every verifier to hold the secret that can also mint tokens — acceptable within one service, dangerous across a fleet. Asymmetric algorithms let you publish a verification key freely (typically as a JWKS endpoint) while the signing key stays in one place. This is why every OIDC provider uses RS256 or ES256.
Why Base64URL Rather Than Base64
Standard Base64 uses +, / and =, all of which have meaning in URLs and are mangled by percent-encoding. Base64URL (RFC 4648 §5) substitutes - for + and _ for /, and drops the = padding, so a token can sit safely in a query string, a path segment, a cookie, or a header without further escaping.
The practical consequence: atob() and standard Base64 decoders will not decode a JWT segment correctly without translating those characters first, and without re-adding the stripped padding. That is covered step by step in How to Decode a JWT.
What JWTs Are Good For
- Stateless API authentication. The token carries the identity, so any instance of any service can authorise a request without a database lookup or a shared session store.
- Federated identity. OpenID Connect ID tokens are JWTs. This is the single largest deployment of the format.
- Service-to-service authorisation in a microservice estate, where the calling service's identity travels with the request.
- Short-lived, single-purpose links — email verification, password reset, signed download URLs — where the token is the capability and expires quickly.
What They Are Not Good For
- Sessions you need to revoke instantly. A signed token is valid until it expires, full stop. There is no "log out everywhere" without adding server-side state, which forfeits the statelessness that motivated the choice.
- Anything confidential, as above.
- Long-lived credentials. A stolen 30-day token is a 30-day compromise.
- Frequently changing data. Claims are frozen at issuance. Revoke a permission and every outstanding token still asserts it.
- Replacing a session cookie in a plain server-rendered app. If you already have a session store and a single backend, a session cookie is simpler, revocable, and keeps nothing sensitive on the client.
JWT vs Server-Side Sessions
| JWT | Session cookie | |
|---|---|---|
| State | Stateless — nothing stored server-side | Session record per user |
| Revocation | Only at expiry, unless you add a denylist | Immediate |
| Horizontal scaling | Any instance verifies independently | Needs a shared session store |
| Data freshness | Frozen at issuance | Read live on every request |
| Size on the wire | Hundreds of bytes to a few KB | An opaque ID |
The honest summary: JWTs trade revocability for statelessness. If you find yourself building a denylist, a revocation table, and a lookup on every request, you have rebuilt sessions with extra steps — and a session store would have been the simpler design.
Security: The Failures That Actually Happen
alg: none. RFC 7519 defines an "unsecured JWT" with no signature. A verifier that trusts the header's alg field will accept a token with the signature stripped and alg set to none — forged by anyone. Real libraries shipped this default; it is the origin of the rule below.
Algorithm confusion. A verifier expecting RS256 that also accepts HS256 can be attacked by signing a token with the issuer's public key as the HMAC secret. The public key is public, so the attacker can mint valid tokens.
Both failures share one root cause, and one fix: never let the token tell you how to verify it. Pin the expected algorithm in your verification call.
// Wrong — the attacker chooses the algorithm
jwt.verify(token, key);
// Right — you choose it
jwt.verify(token, key, { algorithms: ["RS256"] });The rest of the checklist:
- Verify before you read. Decoding is not validation. A decoded-but-unverified payload is attacker-controlled input.
- Always set
exp. A token without one never expires. - Check
audandiss. A valid token issued for a different service is still a valid signature. - Keep lifetimes short and use refresh tokens for continuity — see JWT Expiration Explained.
- Mind storage in browsers.
localStorageis readable by any injected script; anhttpOnlycookie is not, but needs CSRF protection. - Rotate signing keys using
kidso old and new keys can coexist during a rollover.
Frequently Asked Questions
Is a JWT encrypted? No. A standard JWT is signed (JWS) and its payload is plainly readable. Encryption is JWE, a separate specification.
Can a JWT be revoked? Not by itself. Revocation requires server-side state — a jti denylist, a token version counter on the user record, or short expiry plus refresh-token rotation.
How big can a JWT be? There is no limit in the specification, but proxies and servers cap header size, commonly around 8 KB total. Keep tokens to a few hundred bytes.
Are JWT and OAuth the same thing? No. OAuth 2.0 is an authorisation framework; a JWT is a token format. OAuth access tokens are sometimes JWTs and sometimes opaque strings. OpenID Connect ID tokens are always JWTs.
Should I use a JWT for my login system? If you have one backend and a session store, a session cookie is usually simpler and safer. JWTs earn their complexity when multiple independent services must verify identity without a shared store.
Decode and pretty-print any token with the JWT Decoder, or inspect every claim with expiry countdowns and human-readable timestamps using the JWT Claims Inspector. For the encoding underneath the dots, see the Base64 & Base64URL Encoder / Decoder.
ProfitTool Team
Security & DevWritten and maintained by the ProfitTool team. Where an article references a ProfitTool utility, the behaviour described is the behaviour of the tool on this site — you can open it and check the output yourself.
Try the Interactive Tools Mentioned
Free to use with no ProfitTool signup. Data handling varies by tool.
More from the Knowledge Hub
JWT Expiration Explained: exp, iat and nbf Claims
How JWT time claims work — exp, iat and nbf — plus clock skew tolerance, choosing a token lifetime, refresh-token rotation and the revocation gap.
Developer ToolsHow to Decode a JWT Token (Without a Library)
You can decode a JWT token in any language using nothing but base64url decoding and JSON parsing. Here is how to do it in JavaScript, Python and the browser.
Developer ToolsOAuth 2.0 Explained Simply: Access Tokens, Scopes and Flows
OAuth 2.0 is the industry standard for delegated authorisation. This guide explains access tokens, scopes, the four grant flows, and how JWTs fit into the picture.