{Inspector}

Developer Guide · 2026

Decode a JWT from the Command Line — Nowhere Near a Browser

A JWT is three Base64URL strings separated by dots. That means decoding it is a two-line shell job — no website, no library, no token leaving your machine. This guide gives you copy-paste commands for bash, Python, PowerShell and Node.js, then explains the one thing you can’t do without the secret: verifying the signature.

Benjamin Rotshtein

Written by Benjamin Rotshtein

Updated

How do you decode a JWT locally?

A JWT is three base64url segments separated by dots. The header and payload are plain JSON, so you can read them by simply decoding segment two with atob() — no server, no secret required. The signature (segment three) can’t be verified without the signing key, but decoding the claims is always safe.

What you’re actually decoding

A token looks like eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMifQ.sflKxwRJSMeK. Split on the dots and you get three pieces:

Segments 1 and 2 are pure Base64URL. Decoding them is how you read the token; it is not decryption and it leaks nothing new — the data was never meant to be hidden, just integrity-protected.

Decode in a bash one-liner

JWT="eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMifQ.sflKxwRJSMeK"
echo "$JWT" | cut -d. -f2 | base64 -d 2>/dev/null | base64 | tr -d '='   # raw.
# Pretty-printed, with the header too:
for f in 1 2; do
  echo "$JWT" | cut -d. -f$f | tr '_-' '/+' | base64 -d 2>/dev/null
  echo
done

The tr '_-' '/+' step is required on macOS and many Linux distros: JWT uses the Base64URL alphabet, while base64 expects standard Base64. Ubuntu’s coreutils tolerate a missing "=" padding, but macOS does not — pad to a multiple of four first if a decode silently returns nothing.

Decode in Python (no library)

python3 - <<'EOF'
import base64, json, sys
jwt = sys.argv[1] if len(sys.argv) > 1 else input("JWT: ")
def dec(seg):
    seg += "=" * (-len(seg) % 4)          # re-add padding
    return base64.urlsafe_b64decode(seg)
header = json.loads(dec(jwt.split(".")[0]))
payload = json.loads(dec(jwt.split(".")[1]))
print(json.dumps(header, indent=2))
print(json.dumps(payload, indent=2))
EOF

That is everything PyJWT does to decode. If you also need to verify the signature, use pyjwt.decode(jwt, secret, algorithms=["HS256"]) — hand-rolling HMAC-SHA256 verification is where subtle bugs live.

Decode in PowerShell

$jwt = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMifQ.sflKxwRJSMeK"
$parts = $jwt.Split(".")
foreach ($seg in $parts[0..1]) {
  $padded = $seg + ("=" * ((4 - ($seg.Length % 4)) % 4))
  [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($padded.Replace('-','+').Replace('_','/')))
  ""
}

Decode in Node.js

node -e '
const jwt = process.argv[1] || "";
const [h, p] = jwt.split(".");
const dec = s => JSON.parse(Buffer.from(s, "base64url").toString());
console.log(JSON.stringify(dec(h), null, 2));
console.log(JSON.stringify(dec(p), null, 2));
' "$JWT"

Node 16+ has base64url built into Buffer, which handles the alphabet switch and padding for you.

The line you can’t cross without the secret

Decoding reads; verifying checks. Without the signing secret (HS256) or the issuer’s public key (RS256), you can display every claim but cannot prove the token was issued by your server and not tampered with. Any offline workflow that only decodes should be used for debugging — never as an authorization gate in production, where a library verifies the signature on every request.

Frequently asked questions

Can you decode a JWT without the secret?

Yes — the header and payload of a JWT are only Base64URL-encoded, never encrypted. Anyone can decode them and read the claims. The signature is the only part that requires the secret; without it you can decode but you cannot verify that the token was not tampered with.

What is the fastest way to decode a JWT in the terminal?

For bash, split the token on dots and Base64-decode the second segment: echo $JWT | cut -d. -f2 | base64 -d 2>/dev/null. Readable output needs the JSON pretty-printed, which is why Python's JWT-aware one-liners or json.tool are more comfortable for long payloads.

How do I decode a JWT in Python without a library?

Decode the payload segment with padding re-added: jwt.split('.')[1] → add '=' padding to a multiple of 4 → base64.urlsafe_b64decode → json.loads. To verify the signature you need the secret plus an HMAC-SHA256 (HS256) computation, which is exactly what the PyJWT library wraps.

Is decoding a JWT on a third-party website safe?

Only if the site claims and actually performs all work client-side in your browser. A JWT often contains your user ID, roles and session claims — a server-side decoder would see them. Local decoding (CLI or a client-side inspector) keeps the token on your machine.

What can I verify locally without the signing secret?

Nothing cryptographic — you can read all claims and check exp/iat yourself, but without the secret you cannot confirm the signature is valid, so a forged token with edited claims would pass an unverified read. Verification always requires the secret (HS256) or the issuer's public key (RS256).

Prefer a visual decode? It never leaves your browser

Decode a JWT in your browser with the header, payload and signature colour-coded — everything runs client-side, nothing is uploaded.

Related guides