Cybersecurity

JWT Explained: Header, Payload and Signature

RAG AI Agents Cybersecurity Encryption Authentication JWT Microservices Rust Passwords Hashing TLS HTTPS
1,456 words Includes Code
Key Takeaway: A JWT is three Base64URL-encoded parts separated by dots: header (algorithm), payload (claims), and signature (integrity). The signature proves the token hasn't been tampered with — but JWTs are signed, not encrypted. Anyone can read the payload. Never put secrets in a JWT.

JWT Explained: Header, Payload and Signature

JSON Web Tokens (JWTs) are everywhere: login systems, API authentication, microservice communication, and single sign-on. Yet most developers use JWTs without understanding how they actually work — leading to security vulnerabilities, broken authentication, and debugging nightmares.

This article explains the JWT structure, shows exactly how verification works, walks through synthetic examples you can decode yourself, and covers the most common mistakes developers make with JWTs.

JWT token structure showing three Base64URL-encoded parts Header Payload and Signature with decoded JSON examples
A JWT is three parts separated by dots. Each part is Base64URL-encoded JSON. The signature proves integrity.

What Is a JWT?

A JSON Web Token (RFC 7519) is a compact, self-contained token that represents claims between two parties. Its most common use is authentication: the server issues a JWT after login, and the client includes it in every subsequent request.

A complete JWT looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMTIzNDUiLCJuYW1lIjoiQWxpY2UiLCJyb2xlIjoiYWRtaW4iLCJpYXQiOjE3NTYzMDA4MDAsImV4cCI6MTc1NjMwNDQwMH0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Split on "." → 3 parts:
Part 1 (Header):     eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Part 2 (Payload):    eyJ1c2VyX2lkIjoiMTIzNDUiLCJuYW1lIjoiQWxpY2Ui...
Part 3 (Signature):  SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Decode any JWT: Use the JWT Decoder to paste this token and see the decoded header, payload, and signature instantly. The JWT Structure Analyzer breaks down every field.

Part 1: The Header

The header specifies the signing algorithm and token type:

Base64URL decode the header:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
↓ decode
{
  "alg": "HS256",    ← Signing algorithm (HMAC-SHA256)
  "typ": "JWT"       ← Token type
}
Field Required Description
alg Yes Signing algorithm: HS256, HS384, HS512, RS256, ES256
typ Yes Token type: always "JWT"
kid No Key ID — identifies which key was used to sign

Try it: Use the JWT Header Decoder to decode the header of any JWT token.

Part 2: The Payload

The payload contains the claims — statements about the user and the token:

Base64URL decode the payload:

eyJ1c2VyX2lkIjoiMTIzNDUiLCJuYW1lIjoiQWxpY2UiLCJyb2xlIjoiYWRtaW4iLCJpYXQiOjE3NTYzMDA4MDAsImV4cCI6MTc1NjMwNDQwMH0
↓ decode
{
  "user_id": "12345",
  "name": "Alice",
  "role": "admin",
  "iat": 1756300800,
  "exp": 1756304400
}

Standard Claims (Registered)

Claim Name Example Description
iss Issuer "auth.bestwordz.com" Who created the token
sub Subject "12345" Who the token is about
aud Audience "api.bestwordz.com" Who should accept this token
exp Expiration 1756304400 When the token expires (Unix timestamp)
iat Issued At 1756300800 When the token was created
nbf Not Before 1756300800 When the token becomes valid

Decode claims: Use the JWT Payload Decoder to see all claims. The JWT Claims Viewer provides a detailed breakdown. Convert timestamps with the JWT Timestamp Converter.

Custom Claims

You can add any custom claims you need:

{
  "user_id": "12345",
  "name": "Alice",
  "email": "alice@example.com",      ← custom
  "role": "admin",                    ← custom
  "permissions": ["read", "write"],   ← custom
  "org_id": "org_42",                 ← custom
  "iat": 1756300800,
  "exp": 1756304400
}
⚠️ Critical: The payload is Base64URL-encoded, NOT encrypted. Anyone who intercepts the token can decode the payload and read every claim. Never put passwords, API keys, or sensitive personal data in a JWT payload.

Part 3: The Signature

The signature is the security mechanism. It proves two things:

  1. Integrity: The token hasn't been modified since it was issued
  2. Authenticity: It was created by someone who knows the secret key
How the signature is computed:

For HMAC (HS256, HS384, HS512):
  signature = HMAC-SHA256(
    base64url(header) + "." + base64url(payload),
    secret_key
  )

For RSA (RS256, RS384, RS512):
  signature = RSA-SHA256(
    base64url(header) + "." + base64url(payload),
    private_key
  )

For ECDSA (ES256, ES384, ES512):
  signature = ECDSA-SHA256(
    base64url(header) + "." + base64url(payload),
    private_key
  )

Try HMAC: Use the HMAC-SHA256 Generator to compute HMAC signatures. See how the HMAC Demonstrator works step by step. For RSA signatures, try the RSA-PSS Sign tool.

How Verification Works

When a server receives a JWT, it follows this verification process:

JWT verification flow showing receive split decode header verify signature check claims and grant or reject access
The server recomputes the signature and compares it to the token's signature. Any mismatch means the token was tampered with.
Verification pseudocode:

function verifyJWT(token, secret_key):
    # 1. Split the token
    parts = token.split(".")
    if parts.length != 3:
        return REJECT  # Malformed token

    # 2. Recompute the expected signature
    signing_input = parts[0] + "." + parts[1]
    expected_sig = HMAC_SHA256(signing_input, secret_key)

    # 3. Compare signatures (constant-time comparison!)
    actual_sig = base64url_decode(parts[2])
    if not constant_time_compare(expected_sig, actual_sig):
        return REJECT  # Invalid signature

    # 4. Decode and validate claims
    payload = base64url_decode(parts[1])
    if payload.exp < current_time():
        return REJECT  # Token expired

    if payload.iss != expected_issuer:
        return REJECT  # Wrong issuer

    # 5. All checks passed
    return ACCEPT

JWT Algorithm Comparison

Algorithm Type Key Use Case
HS256 Symmetric Shared secret Same server issues and verifies
RS256 Asymmetric RSA private/public pair Multiple services verify
ES256 Asymmetric ECDSA private/public pair Smaller signatures, mobile
none None No key NEVER use in production

Common JWT Mistakes

❌ Mistake 1: Putting Secrets in the Payload

Dangerous:

{"user": "alice", "password": "secret123", "ssn": "123-45-6789"}

The payload is only Base64URL-encoded. Anyone can decode it. JWTs provide integrity, not confidentiality.

❌ Mistake 2: Not Validating the Algorithm

Dangerous:

An attacker changes the header to {"alg": "none"}. If the server doesn't validate the algorithm, it accepts the token without any signature check.

Fix: Always verify the algorithm matches what you expect before checking the signature.

❌ Mistake 3: Using a Weak Secret

Dangerous:

secret = "mysecret"

Short or predictable secrets can be brute-forced. Use at least 256 bits (32 bytes) of cryptographically random data.

❌ Mistake 4: Not Checking Expiration

Dangerous:

If the server never checks the exp claim, a stolen token works forever.

Fix: Always validate exp, iat, and nbf. Use short expiration times (15 min for access tokens).

❌ Mistake 5: Using JWTs as Session Tokens

Risky:

JWTs cannot be revoked. Once issued, a valid JWT works until it expires. If a user logs out or their account is compromised, you cannot invalidate their existing JWTs.

Fix: Use short-lived access tokens + refresh token rotation, or maintain a token blocklist.

HS256 vs RS256 vs ES256

Property HS256 RS256 ES256
Key type Shared secret RSA key pair ECDSA key pair
Speed Fastest Slowest Fast
Signature size 32 bytes 256 bytes 64 bytes
Key distribution Hard (shared) Easy (public key) Easy (public key)
Best for Single server Microservices Mobile, IoT

Generate keys: Create RSA keys with the RSA Key Pair Generator. Create ECDSA keys with the ECDSA Key Generator. Sign with RSA-PSS Sign and verify with RSA-PSS Verify.

JWT Best Practices Checklist

  • ✅ Always validate the signature before trusting any claims
  • ✅ Validate the algorithm in the header matches your expected algorithm
  • ✅ Check the exp claim — reject expired tokens
  • ✅ Check iss and aud claims
  • ✅ Use short expiration (15 min for access tokens)
  • ✅ Use at least 256-bit secrets for HS256
  • ✅ Never put secrets or sensitive data in the payload
  • ✅ Use RS256 or ES256 when multiple services need to verify
  • ✅ Implement token revocation for logout/compromise
  • ✅ Use HTTPS to prevent token interception
  • ✅ Store tokens in httpOnly cookies or secure storage, not localStorage

Try It Yourself: BestWordz JWT Tools

Decode & Inspect

Sign & Verify

Key Generation

Conclusion

JWTs are a powerful authentication mechanism, but they're only as secure as their implementation. The three-part structure — header, payload, signature — is simple to understand but easy to misuse.

The most critical thing to remember: JWTs are signed, not encrypted. The payload is readable by anyone. The signature only proves integrity and authenticity. Use JWTs for stateless authentication where the claims are not sensitive, and always validate every field before trusting the token.

Related BestWordz Resources

Explore all BestWordz Cybersecurity Tools →

💬 Discuss on BestWordz Community

Join the conversation about RAG, AI Agents, Cybersecurity on the BestWordz Community forum.

Visit Forum →