What is JWT? A Complete Developer's Guide (2026)

If you've authenticated against any modern API in the last few years, you've handled a JWT — those eyJ... strings that come back in the response body and get tacked onto every subsequent request. JWT (JSON Web Token) is the de-facto standard for stateless authentication in single-page apps, mobile backends, and microservice architectures. It's compact, self-contained, and works without a server-side session store.

This guide breaks down the anatomy of a JWT, walks through the three signing algorithms you'll actually meet (HS256, RS256, ES256), compares it head-to-head with traditional session cookies, and flags the four security mistakes that show up in production code all too often.

1. The Three-Part Structure

A JWT is just three Base64URL-encoded strings joined by dots:

header.payload.signature

Each segment has a job. Together they let a receiver verify that the token came from a trusted issuer and hasn't been tampered with. The format is defined in RFC 7519.

Header

Declares the signing algorithm and token type:

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

After Base64URL encoding, that JSON becomes eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. Note: Base64URL is the URL-safe variant (uses - and _ instead of + and /, drops padding) — see the Base64 guide for details.

Payload (Claims)

The payload carries the actual data — known as "claims." There are three flavors you should know about, and they often get conflated in tutorials:

Signature

The signature is what makes the token trustworthy. For HS256 it's calculated like this:

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)

The server recomputes this hash on every request and rejects tokens whose signature doesn't match. If even a single character in the header or payload changes, the signature will no longer verify.

2. HS256 vs RS256 vs ES256

The alg field in the header picks the signing algorithm. The three you'll meet most often:

AlgorithmTypeKey modelBest for
HS256Symmetric HMACOne shared secretSingle-service apps, internal APIs
RS256Asymmetric RSAPrivate signs, public verifiesMulti-service verification, OAuth 2.0, OIDC
ES256Elliptic curvePrivate signs, public verifiesMobile/IoT, short signatures, high security

Rule of thumb: HS256 for a single backend signing and verifying its own tokens (simpler is better). RS256 once you have multiple services that need to verify tokens minted by an auth server — the public key can be distributed freely without compromising security. ES256 when signature size or CPU cost matters (mobile clients, edge devices).

3. A Complete Example

Here's the canonical example from jwt.io, which you can paste into any decoder to verify:

Header (Base64URL-encoded):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Payload (Base64URL-encoded):

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ

Full token:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Three segments, two dots, one signed payload. Decoded, the payload reads:

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

4. JWT vs Session Cookies

The classic question: when to reach for a JWT, and when to stick with the tried-and-true server-side session cookie? Here's the honest comparison.

DimensionJWTSession cookie
State modelStateless — token carries everythingStateful — server stores session
ScalabilityTrivial — any node can verifyNeeds a shared store (Redis)
RevocationHard — needs a blacklist or short expiryEasy — delete the session record
Size per requestLarger (header + payload + signature)Tiny — opaque session ID
CSRF riskNone if sent in Authorization headerNeeds CSRF tokens or SameSite cookies

In short: JWTs shine for stateless, distributed systems and cross-service auth. Sessions still win for traditional web apps with one server, easy logout, and tight revocation requirements.

5. Anatomy of a Login Request

Here's what a typical JWT-based login flow looks like, end to end:

┌──────────┐                         ┌──────────┐
│  Client  │                         │  Server  │
└────┬─────┘                         └────┬─────┘
     │                                    │
     │  POST /login  {user, password}     │
     │ ──────────────────────────────────>│
     │                                    │ verify creds
     │                                    │ sign JWT
     │  200 OK  {token: "eyJhbG..."}     │
     │ <──────────────────────────────────│
     │                                    │
     │  GET /api/profile                  │
     │  Authorization: Bearer eyJhbG...   │
     │ ──────────────────────────────────>│
     │                                    │ verify signature
     │                                    │ read claims
     │  200 OK  {name: "John", ...}       │
     │ <──────────────────────────────────│
     │                                    │

The server never stores the token. The client holds it (in memory or a cookie — see security below) and presents it on every request.

6. Four Security Mistakes You Must Avoid

1. Storing the token in localStorage

Any XSS bug gives an attacker localStorage.getItem("token"). Store tokens in an httpOnly, Secure, SameSite=Lax cookie instead — JavaScript can't read those, and the browser won't send them cross-site.

2. Trusting the alg header blindly

The classic alg=none attack: an attacker replaces the header with {"alg":"none"} and strips the signature. If your library accepts that, anyone can forge a valid token. Equally dangerous is the algorithm-confusion attack, where a server expecting RS256 is tricked into verifying with HS256 using the public key as a symmetric secret. Always pin the expected algorithm on the server side and never let the header dictate it.

3. Skipping TLS

A JWT in an HTTP request body is plaintext. Anyone on the network path reads it. Always serve auth endpoints over HTTPS, and reject plain-HTTP callbacks for refresh tokens.

4. Long-lived access tokens, no rotation

A token valid for 30 days with no rotation is a 30-day window for an attacker. Keep access tokens short (15–60 minutes) and pair them with refresh tokens. Rotate the refresh token on every use so a stolen one is detected quickly.

7. Decode a JWT Locally with DevToolbox

When you're debugging an API and need to peek inside a token — say, to check whether the exp claim is why auth is failing — the JWT Decoder runs entirely in your browser. Paste the token, see the header and payload decoded instantly, and the signature is verified locally so nothing leaves your machine.

Decode any JWT in one click

Header + payload parsed in your browser. No network calls. No token logging.

Open JWT Decoder →

Frequently Asked Questions

Can I store sensitive data in the JWT payload?

No. The payload is just Base64URL-encoded JSON — anyone who has the token can read it with a single line of code. The signature guarantees it wasn't tampered with, not that it's secret. Keep sensitive data on the server and look it up using an ID from the claims.

How do I revoke a JWT before it expires?

The honest answer is that pure JWTs don't support revocation well. In practice, teams do one of three things: (1) maintain a blacklist of revoked jti values in Redis, checked on every request; (2) keep access tokens very short (minutes) and rely on a refresh-token system, so a forced logout just deletes the refresh token; (3) maintain a "tokens-issued-after" timestamp per user and reject anything older on logout.

JWT vs OAuth 2.0 — what's the difference?

They live at different layers. OAuth 2.0 is an authorization framework — a protocol for delegating access ("this app can act on your behalf with this other service"). JWT is a token format — a way to encode claims inside a compact, signed string. The two often appear together: OAuth 2.0 flows frequently use JWTs as the access-token format (then called "JOSE" or "JWT bearer tokens"). JWT alone is not an authorization protocol.

Is JWT a good fit for API authentication?

Yes, especially for stateless REST or GraphQL APIs served by multiple nodes. It's the right choice when the auth service is separate from the resource servers and you don't want every request hitting a central session store. Just follow the four security practices above — short expiry, httpOnly storage, pinned algorithm, HTTPS only.

Which JWT library should I use?

Pick the one with the most downloads and the most recent commit in your language's ecosystem. A few battle-tested choices: Node.jsjsonwebtoken. PythonPyJWT. Javajjwt or nimbus-jose-jwt. Gogolang-jwt/jwt. Rubyruby-jwt. Avoid rolling your own crypto.

Wrapping Up

JWT is a small specification that punches above its weight. Three segments, a signature, and you've got a portable way to convey identity across services. Get the algorithm choice right for your architecture, treat the token like a password, and you'll avoid 95% of the JWT-related security incidents that show up in the news.

When you're staring at a 401 response and need to know what's actually inside the token, skip the CLI one-liner and use the DevToolbox JWT Decoder — it's the fastest way to check the header, claims, and signature status in one place.

Related Articles