Back to blog

JWT Explained: What Is a JSON Web Token? Structure & Examples

What Is JWT?

JWT stands for JSON Web Token. It is an open, industry-standard format (RFC 7519) used to transmit claims—pieces of information—between two parties as a compact, self-contained JSON object.

In simple terms, a JWT is a signed string commonly used to carry authentication and authorization data between an application and an API. A JWT has three parts—header, payload, and signature—separated by dots. The payload is encoded, not encrypted, so it must not contain passwords or other secrets.

A standard JWT consists of three dot-separated sections: header.payload.signature. The header and payload are Base64Url-encoded (not encrypted), allowing anyone with the token to inspect its contents. The signature allows the recipient to cryptographically verify that the token was issued by a trusted party and has not been altered in transit.

Crucially, JWT is a token format, not an authentication protocol. Protocols such as OAuth 2.0 and OpenID Connect (OIDC) frequently rely on JWTs to represent ID tokens or access tokens, but the JWT standard itself merely defines how identity claims, timestamps, and cryptographic signatures are packaged and formatted.


Anatomy of a JWT: The Three-Part Structure

In its serialized format, a JWT is represented as a single URL-safe string containing three distinct segments separated by periods (.):

xxxxx.yyyyy.zzzzz
[Header].[Payload].[Signature]

Here is a practical example of a complete, signed JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMyIsIm5hbWUiOiJBbGljZSBKb2huc29uIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzI2Mzg3ODAwLCJleHAiOjE3MjYzOTE0MDB9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

When an application receives this token, it splits the string at each period to evaluate each component separately.


Part 1: The JWT Header

The Header is a JSON object that provides metadata about the token itself, primarily declaring the token type and the cryptographic algorithm used to secure the signature.

Encoded Header

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Decoded Header

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

The header typically contains two primary parameters:

  • typ: The type of token, which is almost always set to "JWT".
  • alg: The cryptographic signing algorithm. Common algorithms include:
    • HS256 (HMAC with SHA-256): A symmetric algorithm using a single shared secret key for both signing and verification.
    • RS256 (RSA Signature with SHA-256): An asymmetric algorithm using a private key to sign the token and a public key to verify it.
    • ES256 (ECDSA with P-256 and SHA-256): An asymmetric elliptic-curve algorithm producing smaller signatures than RSA with equivalent cryptographic strength.

Part 2: The JWT Payload (Claims)

The Payload contains the claims—statements made about the entity (typically an authenticated user) along with operational metadata.

Encoded Payload

eyJzdWIiOiJ1c2VyXzEyMyIsIm5hbWUiOiJBbGljZSBKb2huc29uIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzI2Mzg3ODAwLCJleHAiOjE3MjYzOTE0MDB9

Decoded Payload

{
  "sub": "user_123",
  "name": "Alice Johnson",
  "role": "admin",
  "iat": 1726387800,
  "exp": 1726391400
}

RFC 7519 categorizes JWT claims into three classes: registered, public, and private.

1. Registered Claims

These are standardized, optional claims defined by the specification to ensure interoperability across diverse services and libraries. A token is not required to contain all of them, but when used, they follow predefined semantics:

ClaimNameDescription
issIssuerIdentifies the principal that issued the JWT (e.g., https://auth.example.com).
subSubjectIdentifies the subject of the token (typically a unique user or service ID).
audAudienceIdentifies the recipients that the JWT is intended for (e.g., https://api.example.com).
expExpiration TimeA Unix timestamp marking when the token ceases to be valid.
nbfNot BeforeA Unix timestamp identifying the time before which the token must not be accepted.
iatIssued AtA Unix timestamp indicating when the JWT was generated.
jtiJWT IDA unique case-sensitive identifier for the token, commonly used to prevent replay attacks.

2. Public Claims

Public claims are custom fields defined by developers that are either registered in the IANA JSON Web Token Claims Registry or given collision-resistant names (such as a URI or namespaced string) to avoid naming clashes across independent systems.

3. Private (Custom) Claims

Private claims are custom fields agreed upon by the producer and consumer of the token to convey application-specific information. For example, role: "admin", organization_id: "org_789", or email: "alice@example.com".

Important Security Rule: Because the payload is Base64Url-encoded rather than encrypted, anyone who holds the token can view every claim. Never place passwords, API secrets, private encryption keys, or sensitive personally identifiable information (PII) inside a JWT payload.


Part 3: The JWT Signature

The Signature is the cryptographic mechanism that guarantees the token's integrity and authenticity. It prevents attackers from modifying the header or payload without detection.

To generate the signature for an HS256 token, the application concatenates the Base64Url-encoded header and payload with a period, then hashes them using HMAC-SHA256 and a secret key:

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

For asymmetric algorithms like RS256 or ES256, the signature is produced using the issuer's private key.

When an API receives a JWT, it performs the following verification check:

  1. Takes the incoming header and payload strings.
  2. Recomputes the expected signature using the trusted secret or public key.
  3. Compares the computed signature against the signature attached to the token.

If an attacker changes "role": "user" to "role": "admin" in the payload, the recomputed signature will not match the attached signature, and the receiving server will reject the token immediately.


JWT Is Not Encryption: JWS vs JWE

A widespread misconception among developers is that JWTs protect the secrecy of their contents.

A standard signed JWT is known as a JWS (JSON Web Signature). A JWS is digitally signed, but it is NOT encrypted.

Its header and payload are simply converted into ASCII characters using Base64Url encoding. Anyone who intercepts the token or inspects it in their browser can decode the payload in milliseconds. The signature protects integrity (confirming the token has not been altered) and authenticity (confirming who issued it), but provides zero confidentiality.

To understand why Base64Url encoding does not protect data secrecy, read our detailed guide on why Base64 encoding is not encryption.

┌────────────────────────────────────────────────────────┐
│                      JWT FORMATS                       │
├──────────────────────────┬─────────────────────────────┤
│  JWS (Signed Token)      │  JWE (Encrypted Token)      │
├──────────────────────────┼─────────────────────────────┤
│ • Header + Payload readable│ • Payload encrypted with key │
│ • Validates integrity     │ • Guarantees confidentiality │
│ • Standard web auth use  │ • More complex & larger size│
└──────────────────────────┴─────────────────────────────┘

If your architecture strictly requires transmitting sensitive data that cannot be visible to the client or intermediate proxies, you must use JWE (JSON Web Encryption), defined in RFC 7516, which encrypts the payload using cryptographic algorithms like AES-GCM.


JWT Decoding vs JWT Verification

Understanding the difference between decoding and verifying a JWT is essential for writing secure code.

┌───────────────────────────────────────────────────────────────────────────┐
│ DECODING: Base64Url String ───► JSON Objects (Anyone can do this)         │
│                                                                           │
│ VERIFICATION: Token + Secret/Public Key ───► Cryptographic Authenticity   │
│                                              + Expiry Check Pass/Fail     │
└───────────────────────────────────────────────────────────────────────────┘

Decoding a JWT

  • What it does: Parses the Base64Url strings representing the header and payload back into readable JSON.
  • Requirements: Requires no keys, passwords, or cryptography.
  • Trust level: Zero trust. A decoded token tells you what claims the token claims to hold, but gives no proof that the token was issued by a legitimate server or that the claims have not been tampered with.

Verifying a JWT

  • What it does: Mathematically checks the cryptographic signature using the designated secret (HMAC) or public key (RSA/ECDSA), validates that the token has not expired (exp), and ensures the issuer (iss) and audience (aud) match expectations.
  • Requirements: Requires the authentic server secret or public certificate.
  • Trust level: High trust. Confirms the token is authentic, untampered, and currently active.

⚠️ Critical Rule: A decoded JWT is NOT automatically valid. Never authenticate a user or grant permissions simply because a token successfully decoded into JSON. Always verify the signature on your backend server before trusting any payload values.

To inspect the header and payload of a token during development, you can safely use our browser-based JWT Decoder, which parses tokens locally on your machine without transmitting them across the internet.


JWT vs Session-Based Authentication

Web applications typically handle user identity using either stateful server sessions or stateless JSON Web Tokens. Neither approach is universally superior; each solves different architectural problems.

AspectJWT-Based AuthenticationServer-Side Sessions
State StorageStateless: User data and permissions travel inside the token held by the client.Stateful: Session data lives in a server-side store (Redis, PostgreSQL, memory).
Horizontal ScalingHighly scalable across distributed microservices; services verify signatures locally.Requires shared centralized session storage (e.g., Redis cluster) or sticky load balancing.
Token RevocationDifficult before expiration unless maintaining a secondary revocation list or blocklist.Trivial; deleting the session entry from the server database immediately logs the user out.
Payload SizeLarger (hundreds of bytes to a few kilobytes) sent on every HTTP request.Minimal; only an opaque session identifier (32–64 characters) is sent via cookies.
Security SurfaceVulnerable to token leakage if stored in insecure client-side web storage.Vulnerable to Cross-Site Request Forgery (CSRF) if cookies are not properly secured.

When to Prefer JWTs

  • Microservice architectures where services need to verify requests independently without querying a central database.
  • Single Sign-On (SSO) systems and mobile application APIs where cookies are inconvenient.
  • Cross-domain API integrations using protocols like OAuth 2.0 and OpenID Connect.

When to Prefer Sessions

  • Monolithic web applications where server-side rendering is dominant.
  • High-security applications requiring instantaneous, server-enforced session revocation (e.g., banking or healthcare).
  • Applications with large user profile states that would bloat HTTP header sizes if encoded in every request.

Access Token vs Refresh Token

Because stateless JWTs cannot be easily revoked before they expire, secure production architectures avoid issuing long-lived access tokens. Instead, they implement a two-token pattern:

┌────────┐                               ┌───────────────┐
│ Client │ ── 1. Present Credentials ──► │  Auth Server  │
│        │ ◄── 2. Return Access + Refresh │               │
│        │                               └───────────────┘
│        │                               ┌───────────────┐
│        │ ── 3. Call API (Access Token)►│  Resource API │
│        │ ◄── 4. Protected Data / 401 ─ │ (Local Verify)│
│        │                               └───────────────┘
│        │                               ┌───────────────┐
│        │ ── 5. Request New Access ───► │  Auth Server  │
│        │ ◄── 6. New Access Token ───── │ (Rotate Check)│
└────────┘                               └───────────────┘

Access Token

  • Lifespan: Short (typically 5 to 15 minutes).
  • Purpose: Sent in the Authorization: Bearer <token> header with every API call to access protected endpoints.
  • Format: Frequently formatted as a signed JWT so downstream microservices can verify permissions locally without database lookups.
  • Important: Not all access tokens must be JWTs; some authorization servers issue opaque reference strings.

Refresh Token

  • Lifespan: Long (days, weeks, or months).
  • Purpose: Exclusively presented to the authorization server to request a fresh access token when the current one expires.
  • Storage: Must be stored with strict security protections (such as an HttpOnly cookie or secure device keychain).
  • Important: Refresh tokens are often opaque random strings tracked in a database so that administrators can revoke user sessions at any time. Not all refresh tokens are JWTs.

JWT Expiration and Time-Based Validation

One of the most vital claims in any JWT is the exp (Expiration Time) claim.

{
  "sub": "user_456",
  "iat": 1726387800,
  "nbf": 1726387800,
  "exp": 1726391400
}

How Applications Validate Expiration

When a backend server verifies a token, its validation routine evaluates the current Unix timestamp against the token's time claims:

  1. exp (Expiration Time): The server verifies currentTime < exp. If the current timestamp is greater than or equal to exp, the verification library throws a TokenExpiredError and denies the request.
  2. iat (Issued At): Records when the token was created. Useful for rejecting tokens issued before a user changed their password or revoked active sessions.
  3. nbf (Not Before): Defines the earliest moment the token can be honored. If currentTime < nbf, the token is rejected as not yet active.

Key Takeaway: Merely decoding a JWT in client code does not enforce expiration. Expiration enforcement must always occur inside the cryptographic verification middleware on the server receiving the request.


Where to Store JWTs: Cookies vs Web Storage

Deciding where to store tokens on the client is one of the most debated topics in web security. There is no universally flawless option; each approach presents distinct trade-offs between Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF).

1. HttpOnly, Secure Cookies (Recommended for Most Web Apps)

  • Mechanism: The backend sets the token in a cookie with the HttpOnly, Secure, and SameSite flags enabled.
  • Pros: Client-side JavaScript running in the browser cannot read or steal the cookie via document.cookie, dramatically reducing the impact of XSS vulnerabilities.
  • Cons: Because the browser automatically attaches cookies to matching requests, your application must implement CSRF defenses (such as SameSite=Lax or SameSite=Strict, or double-submit anti-CSRF tokens).

2. Web Storage (localStorage or sessionStorage)

  • Mechanism: The client receives the JWT in a JSON response body and stores it using the browser's localStorage or sessionStorage API.
  • Pros: Immune to CSRF attacks because the token must be explicitly read and attached via JavaScript (e.g., Authorization: Bearer <token>).
  • Cons: Completely accessible to any JavaScript running on the origin. If your application or any third-party script suffers from an XSS vulnerability, an attacker can silently read and exfiltrate your stored tokens.

3. In-Memory Storage

  • Mechanism: The access token is stored strictly inside JavaScript memory (e.g., a React state variable or closure).
  • Pros: The token is never written to disk or persistent browser storage, minimizing exposure to automated token harvesting scripts.
  • Cons: The token is wiped whenever the user refreshes the page or opens a new browser tab. To provide a seamless user experience, in-memory architectures require a companion refresh token stored in an HttpOnly cookie to silently re-authenticate on page loads.

Common JWT Security Mistakes to Avoid

When implementing JWTs in your applications, protect against these common architectural and configuration pitfalls:

  1. Trusting the Payload Without Verification: Extracting user IDs or role claims directly from an unverified token allows malicious clients to fabricate administrative permissions.
  2. Accepting alg: "none": Older or misconfigured libraries permitted tokens signed with "alg": "none". Always configure your server's verification logic to enforce an explicit whitelist of allowed algorithms (e.g., algorithms: ['HS256']).
  3. Using Weak HMAC Shared Secrets: If using HS256, your secret key must be a long, cryptographically random string (at least 256 bits). Short or guessable passphrases can be cracked offline using dictionary attacks.
  4. Omitting the exp Claim: A JWT issued without an expiration timestamp remains mathematically valid indefinitely, creating a severe permanent exposure if the token is ever leaked.
  5. Storing Sensitive Information in Claims: Remember that Base64Url encoding is completely reversible. Placing private keys, Social Security numbers, or unhashed passwords into a payload creates an immediate data leak.
  6. Logging Full Tokens: Avoid printing authorization headers or complete JWT strings into production application logs or third-party monitoring tools.

How to Inspect and Debug a JWT

During API integration or OAuth troubleshooting, developers frequently need to inspect token claims, verify expiration timestamps, or diagnose authentication middleware rejections.

You can inspect the contents of any token using our free browser-based JWT Decoder:

DevToolkit JWT Decoder Tool
Paste your token to instantly parse and view the color-coded Header and Payload. Inspect registered claims like sub, iss, and exp, and verify human-readable expiration dates. All processing runs 100% locally in your browser—no tokens are ever sent to an external server.

JWT debugging checklist

When a token is rejected by an API, inspect these values before changing application code:

  1. Format — confirm the token has three dot-separated parts: header, payload, and signature.
  2. Algorithm — compare the header's alg value with the algorithms allowed by the server.
  3. Expiration — check whether exp is in the past and whether the server clock is accurate.
  4. Issuer and audience — make sure iss and aud match the API's expected values.
  5. Trust boundary — use the decoder only to inspect the token; verify the signature on the backend before trusting claims.

For a quick local inspection, use the online JWT Decoder. For the encoding details behind JWT payloads, see Base64 encoding versus encryption.


Frequently Asked Questions (FAQ)

What is a JWT?

A JSON Web Token (JWT) is an open standard (RFC 7519) defining a compact, URL-safe container format used to transmit claims between parties as a JSON object. It is widely used for authentication and authorization in web applications and APIs.

What does JWT stand for?

JWT stands for JSON Web Token. The format is standardized by the Internet Engineering Task Force (IETF) in RFC 7519.

Is a JWT encrypted?

Standard JWTs (JSON Web Signatures or JWS) are digitally signed and Base64Url-encoded, but not encrypted. Anyone can decode and view the header and payload. If you require confidentiality so third parties cannot read the claims, you must use an encrypted token (JSON Web Encryption or JWE).

What is the difference between decoding and verifying a JWT?

Decoding converts the Base64Url-encoded header and payload into readable JSON. Anyone can decode a JWT without a secret key. Verifying cryptographically evaluates the signature using a secret or public key to prove the token is authentic, untampered, and unexpired. A decoded JWT is not automatically valid.

What is the difference between an access token and a refresh token?

An access token is short-lived (e.g., 15 minutes) and sent with API requests to access protected resources. A refresh token has a longer lifespan and is stored securely to request new access tokens when they expire. Not all access or refresh tokens are required to be JWTs.

Where should I store a JWT in a web application?

Token storage involves trade-offs. HttpOnly, Secure, SameSite cookies protect tokens from JavaScript access and XSS theft, but require CSRF defenses. Web storage (localStorage) is easily accessed by frontend JavaScript but is vulnerable to XSS. In-memory storage is highly secure against XSS persistence but resets on page reload.


Summary

  • JWT Meaning: JWT stands for JSON Web Token—a compact, URL-safe container format for exchanging claims.
  • Structure: Composed of three dot-separated components: Header (metadata and algorithm), Payload (claims), and Signature (cryptographic verification).
  • Encoded vs Encrypted: Standard signed JWTs (JWS) are Base64Url-encoded, not encrypted. Their claims are visible to anyone possessing the token.
  • Decoding vs Verifying: Decoding only displays the JSON data; verification proves authenticity, integrity, and unexpired status. Never trust an unverified token.
  • Architecture: Pair short-lived access tokens with carefully secured refresh tokens to minimize exposure and maintain session control.
  • Developer Tools: Use the JWT Decoder to inspect token claims locally during development, and review why Base64 encoding is not encryption to understand data encoding fundamentals.

For focused troubleshooting, continue with how to validate JWT expiration or how to decode a JWT online.