How to Decode JWT Tokens Safely in Your Browser Without Sending Data
JWT tokens show up in every web project. Here is how to inspect one locally, verify what a tool is actually doing with your data, and decode tokens without pasting them into a third-party site.
You pull a JWT from a cookie or response body. It looks like three base64 strings joined by dots. You want to know what is inside the payload: the user ID, the expiration, the roles. The usual approach is to paste it into an online decoder and hope for the best. That works until the token contains something sensitive and the decoder is logging your input on a server somewhere.
This happens. Third-party JWT decoders have been caught logging tokens on servers or returning them in search results. If you are working with production credentials or anything tied to a real user, you need a way to decode tokens that never leaves your machine.
What a JWT Actually Contains
A JSON Web Token has three parts separated by dots. The header is base64-encoded JSON describing the algorithm. The payload is base64-encoded JSON with the claims. The signature is a cryptographic proof that the header and payload have not been tampered with.
Decoding a token means base64-decoding the first two segments and reading the JSON. You can do this in a browser console without any library:
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
const payload = JSON.parse(atob(token.split('.')[1]));
console.log(payload);
// { sub: '1234567890', name: 'John Doe', iat: 1516239022 }
The iat claim is a Unix timestamp. You can convert it with:
new Date(payload.iat * 1000).toISOString()
// '2018-01-18T00:50:22.000Z'
The exp claim, if present, tells you when the token expires. If Date.now() / 1000 > payload.exp, the token is stale.
This works. It is a valid way to peek inside a token quickly. But it is not always convenient, especially when the payload spans multiple lines or when you want to inspect headers and expiration in one view.
Why Browser-Based Decoding Is the Right Default
Browser-based JWT decoders run entirely in JavaScript. The token you paste never gets sent to any server. The decoding happens in your tab, using your CPU.
The privacy advantage is real. A token might include a user ID, a session handle, or a role claim. That information should not travel further than your own machine. When decoding happens client-side, there is no server to log the input, no network request to trace, and no third party with access to your paste history.
You can verify this yourself. Open DevTools, go to the Network tab, paste a token into a client-side decoder, and confirm there are zero outbound requests. If the tool sends your token anywhere, you will see it in the network log immediately.
This is the verification step most developers skip. It takes thirty seconds and tells you whether the tool you are using is trustworthy.
What to Look for in a JWT Payload
The standard claims in most JWT implementations:
iss- who issued this tokensub- the subject (usually the user ID)aud- who this token is intended forexp- expiration timestampiat- when the token was issuednbf- not before timestamp
Beyond the standard claims, applications often embed custom data in the payload: role names, permissions, tenant IDs, or feature flags. If you are debugging an authorization issue, the custom claims are where you start. Look for any claim that sounds like it gates access.
Common JWT Problems You Can Spot by Decoding
A token that should work but does not is usually one of these:
Expired token. The exp claim is in the past. Your application is rejecting it because the expiry time has passed. If you are in development, check your system clock first.
Wrong audience. The aud claim does not match what your API expects. A token issued for api.example.com will not work on app.example.com even if the user is the same.
Algorithm mismatch. The alg in the header tells you how the signature is encoded. HS256 uses a shared secret. RS256 uses a public/private key pair. If you are verifying tokens and getting signature errors, the algorithm in the header might not match what your system expects.
Missing claims. Some systems require a specific claim to be present. A token that decodes fine but still fails might be missing a claim like scope or a specific iss.
Decoding the token first tells you which of these it is. That narrows the debugging from "something is wrong" to "the exp timestamp is from last month."
How to Use a Browser JWT Decoder Safely
A good client-side JWT decoder shows you the header, payload, and expiration status in one view. It runs entirely in the browser without fetching anything.
Before you paste a token into any tool, do the thirty-second check:
- Open DevTools, go to Network tab
- Make sure recording is on
- Paste the token and hit decode
- Check for any outbound requests
If you see any requests, the tool is sending your token somewhere. Use a different one.
For local development, you can also use the browser console approach above. It takes longer to read, but it is always available and never sends data anywhere.
The goal is simple: know what is in the token, know what the tool is doing with it, and decode without adding unnecessary risk.
Frequently Asked Questions
Can a JWT be encrypted instead of just signed? Yes. JWE (JSON Web Encryption) encrypts the payload. Standard JWT decoders that only base64-decode will show gibberish for an encrypted token. Most auth systems use signed JWTs (JWS) rather than encrypted ones.
Is it safe to decode a token from production? Decoding is read-only. You cannot modify a token by decoding it, and you cannot forge a valid signature without the secret or private key. The risk is sharing the token with an untrusted third party. Keep production tokens local.
How do I verify a token signature in the browser? You need the public key or shared secret. You can use the Web Crypto API in modern browsers to verify signatures without sending the token anywhere. Most client-side JWT libraries (like jwt-decode) can validate signatures if you provide the key.
Why do some JWT decoders show an error for a token I know is valid? Common causes: the token is not valid base64, the token uses a different encoding (JWE instead of JWS), or the decoder does not handle multi-line payloads correctly. Try decoding in the browser console first to verify the token itself is well-formed.