JWT Structure — Header, Payload, and Signature Explained in Detail
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 tokensub— Subject (user ID)aud— Audience (intended recipient)exp— Expiration timenbf— Not Beforeiat— Issued Atjti— JWT ID (unique identifier)
Public Claims (defined by IANA or custom):
name,email,picture— user profileroles,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
- What are the three parts of a JWT?
- What information is stored in the header?
- What is the difference between registered and private claims?
- How is the JWT signature computed?
- Can the payload be read without knowing the secret?
Answers:
- Header (algorithm, type), Payload (claims), Signature (cryptographic verification).
- The header contains the signing algorithm (
alg) and token type (typ), plus optional fields likekidandjku. - Registered claims are standardized (exp, iss, sub, aud). Private claims are custom between parties.
HMACSHA256(base64url(header) + '.' + base64url(payload), secret)for HS256. Other algorithms use different computations.- 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
{{< 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}))'" >}}
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