JWT decode: how to decode a JWT token in Chrome without an online decoder
Updated · 7 min read
To decode a JWT token, split it on the dots and base64url-decode the first two parts. The header and payload are plain JSON that anyone holding the token can read. In Chrome you can find the token in the Network panel (usually in an Authorization: Bearer header) and decode it with a short snippet in the DevTools Console, so the token never leaves your machine.
Steps
- 1
Open the Network panel and reload
Open DevTools (F12, or Cmd+Option+I on a Mac), go to the Network panel, click the Fetch/XHR filter and reload the page so DevTools logs the app's API calls.
- 2
Search for the token
With the Network panel focused, press Ctrl+F (Cmd+F on a Mac) and search for
eyJ. A JWT header is JSON starting with{", which base64url-encodes toeyJ, so this finds tokens in headers, payloads and responses. - 3
Copy the token
Open a matching request, go to the Headers tab and copy the
Authorizationvalue afterBearer. If the app keeps the token in a cookie or storage, look under Application > Storage instead. - 4
Decode it in the Console
Open the Console panel, paste the
decodeJwtsnippet from the next section, and call it with your token. Chrome may ask you to typeallow pastingthe first time you paste into the Console. See how to allow pasting in the Chrome Console if it will not accept the paste. - 5
Read the header and claims
Check
algin the header, thenexp,iat,iss,audandsubin the payload. Convertexpto a date withnew Date(payload.exp * 1000).
What is a JWT?
A JSON Web Token (RFC 7519) is a compact, URL-safe way to pass claims between two parties. Most JWTs you see in a browser are signed tokens in JWS compact form (RFC 7515), three base64url strings joined by dots.
- Header, JSON that names the signing algorithm (
alg) and often the token type (typ). - Payload, JSON with the claims, such as
sub(who the token is about),exp(expiry),iat(issued at),iss(issuer) andaud(audience). - Signature, computed over the encoded header and payload with the issuer's key.
The header and payload are only encoded, so the payload of a signed JWT is readable by anyone who has the token. Only encrypted JWTs (JWE) hide their claims, and those are rare in browser traffic. Time claims such as exp are NumericDate values, meaning seconds since 1970-01-01 UTC.
Base64url decode: how JWT parts are encoded
Base64url (RFC 4648, section 5) is regular base64 with - and _ instead of + and /, so the output is safe in URLs and headers. JWTs also drop the = padding. That is why pasting a JWT part into a plain base64 decoder sometimes fails or returns garbage: the - and _ characters are not in the standard alphabet.
Current Chrome, Firefox and Safari can decode base64url directly with `Uint8Array.fromBase64`, which accepts missing padding by default:
// Any base64url string to text
const base64UrlDecode = (part) =>
new TextDecoder().decode(Uint8Array.fromBase64(part, { alphabet: "base64url" }));
base64UrlDecode("eyJhbGciOiJIUzI1NiJ9"); // '{"alg":"HS256"}'In older browsers, swap - for + and _ for /, then pass the result to atob, as the snippet in the next section does. atob tolerates the missing padding. In Python, add the padding back yourself: base64.urlsafe_b64decode(part + "=" * (-len(part) % 4)).
How to decode a JWT token in JavaScript
This snippet runs in the Chrome DevTools Console and in any modern browser. It swaps the base64url characters back, decodes with atob, and runs the bytes through TextDecoder so non-ASCII names come out correctly.
const decodeJwt = (jwt) =>
jwt.split(".").slice(0, 2).map((part) =>
JSON.parse(new TextDecoder().decode(
Uint8Array.from(atob(part.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0))
))
);
const [header, payload] = decodeJwt("PASTE_TOKEN_HERE");
console.log(header, payload);
console.log("expires", new Date(payload.exp * 1000).toISOString());In Node.js you can skip the character swap, because Buffer understands base64url directly: JSON.parse(Buffer.from(part, "base64url").toString()).
Is it safe to paste a JWT into an online decoder?
For a test token, usually yes. For a live production token, avoid it. Most JWTs are bearer tokens, which means anyone who holds one can call the API as that user until it expires. Pasting it into a website hands that credential to a page you do not control.
- You cannot audit what the page does with the text, today or after its next deploy.
- Other scripts on the page, such as analytics, ads or browser extensions, can read form fields.
- Tokens often carry personal data in their claims, such as emails, names and tenant IDs.
- A leaked access token stays valid until
exp, and many apps cannot revoke a single token early.
Decoding locally with the Console snippet removes all of that. If you already pasted a live token somewhere you do not trust, sign out or rotate the session so the server issues a new one.
What does HS256 mean?
HS256 is HMAC using SHA-256, defined in JSON Web Algorithms (RFC 7518). It is symmetric, so the same shared secret both signs and verifies the token. RFC 7518 requires a key at least as long as the hash output, which is 256 bits for HS256.
RS256 (RSA) and ES256 (ECDSA with P-256) are asymmetric. The issuer signs with a private key and anyone can verify with the public key, so verifying services never hold signing power. That makes them a common choice when several services need to check tokens.
You do not need any key to decode a token, whatever its alg. You need the secret (HS256) or the public key (RS256, ES256) to verify it.
What to check when you inspect a JWT
algisnone. That is an unsecured JWT with an empty signature, and an API should not accept it as proof of identity.expis missing, or the lifetime fromiattoexpis very long. Long-lived bearer tokens raise the cost of a leak.- The token was already expired when the request was sent, which can point to refresh bugs.
- The header has
jkuorx5u. These are URLs to keys or certificates, and a server must never fetch keys from an untrusted URL named in the token. - The payload holds personal or sensitive data. Signed JWT claims are readable, so keep secrets out of them.
- The token appears in a URL query string, where it can land in logs and browser history.
If your app uses HS256 with a weak secret, the issue is on the server side. Decoding in the browser will not show the secret, but a short or guessable key can be brute-forced offline from any captured token.
Doing this with Netraptor
Netraptor is a Chrome extension with a Netraptor panel in DevTools. Every request in its Requests table has a JWT & Security tab that lists each JWT found in headers, cookies, URL params and bodies. For each token it shows where it came from, the alg, any problems it found and a table of decoded claims, with exp and iat shown as dates. Decoding happens locally in your browser.
- Right-click a request and choose Decode JWT to jump straight to the tab.
- The raw token shows trimmed, with Copy and View buttons. View opens the full value with the decoded header and payload. The same buttons appear on header, cookie and query values, so an
Authorization: Bearer eyJ…header decodes in one click. - Click the filter icon next to a claim to show every request with that claim, for example
jwt:role=admin. Filter by algorithm withjwt-alg:HS256. - Open Saved filters for the security presets:
is:alg-none,is:expired(expired when sent),is:no-exp,is:long-lived(over 24 hours),is:key-url(jku/x5uheader),is:piiandhas:url-tokenfor credentials in the URL.
Inside the panel you see real values, because it is your own browser. Exports, Save all as HAR (sanitized) and the local MCP server still redact credentials, and exports strip auth headers and cookies by default. Decoding, the JWT & Security tab, the filters and saved filters are all on the Free plan.
Frequently asked questions
- Can you decode a JWT without the secret key?
- Yes. The header and payload are only base64url-encoded, so anyone with the token can read them. The key is needed to verify the signature, not to read the claims.
- How do I decode a base64url string?
- In a current browser, use
Uint8Array.fromBase64(str, { alphabet: "base64url" })and aTextDecoder. Otherwise replace-with+and_with/, then callatob. In Node.js, useBuffer.from(str, "base64url"). - Is a JWT encrypted?
- A signed JWT (JWS) is not encrypted, only signed. Encrypted JWTs use JWE and look different, with five dot-separated parts instead of three.
- How do I check if a JWT is expired?
- Decode the payload and compare
expwith the current time in seconds:payload.exp < Date.now() / 1000means it has expired. - Where are JWTs stored in the browser?
- Usually in a cookie,
localStorageorsessionStorage, or only in memory. In Chrome, check Application > Storage for cookies and storage, and the Network panel for tokens sent in headers.