The Problem: You Are Probably Leaking Tokens
Here is a scenario that plays out thousands of times a day. A developer is debugging an authentication issue. They copy a JWT from a request header, open the first "jwt decoder online" result in Google, and paste the token. The decoded header and payload appear. The bug becomes obvious. The developer fixes it and moves on.
What they probably did not check: whether that decoder sent their token to a server.
Many popular JWT decoders -- including some of the most well-known ones -- transmit the token to a backend for processing. This means the server operator has a copy of your token. Depending on the token's expiration and the system's security model, that token might still be valid. It might contain a user ID, email address, role, or other claims that are useful for impersonation or reconnaissance.
The fix is straightforward: use a JWT decoder that runs entirely in your browser. Decoding a JWT is a trivial operation (it is just Base64URL decoding). There is zero technical reason for it to require a server. If a decoder sends your token anywhere, that is a design choice, not a necessity -- and it is a choice that works against your interests.
Never paste a valid, unexpired JWT into an online decoder unless you have verified that the tool processes data client-side only. Open DevTools > Network tab and watch for outbound requests. If you see one, your token has been transmitted.
JWT Anatomy: What You Are Actually Decoding
A JSON Web Token is a string with three parts, separated by dots. Here is a real (example) token with its three segments color-coded:
Part 1: Header
The header is a JSON object, Base64URL-encoded. It tells you which algorithm was used to sign the token and the token type. Decoding the red segment above gives you:
{
"alg": "HS256",
"typ": "JWT"
}
Common algorithms you will see: HS256 (HMAC with SHA-256, symmetric key), RS256 (RSA with SHA-256, asymmetric key pair), and ES256 (ECDSA with P-256 curve). The algorithm determines how the signature is created and verified.
Part 2: Payload
The payload is also a Base64URL-encoded JSON object. It contains the claims -- the actual data the token carries. Decoding the purple segment gives you:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"role": "admin"
}
Standard claims defined in the JWT spec include:
sub(subject) -- who the token is about, usually a user IDiss(issuer) -- who issued the tokenaud(audience) -- who the token is intended forexp(expiration) -- Unix timestamp when the token expiresiat(issued at) -- Unix timestamp when the token was creatednbf(not before) -- Unix timestamp before which the token is invalidjti(JWT ID) -- unique identifier for the token
You can also add any custom claims you want. In the example above, role is a custom claim.
Part 3: Signature
The signature is created by taking the encoded header, the encoded payload, a secret key, and the algorithm specified in the header, and running them through the signing function. The signature allows the receiver to verify that the token has not been tampered with.
Critically: the signature does not encrypt anything. The header and payload are readable by anyone. The signature only proves authenticity and integrity.
Decoding a JWT requires zero secrets. The header and payload are just Base64URL-encoded JSON. Verifying a JWT requires the signing key. These are two completely different operations, and an online decoder only needs to do the first one.
Decode vs. Verify: Why the Distinction Matters
This is the most misunderstood aspect of JWTs. Let us be precise:
- Decoding = reading the header and payload by Base64URL-decoding them. No key needed. Anyone can do it. This is what a JWT decoder tool does.
- Verifying = checking that the signature is valid, which proves the token was issued by a trusted party and was not modified in transit. This requires the secret key (for HMAC) or the public key (for RSA/ECDSA).
When you paste a JWT into an online decoder, all it needs to do is decode. There is no reason for the tool to ask for your secret key (unless it also offers verification, which should be optional). And there is absolutely no reason for the tool to send the token to a server -- decoding is a few lines of JavaScript that run in milliseconds.
If a tool sends your token to a server to "decode" it, something is wrong. Either the tool is poorly architected, or it is collecting tokens. Neither is acceptable when you are debugging production authentication.
Why Client-Side Decoding Is the Only Sane Default
When you need to decode a JWT token during development or debugging, the safest approach is a tool that never transmits your token. QTool's JWT Decoder is built exactly this way: the entire decoding operation runs in JavaScript in your browser. Your token never leaves your machine.
Here is how to verify that any decoder tool is truly client-side:
- Open the tool in your browser.
- Open DevTools (F12 or Cmd+Option+I).
- Go to the Network tab.
- Clear the log.
- Paste a JWT and trigger decoding.
- Check the Network tab. If zero requests were made (other than pre-existing analytics or fonts), the tool is client-side.
You can even disconnect from the internet after the page loads. If the tool still works, it is definitively client-side.
Code Examples: Decode JWTs in Any Language
If you prefer to decode JWTs in code rather than using an online tool, here are copy-paste examples for the most common languages.
JavaScript (Browser or Node.js)
function decodeJWT(token) {
const [headerB64, payloadB64, signature] = token.split('.');
// Base64URL -> Base64 -> decode
const decode = (str) => {
const base64 = str.replace(/-/g, '+').replace(/_/g, '/');
const json = atob(base64);
return JSON.parse(json);
};
return {
header: decode(headerB64),
payload: decode(payloadB64),
signature: signature
};
}
// Usage
const token = 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0In0.abc123';
const decoded = decodeJWT(token);
console.log(decoded.header); // { alg: "HS256" }
console.log(decoded.payload); // { sub: "1234" }
Python
import base64
import json
def decode_jwt(token):
parts = token.split('.')
if len(parts) != 3:
raise ValueError('Invalid JWT: expected 3 parts')
def decode_part(part):
# Add padding if needed
padding = 4 - len(part) % 4
if padding != 4:
part += '=' * padding
decoded = base64.urlsafe_b64decode(part)
return json.loads(decoded)
return {
'header': decode_part(parts[0]),
'payload': decode_part(parts[1]),
}
# Usage
token = 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0In0.abc123'
decoded = decode_jwt(token)
print(decoded['header']) # {'alg': 'HS256'}
print(decoded['payload']) # {'sub': '1234'}
Go
package main
import (
"encoding/base64"
"encoding/json"
"fmt"
"strings"
)
func decodeJWTPart(part string) (map[string]interface{}, error) {
decoded, err := base64.RawURLEncoding.DecodeString(part)
if err != nil {
return nil, err
}
var result map[string]interface{}
err = json.Unmarshal(decoded, &result)
return result, err
}
func main() {
token := "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0In0.abc123"
parts := strings.Split(token, ".")
header, _ := decodeJWTPart(parts[0])
payload, _ := decodeJWTPart(parts[1])
fmt.Println("Header:", header)
fmt.Println("Payload:", payload)
}
Bash (with base64 and jq)
# Decode JWT header
echo "eyJhbGciOiJIUzI1NiJ9" | base64 -d 2>/dev/null | jq .
# Decode full JWT (extract payload)
TOKEN="eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0In0.abc123"
echo "$TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq .
# One-liner: decode and pretty-print the payload
jwt_decode() { echo "$1" | cut -d. -f2 | base64 -d 2>/dev/null | jq .; }
jwt_decode "$TOKEN"
Every one of these examples does the same thing: split on dots, Base64URL-decode, parse JSON. That is it. No libraries, no secrets, no network calls. This is why there is no technical justification for an online decoder to send your token to a server.
Decode JWTs Instantly, Privately
QTool's JWT Decoder runs 100% in your browser. Paste a token, see the header, payload, and expiration. Your data never leaves your machine.
Open JWT DecoderWhat You Should Never Put in a JWT Payload
Because JWTs are only encoded and not encrypted, the payload is readable by anyone who has the token. This means anyone with access to the client (browser JavaScript, mobile app memory, network logs) can read every claim. Here is what does not belong in a JWT:
- Passwords or password hashes. Never. Not even hashed. The token is not the right place.
- Credit card numbers or financial data. Use a tokenized payment processor instead.
- Personally identifiable information (PII) beyond what is necessary. A user ID is fine. A full address and social security number is not.
- Internal system secrets. Database connection strings, API keys for other services, or encryption keys should never appear in a JWT.
- Large data blobs. JWTs are sent with every request in the Authorization header. A 50 KB payload adds 50 KB to every API call.
What should go in a JWT payload: a user identifier (sub), role or permissions, expiration time (exp), issuer (iss), and any minimal claims the consuming service needs to make authorization decisions without a database lookup.
Many developers store the user's email in the JWT for convenience. This is borderline -- an email is PII but is often already known to the client. The risk is that JWTs get logged, cached, and stored in places you do not control (browser storage, proxy logs, error reporting services). Every claim you add is a claim that might leak. Be deliberate about what you include.
JWT Decoder Tool Comparison
Here is how the most popular online JWT decoders compare on the things that actually matter:
- QTool JWT Decoder -- Fully client-side, zero ads, clean interface, instant decode with header/payload/signature breakdown, expiration timestamp conversion. Part of 269 free tools.
- jwt.io -- The most well-known JWT tool. Decodes client-side and offers optional signature verification. Clean interface. However, the signature verification feature encourages pasting your secret key into a browser, which is risky if the page ever adds server-side features or gets compromised.
- jwt.ms (Microsoft) -- Client-side decoder designed for Azure AD tokens. Good for decoding tokens from Microsoft identity platforms but has a narrow focus.
- token.dev -- Modern interface, client-side, supports multiple token formats. Clean design but less established.
For routine decoding during development and debugging, QTool's JWT Decoder gives you the fastest workflow: paste the token, see the decoded output immediately, check the expiration in human-readable format, and move on. No account, no configuration, no risk.
Frequently Asked Questions
Is it safe to paste a JWT token into an online decoder?
It depends on whether the decoder processes the token in your browser or sends it to a server. Client-side decoders like QTool JWT Decoder parse the token entirely in your browser using JavaScript. Your token never leaves your machine. Server-side decoders transmit your token to their backend, which means the server operator could log it. To verify, open your browser's DevTools Network tab while pasting the token. If no outbound request is made, the tool is client-side and safe.
Can you decode a JWT without the secret key?
Yes. The header and payload of a JWT are only Base64URL-encoded, not encrypted. Anyone with the token can decode and read them. The signature section requires the secret key to verify, but not to read the header and payload. This is by design: JWTs are meant to carry readable claims. The signature ensures the claims have not been tampered with, but it does not hide them. This is why you should never put sensitive data like passwords or credit card numbers directly in a JWT payload.
What is the difference between decoding and verifying a JWT?
Decoding means reading the header and payload by Base64URL-decoding the first two segments. No key required. Verifying means checking that the signature is valid, proving the token was issued by a trusted party and has not been modified. Verification requires the signing key: the shared secret for HMAC algorithms (HS256) or the public key for RSA/ECDSA algorithms (RS256, ES256). Online decoders show you the decoded content. Verification should be done server-side in your application code.
How do I decode a JWT in JavaScript without a library?
Split the token on dots to get three parts. Base64URL-decode the first two parts and parse as JSON. The key code: const payload = JSON.parse(atob(token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/'))). The replace calls convert Base64URL characters back to standard Base64 before atob decodes them. This works in any modern browser and in Node.js with a Buffer-based alternative to atob.
Why should I not put sensitive data in a JWT payload?
Because the JWT payload is only encoded (Base64URL), not encrypted. Anyone who obtains the token can decode and read every claim. JWTs are often stored in browser localStorage or cookies and transmitted in HTTP headers, making them accessible to browser extensions, network intermediaries, and anyone with access to the client. Store only non-sensitive identifiers (user ID, role, expiration) in the payload. Keep passwords, payment details, and PII in your database, not in the token.
Summary
Decoding a JWT is a trivial operation -- split on dots, Base64URL-decode, parse JSON. It requires zero secrets and zero network calls. If an online decoder sends your token to a server, it is doing something unnecessary and potentially dangerous.
The rules are simple:
- Always use a client-side decoder. Verify by checking the Network tab in DevTools.
- Never paste your signing secret into a web tool unless you fully trust it and understand the risk.
- Keep JWT payloads minimal. Only include the claims the consuming service actually needs.
- Do signature verification in your backend code, not in a browser tool.
- Treat every JWT as readable by anyone. Design your claims accordingly.
QTool's JWT Decoder does exactly what a JWT decoder should do: decode, display, and stay out of your way. No server calls, no secret key prompts, no tracking. It is part of QTool's collection of 269 free developer tools, all built with the same privacy-first principle.
Need More Developer Tools?
JWT decoding is one of 269 free tools on QTool. Explore Base64 encoding, JSON formatting, password strength checking, and more.
Browse All Free Tools