Back to Learn
Published: June 2026By Web Util Slyce Team10 min read

JWT Explained: JSON Web Tokens Guide

A comprehensive guide to understanding JSON Web Tokens (JWTs). Learn the structure, decode them using our free tool, or do it manually with code in JavaScript, Python, and other languages.

What is a JWT?

A JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. JWTs are commonly used for authentication and authorization in web applications and APIs. According to the Postman State of API Report, JWTs are the most widely used token format in modern API authentication, adopted by over 80% of OAuth 2.0 implementations.

How JWT Authentication Works

JWT authentication follows a stateless flow that eliminates the need for server-side session storage:

  1. User logs in with credentials (username/password, OAuth provider, etc.)
  2. Server validates credentials and creates a JWT containing user identity claims (sub, name, role)
  3. Server signs the JWT with a secret key (HMAC) or private key (RSA/ECDSA)
  4. Client stores the token (localStorage, sessionStorage, or httpOnly cookie)
  5. Client sends the JWT with every subsequent request via the Authorization header: Bearer <token>
  6. Server verifies the signature and expiry, then extracts user identity from the payload — no database lookup required

This stateless approach makes JWTs ideal for microservices architectures and distributed systems where a centralized session store would be a bottleneck or single point of failure.

JWT Structure

A JWT consists of three parts separated by dots (.):

header.payload.signature

Here is a real-world example:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

1. Header

Contains metadata about the token, including the type (JWT) and signing algorithm (HS256, RS256, etc.).

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

2. Payload

Contains claims — statements about the user and additional data. Standard claims include sub, exp, iat, iss.

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

3. Signature

Used to verify the token hasn't been tampered with. Created by signing the encoded header + payload with a secret key.

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

Standard JWT Claims (Registered Claims)

RFC 7519 defines a set of registered claim names that are not mandatory but recommended for interoperable JWTs:

ClaimFull NameDescription
issIssuerIdentifies who issued the token
subSubjectIdentifies the subject of the token (usually the user ID)
audAudienceIdentifies the intended recipient of the token
expExpiration TimeUnix timestamp after which the token expires
nbfNot BeforeUnix timestamp before which the token is not valid
iatIssued AtUnix timestamp when the token was issued
jtiJWT IDUnique identifier for the token (prevents replay attacks)

Common JWT Signing Algorithms

AlgorithmTypeUse Case
HS256Symmetric (HMAC + SHA-256)Single service — same secret signs and verifies
HS384Symmetric (HMAC + SHA-384)Higher security symmetric signing
HS512Symmetric (HMAC + SHA-512)Highest security symmetric signing
RS256Asymmetric (RSA + SHA-256)Multiple services — private key signs, public key verifies
RS384Asymmetric (RSA + SHA-384)Higher security asymmetric signing
RS512Asymmetric (RSA + SHA-512)Highest security asymmetric signing
ES256Asymmetric (ECDSA + P-256)Smaller signatures, mobile-friendly
ES384Asymmetric (ECDSA + P-384)Higher security elliptic curve
ES512Asymmetric (ECDSA + P-521)Highest security elliptic curve
EdDSAAsymmetric (Edwards Curve)Modern, fast, constant-time signing

Method 1: Decode JWT Using Our Free Online Tool

The easiest way to inspect a JWT is using our browser-based JWT Decoder tool. It runs entirely in your browser — no data is sent to any server:

  1. Go to the JWT Decoder tool
  2. Paste your JWT token into the input field
  3. View the decoded header, payload, and signature instantly
  4. Copy individual claims or the full decoded JSON

Method 2: Decode JWT in JavaScript

You can decode JWT tokens programmatically in JavaScript using the built-in atob() function:

function decodeJWT(token) {
  const parts = token.split(".");
  if (parts.length !== 3) throw new Error("Invalid JWT");

  const header = JSON.parse(atob(parts[0]));
  const payload = JSON.parse(atob(parts[1]));
  const signature = parts[2];

  return { header, payload, signature };
}

// Example usage
const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" +
  ".eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ" +
  ".SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
const decoded = decodeJWT(token);
console.log("Header:", decoded.header);
console.log("Payload:", decoded.payload);

Method 3: Decode JWT in Python

Python requires handling base64 padding manually since the JWT format uses unpadded base64url encoding:

import json
import base64

def decode_jwt(token):
    parts = token.split(".")
    if len(parts) != 3:
        raise ValueError("Invalid JWT")

    def decode_part(part):
        # Add padding: JWT uses unpadded base64url
        padding = 4 - len(part) % 4
        if padding != 4:
            part += "=" * padding
        return json.loads(base64.urlsafe_b64decode(part))

    header = decode_part(parts[0])
    payload = decode_part(parts[1])
    signature = parts[2]
    return header, payload, signature

token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
header, payload, signature = decode_jwt(token)
print(json.dumps(payload, indent=2))

Method 4: Decode JWT in Node.js

In Node.js, use the built-in Buffer class which handles base64url natively:

function decodeJWT(token) {
  const [headerB64, payloadB64] = token.split(".");

  const decode = (str) =>
    JSON.parse(Buffer.from(str, "base64url").toString("utf-8"));

  return {
    header: decode(headerB64),
    payload: decode(payloadB64),
  };
}

const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";
console.log(decodeJWT(jwt));

How JWT Verification Works

Decoding a JWT only reads the base64-encoded data. Verification ensures the token has not been tampered with. The process differs by algorithm type:

Symmetric (HS256)

The server uses the same secret key to recompute the HMAC signature and compare it with the token's signature. If they match, the token is valid. This means only services that share the same secret can issue and verify tokens.

Asymmetric (RS256 / ES256)

The issuer signs with a private key. Any service with the corresponding public key can verify the signature without being able to issue new tokens. This is the preferred approach for microservices and third-party integrations.

JWT Security Best Practices

Following OWASP guidelines and industry standards, here are critical security practices for JWT implementation:

Never trust the payload

JWT payloads are base64-encoded, not encrypted. Anyone can decode them. Never store sensitive data like passwords or credit card numbers in a JWT payload.

Use HTTPS exclusively

Without HTTPS, JWTs can be intercepted during transmission (man-in-the-middle). Always serve your API and client over TLS.

Validate the signature

Always verify the signature server-side before trusting a JWT. For asymmetric algorithms, verify against the correct public key.

Set short expiration

Use the exp claim with short expiry windows (15-60 minutes). Implement refresh token rotation for long-lived sessions.

Validate all claims

Always check exp, nbf, iss, and aud claims server-side. Reject tokens with invalid or expired claims.

Rotate signing keys

Regularly rotate your signing keys. Use JWKS endpoints for dynamic key distribution in microservice environments.

Common JWT Attacks and Mitigations

AttackDescriptionMitigation
None algorithm attackAttacker changes the alg header to 'none' to bypass signature verificationReject tokens with alg: 'none'. Always verify signatures.
Algorithm confusionAttacker uses a public key as an HMAC secret to forge tokens on RS256-configured endpointsUse separate validation logic per algorithm. Validate alg against an allowlist.
Key confusionAttacker registers their own JWKS endpoint to inject malicious keysMaintain an allowlist of trusted JWKS URLs. Validate key IDs.
Token replayStolen token reused to impersonate a userUse short exp times. Implement jti claim with a server-side denylist. Use refresh token rotation.
Timing attackAttacker measures response times to infer signature validationUse constant-time comparison functions for signature verification.
Information disclosureSensitive data leaked through decoded payloadNever store secrets in the payload. Use JWE (JSON Web Encryption) if confidentiality is required.

JWTs vs Alternative Authentication Approaches

ApproachStateful?ScalabilityUse Case
JWT (Bearer Token)No (self-contained)Excellent — no shared session store neededMicroservices, SPAs, mobile APIs
Session cookieYes (server-side)Requires shared session store (Redis, DB)Traditional server-rendered web apps
API KeyNo (static)Good — simple but less secureService-to-service, third-party integrations
SAML assertionNo (XML token)Good — XML-based, enterprise-focusedEnterprise SSO, legacy systems
OAuth 2.0 + JWTNo (composite)Excellent — standardized delegationThird-party auth, delegated access

Real-World JWT Usage Example

Here is a complete example of how a React application authenticates with a JWT-based API:

// Login: authenticate and store the JWT
async function login(email, password) {
  const res = await fetch("https://api.example.com/auth/login", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ email, password }),
  });

  const { token } = await res.json();

  // Store token (httpOnly cookie is more secure)
  localStorage.setItem("jwt", token);
  return token;
}

// Authenticated request: send JWT in Authorization header
async function fetchUserProfile() {
  const token = localStorage.getItem("jwt");

  const res = await fetch("https://api.example.com/users/me", {
    headers: {
      "Authorization": `Bearer ${token}`,
    },
  });

  if (res.status === 401) {
    // Token expired — redirect to login
    window.location.href = "/login";
    return null;
  }

  return res.json();
}

Frequently Asked Questions

What is the difference between JWT encoding and encryption?

JWT uses base64url encoding, which is reversible — anyone can decode the payload without a key. Encryption (JWE) makes the payload unreadable without a decryption key. Most JWTs are encoded, not encrypted.

Can a JWT be revoked before it expires?

JWTs are stateless, so they cannot be revoked server-side without maintaining a denylist. For scenarios requiring immediate revocation, use short expiration times combined with refresh token rotation, or maintain a token denylist in Redis.

What is the maximum size of a JWT?

There is no hard limit in the specification, but most HTTP servers and proxies limit header sizes to 8-16 KB. Since JWTs are sent in the Authorization header, keeping them under 4 KB is recommended for performance.

Should I store JWT in localStorage or cookies?

Cookies (with httpOnly and Secure flags) are more secure because they prevent JavaScript access, mitigating XSS attacks. localStorage is simpler but vulnerable to XSS. The best practice is using httpOnly cookies with CSRF protection.

What is a refresh token and why do I need one?

A refresh token is a long-lived credential used to obtain new access tokens without requiring the user to re-authenticate. This allows short-lived JWTs (15-60 minutes) while maintaining persistent sessions.