JWT Structure Explained: Header, Payload, and Signature

May 27, 20266 min read

What Is a JWT?

A JSON Web Token (JWT) is a compact, URL-safe string that carries information between two parties. Defined in RFC 7519, JWTs are the backbone of modern authentication and authorization flows.

A JWT looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

That long string is three separate parts joined by dots. Each part serves a distinct purpose.

The Three Parts of a JWT

Every JWT follows the same structure:

header.payload.signature
PartEncoded WithPurpose
HeaderBase64URLIdentifies the token type and signing algorithm
PayloadBase64URLCarries the actual data (claims)
SignatureBase64URLEnsures the token has not been tampered with

Let's break down each one.

The Header

The header tells the recipient how to process the token. It contains two fields:

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

Header Claims

ClaimDescriptionCommon Values
algAlgorithm used to sign the tokenHS256, RS256, ES256
typToken typeJWT

alg (Algorithm) — This is the most critical field. It tells the verifier which algorithm was used to create the signature. Common choices include:

  • HS256: HMAC with SHA-256 (symmetric — same secret for signing and verification)
  • RS256: RSA with SHA-256 (asymmetric — private key signs, public key verifies)
  • ES256: ECDSA with SHA-256 (asymmetric — smaller signatures than RSA)

typ (Type) — Almost always set to JWT. Some implementations omit it, but including it is a best practice for clarity.

The header is then Base64URL-encoded:

Base64URL({"alg":"HS256","typ":"JWT"})
→ eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

The Payload

The payload contains the claims — statements about an entity (typically a user) and additional metadata. Claims come in three categories:

Registered Claims (Standard)

These are reserved claim names defined by RFC 7519:

ClaimFull NameDescription
issIssuerWho issued the token
subSubjectThe principal the token is about (usually a user ID)
audAudienceIntended recipient of the token
expExpirationTime after which the token is no longer valid
nbfNot BeforeTime before which the token is not valid
iatIssued AtTime the token was issued
jtiJWT IDUnique identifier for the token (prevents replay)

All time-based claims use Unix timestamps (seconds since epoch).

Example Payload

{
  "sub": "1234567890",
  "name": "John Doe",
  "role": "admin",
  "iat": 1516239022,
  "exp": 1516242622
}
  • sub identifies the user
  • name and role are custom claims
  • iat records when the token was issued
  • exp sets the token to expire 1 hour after issuance

Public and Private Claims

  • Public claims: Registered in the IANA JSON Web Token Registry to avoid collisions (e.g., email, roles)
  • Private claims: Custom names agreed upon by the parties (e.g., department, plan_id)

Always avoid collision by choosing claim names that won't conflict with registered claims.

Important: Payload Is Not Encrypted

The payload is Base64URL-encoded, not encrypted. Anyone who intercepts the token can read its contents. Never store sensitive data (passwords, API keys, SSNs) in a JWT payload.

The Signature

The signature verifies that the token hasn't been altered. It is computed from the header and payload:

HMAC (HS256) Example

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

RSA (RS256) Example

RSASHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  privateKey
)

The signature ensures integrity — if anyone changes even a single character in the header or payload, the signature will no longer match.

Verification Flow

1. Split the JWT by dots → [header, payload, signature]
2. Recompute the signature using header + payload + secret/key
3. Compare the recomputed signature with the received signature
4. Match → token is valid and untampered
5. No match → token has been modified → reject

Base64URL Encoding Explained

JWTs use Base64URL encoding, not standard Base64. The differences matter:

Standard Base64Base64URLReason
+-+ is interpreted as space in URLs
/_/ is a URL path separator
= paddingRemoved= is percent-encoded in URLs
Output not URL-safeOutput is URL-safeJWTs travel in URLs and cookies

Manual Decode Example

function decodeBase64Url(str) {
  // Restore standard Base64
  let base64 = str.replace(/-/g, '+').replace(/_/g, '/');
  // Restore padding
  while (base64.length % 4) base64 += '=';
  return JSON.parse(atob(base64));
}

const header = decodeBase64Url('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9');
// { alg: "HS256", typ: "JWT" }

How the Three Parts Work Together

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│    HEADER    │  .  │   PAYLOAD    │  .  │  SIGNATURE   │
│  (algorithm) │     │   (claims)   │     │  (proof)     │
└──────────────┘     └──────────────┘     └──────────────┘
       ↓                    ↓                     ↓
  Base64URL           Base64URL            HMAC/RSA of
  encoded             encoded              header.payload
  • The header tells the verifier which algorithm to use
  • The payload carries the claims the application needs
  • The signature proves the first two parts were issued by a trusted source

If any part changes, the signature breaks — and the token is rejected.

Common JWT Myths

Myth: JWTs Are Encrypted

Reality: JWTs are encoded, not encrypted. Anyone can decode the header and payload without a secret key. Use JWE (JSON Web Encryption) if you need confidentiality.

Myth: You Cannot Trust a JWT Without a Database Lookup

Reality: With asymmetric algorithms (RS256), the verifier only needs the public key — no database call needed. This is the key advantage of self-contained tokens.

Myth: Longer Secrets Mean Better Security

Reality: With HMAC, yes — use at least 256-bit secrets. With RSA or ECDSA, key length matters differently. Follow the algorithm's best practices, not guesswork.

Try It Yourself

Want to see the structure of a real JWT? Paste any token into our free JWT Decoder to instantly inspect its header, payload, and claims — all processed securely in your browser with no data sent to any server.