What's Actually Inside a JWT Token — and Can Anyone Read It?
JWT tokens are easy to read once you have the string, but that does not make them safe to trust. If you want to inspect the header, payload, and signature without manually decoding base64url, try our free JWT decoder tool.
What a JWT actually is
A JWT, or JSON Web Token, is a compact text format made of three parts separated by dots: header.payload.signature. The header describes the token type and algorithm. The payload carries claims, which are just key-value pairs like sub, iss, exp, or custom app data.
The important part is this: a JWT is not encrypted by default. It is encoded, usually with base64url, which makes it safe to transport but not private. If someone gets the token string, they can usually read the header and payload right away. The signature only proves whether the token was tampered with after it was issued.
Can anyone read a JWT token?
Yes, in most real-world cases, anyone who has the token can decode it. They do not need the signing secret or private key to inspect the contents. They only need the raw JWT string.
That is why you should treat JWT payloads as visible data. Do not put passwords, API keys, session secrets, or anything you would regret seeing in browser devtools. If a claim would be bad news in plain text, it should not live inside the token.
This is also where people mix up authentication and authorization. A JWT can carry identity and role claims, but your server still needs to verify the signature before it trusts those claims. If you skip verification, you are just accepting unsigned user-controlled text with a fancy header.
Header, payload, signature, decoded
The three JWT sections are small, but each one has a job.
- Header: metadata such as
algandtyp. - Payload: claims about the token subject and context.
- Signature: integrity check created from the header and payload.
The header often looks like this:
{
"alg": "HS256",
"typ": "JWT"
}The payload might look like this:
{
"sub": "user_123",
"name": "Alex",
"role": "admin",
"iat": 1710000000,
"exp": 1710003600
}Neither of those blocks is hidden. The signature is the part that prevents silent edits like changing role from user to admin. If the token was altered, signature verification fails. That is the whole game.
For a broader refresher on how the encoding side works, our guide on why base64 shows up everywhere is worth a look.
How to decode one without breaking it
JWT decoding is a read-only operation. You split the token on dots, base64url-decode the first two parts, and inspect the JSON. The signature is not decoded in the same way; it is verified against a secret or public key.
In pseudo-code, it looks like this:
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMyIsInJvbGUiOiJhZG1pbiJ9.SIGNATURE"
header_b64, payload_b64, signature = token.split('.')
header = base64url_decode(header_b64)
payload = base64url_decode(payload_b64)
print(header)
print(payload)
If you are debugging a login flow, a refresh token issue, or a weird expiry bug, this kind of inspection saves time. You can spot clock skew, a bad issuer, a missing audience, or a token that expired five minutes ago and is somehow still being sent around. The trick is to look at the claims first, then verify the signing assumptions.
If you are dealing with text that needs cleanup before inspection, a simple text case converter can help normalize claim names in notes or logs. No magic. Just fewer tiny annoyances.
Common JWT mistakes that bite later
JWTs are popular because they are portable, stateless, and easy to pass between services. They are also easy to misuse. A lot of teams learn that the hard way, usually after putting the wrong kind of data into the payload or trusting it before verifying it.
- Storing secrets in the payload. If the client can read it, assume everyone can read it.
- Skipping signature verification. Decoding is not verification.
- Accepting the wrong algorithm. The
algclaim is part of the data, so do not blindly trust it. - Ignoring expiration.
expexists for a reason. - Using JWTs as a database. Tokens should not become your source of truth.
One more subtle problem: some developers assume JWTs are automatically secure because they are “signed.” Signed data can still be read. A signed postcard is still a postcard. The signature tells you whether someone swapped the words after the sender mailed it.
When to use JWTs, and when not to
JWTs make sense when you want a self-contained token that can be verified without a database lookup on every request. That can work well for APIs, single sign-on flows, and service-to-service auth where a backend checks claims and expiration on the fly.
They are less useful when you need instant revocation, very sensitive data, or complex server-side session control. If you need to kill access immediately and everywhere, a JWT alone is often the wrong tool. You can build revocation lists and short-lived access tokens, but at that point you are already doing more work to get back some of the control that a server-side session gives you naturally.
So the practical rule is simple: keep JWT payloads boring, short-lived, and non-secret. Use them for identity and claims, not for hiding information. If the contents matter, verify them. If the contents are sensitive, do not put them in the token in the first place.
See It in Action
Here is a realistic example of a token before and after decoding. The token itself is compact and unreadable at a glance, but the payload becomes plain JSON once decoded.
Before
------
Header: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Payload: eyJzdWIiOiJ1c2VyXzEyMyIsIm5hbWUiOiJBbGV4IiwiZXhwIjoxNzEwMDAzNjAwfQ
Sig: p0D8l1xS4n3bqK2W0Z8QjW7z8m1DqFQ0c0Qy1X6H6wA
After decoding
--------------
{
"alg": "HS256",
"typ": "JWT"
}
{
"sub": "user_123",
"name": "Alex",
"exp": 1710003600
}Now imagine the payload had this instead:
{
"sub": "user_123",
"email": "alex@example.com",
"plan": "pro"
}That is not secret, but it is still data the client can see. If you would not want it exposed in a browser session, treat it as public. Decode first, verify second, and only then make any auth decision.
Frequently Asked Questions
Can JWT tokens be read by anyone?
Yes. If someone has the token string, they can decode the header and payload because JWTs are usually base64url-encoded, not encrypted. The signature does not hide the contents.
What part of a JWT is secret?
Usually nothing inside the token should be treated as secret. The signing key or private key is secret, but the token contents themselves are meant to be inspectable. If you need confidentiality, you need encryption, not just signing.
How do I verify a JWT token?
You verify the signature using the expected secret key or public key, depending on the algorithm. After that, you should also validate claims like exp, iss, and aud. Decoding alone does not count as verification.
Is a JWT the same as a session cookie?
No. A session cookie usually points to server-side session data, while a JWT contains claims inside the token itself. JWTs are more self-contained, but that also means they can expose more data if you are careless.
The Bottom Line
JWT tokens are readable by design, which is useful for debugging and dangerous when people confuse readability with trust. The payload can be inspected by anyone who has the token, so keep sensitive data out of it and always verify the signature before trusting claims.
If you are staring at a token and want the header, payload, and signature broken out cleanly, use the JWT decoder. It is the fastest way to see what is actually inside without hand-decoding base64url like it is 2014.
From there, check the claims that matter: exp, iss, aud, and whatever custom fields your app depends on. If the token looks wrong, expired, or suspiciously overstuffed, that is usually your clue.