How JWTs Work: Decode, Verify, and Debug Tokens
Last updated: July 30, 2026
JWT Decoder
Paste any JWT and instantly see the decoded header, payload, and signature details.
Try It Free →JSON Web Tokens, almost always written as JWTs (pronounced "jot"), show up in virtually every modern web application. They carry authentication state between a client and a server without requiring the server to store session data. If you have ever called an API with an Authorization: Bearer ... header, you have used one. Understanding what is actually inside that token, and how the trust model works, will save you hours of debugging and help you spot security mistakes before they reach production.
Last updated: July 2026
The three-part structure
Every JWT is three Base64URL-encoded strings separated by dots:
header.payload.signature
That is it. The token is not encrypted by default. Anyone who gets hold of it can read the header and payload by decoding them. The signature only proves the token has not been tampered with. Confusing confidentiality with integrity is one of the most common JWT mistakes developers make.
The header
The header is a small JSON object that describes the token type and the signing algorithm used:
{
"alg": "HS256",
"typ": "JWT"
}
The alg field is critical. Common values are HS256 (HMAC with SHA-256, a symmetric algorithm), RS256 (RSA with SHA-256, an asymmetric algorithm), and ES256 (ECDSA with SHA-256). There is also a notorious value called none, which signals no signature at all. Accepting none in production is a severe vulnerability. Always whitelist the algorithms your server will accept.
The payload
The payload holds claims. Claims are key-value pairs that assert facts about the token subject or the token itself. The JWT specification defines a set of registered claim names:
- iss (issuer): who created the token
- sub (subject): who the token is about, usually a user ID
- aud (audience): who the token is intended for
- exp (expiration): a Unix timestamp after which the token is invalid
- nbf (not before): a Unix timestamp before which the token must be rejected
- iat (issued at): when the token was created
- jti (JWT ID): a unique identifier for the token, useful for revocation
You can add any custom claims you need, such as role, plan, or org_id. Just remember: because the payload is only encoded, not encrypted, do not put sensitive data like passwords, social security numbers, or payment details in it.
When debugging a token, paste it into the JWT Decoder on EveryFreeTool to instantly see the raw JSON in each section. You can then copy the payload into the JSON Formatter to inspect nested structures more clearly.
The signature
The signature is computed by the server that issues the token. For HS256, the process looks like this:
HMAC-SHA256(
base64url(header) + "." + base64url(payload),
secret
)
For RS256, the server signs with its private key. The recipient verifies with the corresponding public key. This is the asymmetric approach, preferred when multiple services need to verify tokens without sharing a secret.
The signature does not hide the data. It proves the data has not changed since the token was issued. If an attacker flips a single byte in the payload, the signature check will fail and the server must reject the token.
How verification actually works
When a client sends a JWT to an API, the server does not look the token up in a database. Instead, it:
- Splits the token on dots to get the three parts.
- Decodes the header to determine the signing algorithm.
- Recomputes the expected signature using the header, payload, and its own key.
- Compares the computed signature against the one in the token.
- If they match, checks the
exp,nbf,iss, andaudclaims. - If all checks pass, trusts the claims in the payload.
This stateless design is why JWTs are popular in microservices: any service that has the public key or shared secret can verify a token independently, without a round-trip to a central auth server.
Common security mistakes
Not validating the algorithm
Early JWT libraries allowed the client to specify "alg": "none" and the server would skip signature verification entirely. Never trust the algorithm in the incoming token header. Hardcode the expected algorithm in your server configuration.
Storing JWTs in localStorage
Tokens stored in localStorage are accessible to any JavaScript on the page, making them vulnerable to XSS attacks. Storing tokens in HttpOnly cookies prevents JavaScript access entirely. The tradeoff is that cookies require CSRF protection instead.
Long expiration times without refresh logic
A token that expires in 30 days is effectively a long-lived password. If it is stolen, the attacker has 30 days of access. Better practice is a short-lived access token (15 minutes is common) paired with a longer-lived refresh token stored securely. The refresh token is used only to obtain a new access token.
Putting authorization logic in the payload without re-validation
If you store a user's role in the JWT and that role changes (a subscription is cancelled, an admin is demoted), the old token remains valid until it expires. For roles that change frequently, validate current state server-side on sensitive operations rather than trusting the claim blindly.
Not rotating signing keys
If your signing secret is leaked, every token signed with it is compromised. Use a key management system that supports rotation. For RS256, publish your public keys at a JWKS (JSON Web Key Set) endpoint so consumers can fetch the current keys automatically.
Debugging a real token
When a JWT-related bug appears in your application, work through this checklist:
- Decode the token and confirm the payload claims look correct. The JWT Token Decoder on EveryFreeTool does this in seconds without sending your token to any third-party server.
- Check the
expclaim. Convert the Unix timestamp to a human-readable date using an epoch converter to confirm the token has not expired. - Confirm the
audclaim matches what your server expects. A token issued for one service is not valid for another if audiences are enforced correctly. - Verify your server is using the right key or secret. A mismatch here produces a generic "invalid signature" error that looks identical to a tampered token.
- Test the authenticated endpoint directly using the API Request Tester, setting the
Authorization: Bearer <token>header manually to rule out client-side issues.
JWTs versus sessions: choosing the right tool
JWTs are not always the better choice. Traditional server-side sessions stored in a database or Redis are easier to invalidate immediately (just delete the session record), simpler to reason about for small applications, and carry no risk of leaking claims through client-side decoding. JWTs shine when you need stateless verification across multiple services, when you are building a public API consumed by third parties, or when your infrastructure is distributed across regions and a shared session store adds unacceptable latency.
If you are building a single server-rendered application with a handful of users, a session cookie is simpler and just as secure. Reach for JWTs when the architectural benefit is concrete, not just because they feel modern.
A note on JWE (encrypted JWTs)
If you genuinely need to keep payload data confidential from the client, JSON Web Encryption (JWE) is the standard. A JWE token has five parts instead of three, and the payload is encrypted rather than merely encoded. Most applications do not need JWE. If you think you do, ask first whether the sensitive data belongs in the token at all, or whether it should stay server-side and be fetched on demand.
Quick reference
- JWT: signed, not encrypted. Anyone can decode it.
- JWE: signed and encrypted. Payload is confidential.
- HS256: symmetric. One secret shared between issuer and verifier.
- RS256 / ES256: asymmetric. Private key signs, public key verifies.
- exp: always set it. Tokens without expiration are forever credentials.
- alg: none: never accept it in production.
Understanding the mechanics behind JWTs makes you a better consumer of auth libraries and a sharper reviewer of security-sensitive code. The next time a colleague says "just put it in the JWT," you will know exactly what questions to ask.
JWT Token Decoder
Decode and inspect JWT tokens in your browser without sending them to a third-party server.
Try It Free →Frequently Asked Questions
Can anyone read the data inside a JWT?
Yes, if they have the token. The header and payload are Base64URL encoded, which is trivially reversible. Anyone who intercepts or obtains your JWT can decode it and read every claim. This is why you should never store passwords, credit card numbers, or other sensitive data in a JWT payload. If you need the payload to be confidential, use JWE (JSON Web Encryption) instead.
What is the difference between HS256 and RS256?
HS256 uses a single shared secret for both signing and verification. Every party that needs to verify tokens must know the secret, which creates a risk if that secret leaks. RS256 uses an asymmetric key pair: the issuer signs with a private key, and any verifier checks the signature with the corresponding public key. RS256 is preferred in microservice architectures because services can verify tokens without ever seeing the private key.
How do I invalidate a JWT before it expires?
Standard JWTs cannot be revoked once issued because verification is stateless. The common workarounds are keeping a server-side blocklist of revoked token IDs (the jti claim) and checking it on each request, using very short expiration times so the damage window is small, or maintaining a version counter per user in your database and embedding it as a claim so old tokens fail when the counter increments. Each approach adds some statefulness back, which is the tradeoff you accept for revocation support.
Is it safe to store a JWT in localStorage?
Storing a JWT in localStorage exposes it to any JavaScript running on the page, including injected scripts from XSS vulnerabilities. A more secure approach is to store tokens in HttpOnly cookies, which are inaccessible to JavaScript. The tradeoff is that cookies require CSRF protection. For most web applications the cookie approach is the safer default, but the right choice depends on your specific threat model and whether your application is vulnerable to XSS.
Why do I get an 'invalid signature' error even with the correct token?
The most common cause is a mismatch between the secret or key used to sign the token and the one used to verify it. Check that your server is loading the correct environment variable or key file and that no extra whitespace or encoding difference has crept in. A second common cause is algorithm mismatch: if the token was signed with RS256 but your server expects HS256, verification will always fail. Decode the token header first to confirm the algorithm, then verify your server configuration matches it exactly.
Related Tools
JWT Decoder
Paste any JWT and instantly see the decoded header, payload, and signature details.
JWT Token Decoder
Decode and inspect JWT tokens in your browser without sending them to a third-party server.
Hash Generator
Generate SHA-256 and other hash outputs to understand how signing algorithms work.
API Request Tester
Send authenticated API requests with Authorization headers to test JWT-protected endpoints.
JSON Formatter
Prettify and validate the JSON payloads inside your decoded JWT claims.