← Back to Blog

How to Decode JWT Tokens Online (With Examples)

You are staring at a string that starts with eyJ and you need to know what is inside it. This guide walks through exactly what a JWT is, how its three parts work, how to decode one in seconds, and the security mistakes that get developers in trouble.

Why You Are Probably Here

Somewhere in your API logs, a browser request, or an Authorization header you found a token that looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMyIsIm5hbWUiOiJKYW5lIERldmVsb3BlciIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTc0MjkwMDAwMCwiZXhwIjoxNzQyOTg2NDAwfQ.Xk3RqFz1mP9c2oVtLdNwE8Y7bHJqA4sKgRvMp0TuWxI

That is a JWT. It looks like random noise but it is entirely readable - no decryption key required. The three blocks separated by dots each carry structured data encoded in Base64URL format. You can decode and inspect the contents in seconds using an online tool or a single terminal command.

Understanding JWT structure matters beyond curiosity. When you are debugging authentication failures, auditing a third-party integration, or investigating a security incident, being able to inspect a token and understand what it claims about a user is a fundamental skill.

What Is a JWT?

A JSON Web Token (JWT) is a compact, URL safe token format defined by RFC 7519. It encodes a set of claims as a JSON object and signs that data so the recipient can verify it has not been tampered with. JWTs are the dominant token format for API authentication, single sign-on (SSO), and microservice-to-microservice authorization.

The key distinction that confuses most developers: JWTs are signed, not encrypted by default. The contents are visible to anyone who holds the token. The signature only proves the data has not been modified since it was issued - it does not hide the data. This matters enormously for what you put inside a token.

The Three Parts: Header, Payload, Signature

Every JWT has exactly three sections, separated by periods. Using the example token above:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9       ← Header
.
eyJzdWIiOiJ1c2VyXzEyMyIsIm5hbWUiOiJKYW5lIERldmVsb3BlciIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTc0MjkwMDAwMCwiZXhwIjoxNzQyOTg2NDAwfQ  ← Payload
.
Xk3RqFz1mP9c2oVtLdNwE8Y7bHJqA4sKgRvMp0TuWxI  ← Signature

Part 1: The Header

The header is a Base64URL-encoded JSON object declaring the token type and signing algorithm. Decoding the first segment:

// Base64URL decode of: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
{
  "alg": "HS256",
  "typ": "JWT"
}

The alg field tells the server which algorithm to use to verify the signature. Common values are HS256, RS256, and ES256. The typ field is always JWT for standard tokens. Some tokens also include a kid (Key ID) field pointing to the specific key in a JWKS endpoint that should be used for verification.

Part 2: The Payload (Claims)

The payload is the heart of the token. Decoding the second segment of our example:

// Base64URL decode of the payload segment
{
  "sub": "user_123",
  "name": "Jane Developer",
  "role": "admin",
  "iat": 1742900000,
  "exp": 1742986400
}

Each key-value pair is a claim - a statement about the token subject. JWT defines several registered claims with well known meanings:

  • iss (Issuer): Who created the token, e.g. "https://auth.example.com"
  • sub (Subject): The entity the token is about, typically a user ID
  • aud (Audience): The intended recipient(s) of the token, e.g. "api.example.com"
  • exp (Expiration): Unix timestamp after which the token is invalid
  • nbf (Not Before): Unix timestamp before which the token must not be accepted
  • iat (Issued At): Unix timestamp when the token was created
  • jti (JWT ID): A unique identifier for this token, used for revocation

In the example above, iat: 1742900000 is March 25, 2026 and exp: 1742986400 is roughly 24 hours later. Use our Timestamp Converter to read Unix timestamps as human-readable dates.

The payload is Base64URL-encoded, not encrypted. Paste the second segment of any JWT into a Base64 decoder and you will see the raw JSON claims. Never store passwords, credit card numbers, or other sensitive data in a JWT payload.

Part 3: The Signature

The signature is what makes a JWT trustworthy. For HS256, it is computed as:

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

For RS256, it is an RSA signature over the same concatenated string, using the server's private key. The server verifies by checking the signature against the stored secret (HS256) or the corresponding public key (RS256/ES256). If any bit of the header or payload has been changed, the signature check fails and the token is rejected.

Critically: you cannot forge a valid signature without the secret key. This is what makes JWTs tamper-proof. But it does not make the contents private.

How to Decode a JWT Online: Step by Step

Decoding a JWT to inspect its contents takes about 10 seconds with the right tool.

  1. Copy the full JWT string from wherever you found it (browser DevTools Network tab, API response, log file, etc.).
  2. Open SecureBin JWT Decoder.
  3. Paste the token into the input field.
  4. The tool instantly shows the decoded header and payload as formatted JSON, highlights expiry in human-readable form, and flags if the token is already expired.

No keys, no credentials, and nothing is sent to any server - the decoder runs entirely in your browser using JavaScript's atob() function on the Base64URL-encoded segments.

Decoding a JWT on the command line

If you prefer the terminal, you can decode a JWT with jq and standard base64 utilities:

TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMyIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTc0Mjk4NjQwMH0.signature"

# Decode the payload (second segment)
echo "$TOKEN" | cut -d'.' -f2 | base64 -d 2>/dev/null | jq .

Note: Base64URL encoding uses - and _ instead of + and /. If base64 -d fails, add padding and translate characters:

PAYLOAD=$(echo "$TOKEN" | cut -d'.' -f2)
# Add padding and decode
echo "${PAYLOAD}==" | tr '_-' '/+' | base64 -d 2>/dev/null | jq .

Decode Any JWT Token Instantly

Paste your JWT to see the full decoded header and payload, check expiry in human-readable form, and inspect all claims. 100% client side - your token never leaves your browser.

Open JWT Decoder

JWT Encoding vs Encryption: The Critical Distinction

This is the most common misconception about JWTs. Encoding is not encryption.

  • Encoding (Base64URL): Transforms binary data to text. Fully reversible. No key required. Anyone can decode it.
  • Signing (HMAC/RSA): Creates a tamper-evident seal. Proves the token was issued by someone with the secret key. Does not hide the contents.
  • Encryption (JWE): Actually hides the contents. Requires a key to read. This is not what standard JWTs use.

A standard JWT (JWS - JSON Web Signature) provides integrity but not confidentiality. If you need encrypted tokens where even a malicious client holding the token cannot read the payload, you need JWE (JSON Web Encryption), which is a different standard with a 5-part structure.

HS256 vs RS256: Choosing the Right Algorithm

The algorithm in the header determines how the signature is created and verified:

HS256 (HMAC + SHA-256) - Symmetric

One secret key is used for both signing and verification. Every service that needs to verify tokens must have the secret. Fast and simple, but the secret must be shared across services, which increases the blast radius of a key compromise.

// Header for HS256 token
{
  "alg": "HS256",
  "typ": "JWT"
}

Use HS256 when a single server both issues and verifies tokens. Common for monolithic applications where no external service needs to verify tokens independently.

RS256 (RSA + SHA-256) - Asymmetric

A private key signs the token. Any service can verify using the corresponding public key. The private key never leaves the auth server. Public keys are distributed via a JWKS (JSON Web Key Set) endpoint at a well known URL like https://auth.example.com/.well-known/jwks.json.

// Header for RS256 token (with key ID)
{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "2026-03-key-1"
}

Use RS256 for microservices, OAuth2/OIDC providers, or any architecture where multiple independent services need to verify tokens. If an API service is compromised, the attacker can only read tokens - they cannot forge new ones because they do not have the private key.

ES256 (ECDSA + SHA-256)

Asymmetric like RS256 but uses elliptic curve cryptography. Produces smaller signatures (~64 bytes vs ~256 bytes for RS256) and is significantly faster. Recommended over RS256 for new systems in 2026.

Real Decode Example: Reading an Auth0 Token

Here is a realistic example of what you see when you decode a JWT from an OAuth2 provider like Auth0:

// Header
{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "abc123def456"
}

// Payload
{
  "iss": "https://yourapp.auth0.com/",
  "sub": "auth0|64f2a1b3c8d9e0f1a2b3c4d5",
  "aud": [
    "https://api.yourapp.com",
    "https://yourapp.auth0.com/userinfo"
  ],
  "iat": 1742900000,
  "exp": 1742986400,
  "azp": "xYz9AbCd1234",
  "scope": "openid profile email read:data",
  "permissions": ["read:data", "write:profile"]
}

From this decoded payload you can immediately determine:

  • The token was issued by Auth0 for your application (iss)
  • The user ID in Auth0's system (sub)
  • The APIs this token is valid for (aud)
  • Exactly when it expires - convert exp: 1742986400 to confirm it has not elapsed
  • What the user is allowed to do (permissions)

Token Validation: What Your Server Must Check

Decoding shows you the claims. Validation is the process of deciding whether to trust them. A proper JWT validation flow checks all of the following, in order:

  1. Signature: Verify the signature against the secret or public key. Reject if invalid.
  2. Algorithm: Confirm the alg header matches your expected algorithm. Reject none unconditionally.
  3. Expiration (exp): Reject if the current time is past the expiry timestamp.
  4. Not Before (nbf): Reject if the current time is before the nbf timestamp.
  5. Issuer (iss): Confirm the token was issued by your expected auth server.
  6. Audience (aud): Confirm your API's identifier is listed in the audience. Tokens intended for a different service should be rejected.

Most JWT libraries handle all of this automatically if you configure them correctly. The danger is misconfiguration: skipping audience validation, not checking the algorithm, or catching the "expired" exception and proceeding anyway.

Using JWT in APIs

The standard way to send a JWT to an API is via the Authorization header using the Bearer scheme:

GET /api/v1/profile HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMyJ9.signature

The server extracts the token from the header, validates it, and uses the claims to authorize the request. The user ID from sub identifies who is making the request. The role or permissions claims determine what they are allowed to do.

Token issuance typically follows this flow:

  1. Client sends credentials (username + password, or OAuth code) to the auth endpoint.
  2. Server validates credentials and issues a signed JWT with appropriate claims and expiry.
  3. Client stores the token and sends it with subsequent API requests.
  4. Server validates the JWT on every request without hitting a database (stateless auth).
  5. When the token expires, the client uses a refresh token to obtain a new access token.

Critical Security Pitfalls

The alg:none Attack

The JWT specification includes an algorithm value of "none", meaning no signature. Some JWT libraries, when encountering a token with "alg": "none", skip signature verification entirely. An attacker can set any claims they want and forge admin access.

Fix: Always explicitly whitelist the allowed algorithms in your verification code. Never accept none:

// Node.js example with jsonwebtoken library
jwt.verify(token, secret, { algorithms: ['HS256'] }, callback);

Algorithm Confusion (RS256 to HS256 Downgrade)

If a server is configured to verify RS256 tokens but the JWT library also accepts HS256, an attacker can create a token signed with HS256 using the server's public key as the secret. Since the public key is, by definition, public, the attacker can forge valid-looking tokens.

Fix: Always specify the exact expected algorithm. Do not let the library accept whatever algorithm the token header claims.

Storing JWTs in localStorage

localStorage is accessible to any JavaScript running on the page. A single XSS vulnerability - a malicious ad, a compromised npm package, an unsanitised user input rendered as HTML - can exfiltrate every token in localStorage.

Fix: Store access tokens in memory (JavaScript variable). Store refresh tokens in httpOnly cookies with SameSite=Strict and Secure flags. httpOnly cookies cannot be read by JavaScript, only sent automatically by the browser.

No Expiration on Tokens

A JWT without an exp claim is valid forever (until the signing key is rotated). If a token is stolen, there is no way to invalidate it. Always set expiration. Access tokens should expire in 5–15 minutes. Refresh tokens can be longer-lived but should be stored server side to enable revocation.

Sensitive Data in the Payload

Since the payload is only Base64URL-encoded, anyone holding the token can read it. This includes users inspecting their own tokens in browser DevTools, anyone who intercepts a token in a log, and attackers who exfiltrate tokens via XSS. Never put passwords, SSNs, payment data, or secret keys in a JWT payload. If you need to carry sensitive data, use JWE (JSON Web Encryption) or keep sensitive data server side and only put a reference ID in the token.

Frequently Asked Questions

What does "decode JWT" mean vs "verify JWT"?

Decoding a JWT means Base64URL-decoding the header and payload to read the claims as JSON. This requires no key and reveals the token contents to anyone. Verifying a JWT means cryptographically checking the signature to confirm the token was issued by a trusted party and has not been tampered with. Verification requires the secret key (HS256) or public key (RS256/ES256). You can decode without verifying, but you must never trust the claims without also verifying the signature.

Can I decode a JWT without the secret key?

Yes. The header and payload are Base64URL-encoded, not encrypted. Any Base64 decoder can read them. The SecureBin JWT Decoder does exactly this - it shows you the decoded contents client side with no key required. What you cannot do without the key is verify the signature, meaning you cannot confirm whether the token is genuine or forged.

Why does my JWT start with eyJ?

eyJ is the Base64URL encoding of {" (opening brace + double quote). Since every JWT header and payload begins with a JSON object starting with {", almost all JWTs start with eyJ in both the header and payload segments. It is a reliable visual indicator that you are looking at a JWT.

How do I check if a JWT is expired?

Decode the payload and read the exp claim. It is a Unix timestamp (seconds since January 1, 1970). Compare it to the current time. If exp is less than Date.now() / 1000 (JavaScript) or time.time() (Python), the token is expired. Our JWT Decoder shows this automatically - it calculates time remaining or displays "Expired" with a relative timestamp.

What is a JWKS endpoint and how does it relate to JWT?

A JWKS (JSON Web Key Set) endpoint is a public URL that returns the public keys used to verify JWTs from that issuer. For example, Google's JWKS endpoint is at https://www.googleapis.com/oauth2/v3/certs. When a service receives a JWT, it can fetch the public key identified by the kid claim in the header and use it to verify the signature. This enables key rotation without coordinating secret changes across every service - services just re-fetch the JWKS endpoint and pick up the new key automatically.

Is JWT the same as OAuth2?

No. OAuth2 is an authorization framework. JWT is a token format. OAuth2 can use JWTs as access tokens (and this is common), but OAuth2 can also use opaque tokens (random strings that must be looked up in a database). Conversely, JWTs can be used outside of OAuth2 entirely. The two are often seen together because JWT access tokens allow stateless verification without a database lookup, which scales well in distributed systems.

Summary

JWTs are three Base64URL-encoded segments separated by dots: a header declaring the algorithm, a payload containing claims about the user and session, and a signature that proves the token has not been tampered with. The payload is readable by anyone - never put secrets in it. The signature can only be created by whoever holds the signing key, which is what makes the token trustworthy.

When debugging auth issues, decoding the token to inspect the claims directly is almost always the fastest path to the root cause. Check the exp to see if it has expired, the aud to confirm it is intended for your API, and the iss to verify it came from the expected auth server.

Use our free tool here → SecureBin JWT Decoder - paste any token and see the full decoded output instantly, no login required.

UK
Written by Usman Khan
DevOps Engineer | MSc Cybersecurity | CEH | AWS Solutions Architect

Usman has 10+ years of experience securing enterprise infrastructure, managing high-traffic servers, and building zero-knowledge security tools. Read more about the author.