Skip to content

JWT Structure — Header, Payload, and Signature Explained in Detail

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about JWT Structure. We cover key concepts, practical examples, and best practices to help you master this topic.

A JWT consists of three base64url-encoded segments — header, payload, and signature — separated by dots, each serving a specific role in the token's security and functionality.

What You'll Learn

The exact structure of each JWT segment, standard claims, how the signature is computed, and how to decode JWTs for debugging.

Why It Matters

Understanding JWT structure is essential for debugging authentication issues, implementing custom claims, and identifying security vulnerabilities. Many JWT attacks exploit misunderstanding of the structure.

Real-World Use

When a JWT-based API returns 401, you decode the token to inspect the header (algorithm), payload (expiry, issuer), and verify the signature to determine the root cause.

flowchart LR
    subgraph "JWT Structure"
        A["Header\n{'alg':'HS256','typ':'JWT'}"]
        B["Payload\n{'sub':'123','name':'Alice'}"]
        C["Signature\nHMACSHA256(base64(header)+'.'+base64(payload), secret)"]
    end
    A -->|"base64url"| D["eyJhbGciOiJIUzI1NiJ9"]
    B -->|"base64url"| E["eyJzdWIiOiIxMjMifQ"]
    C -->|"base64url"| F["8X4x8F0P3k"]
    style A fill:#dbeafe,stroke:#2563eb
    style B fill:#fef3c7,stroke:#d97706
    style C fill:#dcfce7,stroke:#16a34a

The Header

The header typically contains two fields:

{
  "alg": "HS256",
  "typ": "JWT"
}
  • alg — Signing algorithm (HS256, RS256, ES256, or "none")
  • typ — Token type (usually "JWT")
  • kid — Key ID (optional, for key rotation)
  • jku — JWK Set URL (optional, where to find the signing key)

The Payload

The payload contains claims. There are three types:

Registered Claims (standardized):

  • iss — Issuer of the token
  • sub — Subject (user ID)
  • aud — Audience (intended recipient)
  • exp — Expiration time
  • nbf — Not Before
  • iat — Issued At
  • jti — JWT ID (unique identifier)

Public Claims (defined by IANA or custom):

  • name, email, picture — user profile
  • roles, permissions — authorization

Private Claims (custom between parties):

  • tenant_id, feature_flags, session_id

The Signature

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

The signature prevents tampering. If anyone modifies the header or payload, the signature verification fails.

Code Example: Decoding a JWT Without Verification

import base64
import json

token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"

# Split the token
header_b64, payload_b64, signature_b64 = token.split(".")

# Add padding for base64 decoding
def b64decode(data):
    padding = 4 - len(data) % 4
    if padding != 4:
        data += "=" * padding
    return base64.urlsafe_b64decode(data)

header = json.loads(b64decode(header_b64))
payload = json.loads(b64decode(payload_b64))

print(f"Header: {json.dumps(header, indent=2)}")
print(f"Payload: {json.dumps(payload, indent=2)}")
print(f"Signature (hex): {base64.urlsafe_b64decode(signature_b64 + '==').hex()[:40]}...")

Expected output:

Header: {
  "alg": "HS256",
  "typ": "JWT"
}
Payload: {
  "sub": "1234567890",
  "name": "Alice",
  "iat": 1516239022
}
Signature (hex): 1f92f2c7044948cf8a285d913e1fc...

Common Mistakes

1. Confusing Base64 Encoding

JWT uses base64url encoding (no padding, - instead of +, _ instead of /). Standard base64 requires padding.

2. Decoding Without Verifying

Decoding the payload does not verify the token is authentic. Anyone can decode. Always verify the signature.

3. Putting Too Much Data in Payload

JWT is sent with every request. Large payloads increase bandwidth. Store minimal data in the token.

4. Ignoring Reserved Claim Names

Creating a custom claim named exp or sub overrides the standard claim. Use unique names for custom claims.

5. Missing Required Claims

If your application requires aud or iss, validate them explicitly. JWT libraries do not validate these by default.

Practice Questions

  1. What are the three parts of a JWT?
  2. What information is stored in the header?
  3. What is the difference between registered and private claims?
  4. How is the JWT signature computed?
  5. Can the payload be read without knowing the secret?

Answers:

  1. Header (algorithm, type), Payload (claims), Signature (cryptographic verification).
  2. The header contains the signing algorithm (alg) and token type (typ), plus optional fields like kid and jku.
  3. Registered claims are standardized (exp, iss, sub, aud). Private claims are custom between parties.
  4. HMACSHA256(base64url(header) + '.' + base64url(payload), secret) for HS256. Other algorithms use different computations.
  5. Yes. The payload is base64-encoded, not encrypted. Anyone with the token can decode and read the claims.

Challenge: Create a Python function that takes a JWT, splits it into its three parts, decodes each part, and returns a dictionary with the decoded header, payload, and signature (in hex). Handle padding correctly.

FAQ

What is the difference between base64 and base64url?

Base64url uses - instead of +, _ instead of /, and omits padding (=). JWT uses base64url for URL safety.

Can I add custom fields to the header?

Yes, but avoid custom header fields unless necessary. The kid (key ID) and typ fields are standard extensions.

What happens if I modify the payload?

The signature verification will fail because the signature was computed over the original (header + '.' + payload).

{{< faq "How do I inspect a JWT quickly?" "Use jwt.io or run: python3 -c 'import jwt; print(jwt.decode(token, options={"verify_signature": False}))'" >}}

What is maximum payload size?

There is no JWT-specified limit, but HTTP servers typically limit headers to 8-16KB. Keep payloads under 1KB.

Mini Project

Build a JWT inspector tool in Python that accepts a JWT, splits and decodes the three parts, validates the signature (if you provide the secret), and pretty-prints the header and payload.

What's Next

Now learn about JWT Signing Algorithms — HS256, RS256, and ES256 and when to use each.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro