Decode JWT Offline in Browser (Privacy-First)
Decode JWTs locally in your browser without sending data to a server.
Decode JWTs privately in your browser to reduce risk during debugging. Understanding why offline decoding matters and how to implement it helps you inspect tokens safely without exposing sensitive data to third parties.
Why offline decoding matters
Security benefits
1. No data transmission
- Tokens never leave your device
- No network requests means no interception risk
- Works even when offline or on isolated networks
2. Privacy protection
- Third-party services can't log your tokens
- No analytics or tracking of your debugging sessions
- Complete control over your data
3. Compliance
- Meets data protection requirements (GDPR, HIPAA)
- No third-party data processing
- Suitable for sensitive environments
When to use offline decoding
Use offline decoding when:
- Debugging production issues with real tokens
- Working with sensitive or regulated data
- Inspecting tokens from security incidents
- Testing in isolated/air-gapped environments
- You want maximum privacy
You can use online tools when:
- Working with test/dummy tokens
- Learning JWT structure
- Quick validation of token format
- Non-sensitive development work
Browser-only implementation
Complete offline decoder
Here's a full-featured browser-only JWT decoder:
<!DOCTYPE html>
<html>
<head>
<title>Offline JWT Decoder</title>
</head>
<body>
<textarea id="tokenInput" placeholder="Paste JWT token here"></textarea>
<button onclick="decodeToken()">Decode</button>
<pre id="output"></pre>
<script>
// Normalize Base64URL to Base64
function normalizeBase64URL(str) {
str = str.replace(/-/g, '+').replace(/_/g, '/');
const padding = (4 - (str.length % 4)) % 4;
return str + '='.repeat(padding);
}
// Decode Base64URL segment
function decodeSegment(segment) {
try {
const normalized = normalizeBase64URL(segment);
const decoded = atob(normalized);
return JSON.parse(decoded);
} catch (error) {
throw new Error(`Failed to decode segment: ${error.message}`);
}
}
// Decode JWT
function decodeJWT(token) {
// Validate token format
if (!token || typeof token !== 'string') {
throw new Error('Token must be a non-empty string');
}
const parts = token.trim().split('.');
if (parts.length !== 3) {
throw new Error('Invalid JWT format. Expected 3 parts separated by dots.');
}
const [headerSegment, payloadSegment, signatureSegment] = parts;
return {
header: decodeSegment(headerSegment),
payload: decodeSegment(payloadSegment),
signature: signatureSegment, // Keep signature as-is (don't decode)
raw: {
header: headerSegment,
payload: payloadSegment,
signature: signatureSegment
}
};
}
// Main decode function
function decodeToken() {
const token = document.getElementById('tokenInput').value.trim();
const output = document.getElementById('output');
if (!token) {
output.textContent = 'Please paste a JWT token';
return;
}
try {
const decoded = decodeJWT(token);
// Format output
output.textContent = JSON.stringify({
header: decoded.header,
payload: decoded.payload,
// Show expiration time if present
expiresAt: decoded.payload.exp
? new Date(decoded.payload.exp * 1000).toISOString()
: null,
issuedAt: decoded.payload.iat
? new Date(decoded.payload.iat * 1000).toISOString()
: null
}, null, 2);
} catch (error) {
output.textContent = `Error: ${error.message}`;
}
}
</script>
</body>
</html>
Why this works:
- Uses browser's built-in
atob()for Base64 decoding - No external libraries or network requests
- Handles Base64URL normalization
- Provides clear error messages
Simplified version
For quick debugging, here's a minimal version:
// Paste this in browser console
function decodeJWT(token) {
const normalize = (s) => {
s = s.replace(/-/g, '+').replace(/_/g, '/');
while (s.length % 4) s += '=';
return s;
};
const [h, p] = token.split('.');
return {
header: JSON.parse(atob(normalize(h))),
payload: JSON.parse(atob(normalize(p)))
};
}
// Usage
const token = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.signature';
console.log(decodeJWT(token));
Using our offline tool
Our JWT Encoder/Decoder runs entirely in your browser:
Features:
- ✅ 100% client-side - No server requests
- ✅ Works offline - No network needed after page load
- ✅ Privacy-first - Your tokens never leave your device
- ✅ Base64URL handling - Automatically normalizes encoding
- ✅ Error highlighting - Shows exactly where decode fails
- ✅ Formatted output - Easy-to-read JSON display
How to verify it's offline:
- Open browser DevTools → Network tab
- Paste a token and decode
- Check Network tab - no requests should appear
- Disconnect internet and try again - still works!
Safety checklist
Before decoding
- ✅ Verify tool is client-side - Check Network tab for requests
- ✅ Use trusted tools - Only use tools you trust or build yourself
- ✅ Check privacy policy - Ensure no data collection
- ✅ Test with dummy tokens first - Verify behavior before using real tokens
During decoding
- ✅ Never paste production secrets - Use test tokens when possible
- ✅ Redact sensitive data - Remove user IDs, emails before pasting
- ✅ Use incognito mode - Extra privacy layer
- ✅ Clear browser cache - After debugging sensitive tokens
After decoding
- ✅ Clear browser history - Remove tokens from browser memory
- ✅ Don't screenshot tokens - Avoid storing decoded tokens in images
- ✅ Verify signature - Decoding isn't verification; always verify in your app
- ✅ Follow security policies - Comply with your organization's data handling rules
Real-world scenarios
Scenario 1: Debugging production authentication
Problem: Production users can't authenticate, need to inspect tokens
Solution:
- Extract token from server logs (redacted)
- Use offline decoder (our JWT Encoder/Decoder)
- Inspect claims without sending token anywhere
- Fix configuration based on decoded information
Why offline: Production tokens contain real user data - must stay private.
Scenario 2: Security incident response
Problem: Need to analyze tokens from a security incident
Solution:
- Extract tokens from incident logs
- Decode offline to preserve evidence chain
- Analyze claims without exposing to third parties
- Document findings securely
Why offline: Security incidents require careful data handling - no third-party exposure.
Scenario 3: Air-gapped environment
Problem: Working in isolated network without internet
Solution:
- Save our JWT Encoder/Decoder page locally
- Open saved HTML file
- Decode tokens without network access
Why offline: Some environments don't allow external network access.
Comparing offline vs online decoders
| Feature | Offline (Our Tool) | Online Decoders |
|---|---|---|
| Privacy | ✅ No data leaves device | ❌ May send to server |
| Network required | ❌ Works offline | ✅ Needs internet |
| Speed | ✅ Instant (no network delay) | ⚠️ Network latency |
| Security | ✅ No third-party exposure | ⚠️ Depends on service |
| Compliance | ✅ Meets strict requirements | ⚠️ May violate policies |
Best practices
1. Always verify tool is offline
How to check:
// In browser console, check for network requests
// Open DevTools → Network tab before decoding
// If any requests appear, tool is NOT fully offline
2. Use browser console for quick checks
Quick decode in console:
// One-liner for quick inspection
const d = t => {const n=s=>s.replace(/-/g,'+').replace(/_/g,'/')+'='.repeat((4-s.length%4)%4);const [h,p]=t.split('.');return{header:JSON.parse(atob(n(h))),payload:JSON.parse(atob(n(p)))}};
// Usage
d('your.token.here');
3. Build your own decoder
If you need custom features:
- Save our decoder code locally
- Customize for your needs
- Use in your development workflow
- Never expose to public if handling sensitive data
4. Handle errors gracefully
function safeDecode(token) {
try {
return decodeJWT(token);
} catch (error) {
// Don't expose full error in production
console.error('Decode failed:', error.message);
return null;
}
}
Common mistakes
Mistake 1: Assuming online tools are safe
What developers think: "Online decoder says it's secure, so it's fine"
Reality: Even if a service claims privacy, you can't verify:
- Whether they log tokens
- If data is stored
- Who has access to servers
- Compliance with regulations
Solution: Use offline tools for sensitive tokens.
Mistake 2: Not verifying offline behavior
What developers do: Use a tool without checking if it's truly offline
Solution: Always check Network tab - no requests should appear when decoding.
Mistake 3: Storing decoded tokens
What developers do: Copy decoded output to notes or screenshots
Why it's risky: Decoded tokens still contain sensitive information that could be misused.
Solution: Only decode when needed, clear browser after use, don't store decoded output.
Advanced: Building a custom decoder
If you need more features, here's an enhanced version:
class OfflineJWTDecoder {
constructor() {
this.cache = new Map(); // Cache decoded tokens (optional)
}
normalizeBase64URL(str) {
return str.replace(/-/g, '+').replace(/_/g, '/') + '='.repeat((4 - str.length % 4) % 4);
}
decodeSegment(segment) {
try {
const normalized = this.normalizeBase64URL(segment);
const decoded = atob(normalized);
return JSON.parse(decoded);
} catch (error) {
throw new Error(`Decode failed: ${error.message}`);
}
}
decode(token, options = {}) {
const { cache = false } = options;
// Check cache
if (cache && this.cache.has(token)) {
return this.cache.get(token);
}
const parts = token.split('.');
if (parts.length !== 3) {
throw new Error('Invalid JWT format');
}
const [headerSegment, payloadSegment, signature] = parts;
const result = {
header: this.decodeSegment(headerSegment),
payload: this.decodeSegment(payloadSegment),
signature,
// Helper properties
expiresAt: null,
isExpired: false,
issuedAt: null,
};
// Calculate expiration
if (result.payload.exp) {
result.expiresAt = new Date(result.payload.exp * 1000);
result.isExpired = Date.now() >= result.payload.exp * 1000;
}
// Calculate issued at
if (result.payload.iat) {
result.issuedAt = new Date(result.payload.iat * 1000);
}
// Cache if requested
if (cache) {
this.cache.set(token, result);
}
return result;
}
}
// Usage
const decoder = new OfflineJWTDecoder();
const decoded = decoder.decode(token, { cache: true });
console.log('Expires:', decoded.expiresAt);
console.log('Is expired:', decoded.isExpired);
Next steps
- Try decoding a token with our JWT Encoder/Decoder - verify it's offline
- Learn about online decoder security myths when choosing tools
- Understand why decoding isn't verification
- Read about safe decoding practices before verification
- See our troubleshooting guide for decode errors
Try JWT Encoder/Decoder Now
Ready to put this into practice? Use our free JWT Encoder/Decoder tool. It works entirely in your browser with no signup required.
Launch JWT Encoder/DecoderFrequently Asked Questions
Q Is the decoder here fully client-side?
Yes. All processing happens in your browser; tokens are never uploaded.
Related Articles
JWT Tokens Explained - Complete Guide to JSON Web Tokens
Learn everything about JWT tokens, how they work, when to use them, and best practices for secure implementation in your applications.
Decode JWT Online — Security Myths vs Facts
Separate myths from facts when using online JWT decoders and learn safe practices.
When Not to Trust a Decoded JWT
Decoding is not validation. Learn when decoded claims are untrustworthy without signature checks.