Skip to main content
DevelopmentFebruary 28, 20266 min read

JWT Token Debugging: Common Issues and Solutions

A practical guide to debugging JWT authentication issues, decoding tokens, and understanding common pitfalls that developers face.

Debug JWT tokens instantly with our JWT Decoder. Inspect headers, payloads, and signatures without sending data to servers.

The Mystery Error Message

Your API returns 'Invalid token'. That is it. No details. No clues. Just 'Invalid token'.

You check the token. It looks fine. You check the expiration. Still valid. You check the signature... wait, which key was used to sign this?

JWT debugging is notoriously opaque. Error messages are vague for security reasons. But that makes debugging a nightmare. Here is how to systematically debug any JWT issue.

JWT Anatomy: Three Parts

A JWT looks like this: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U

It has three parts separated by dots: header.payload.signature. Each part is Base64Url encoded.

The header contains metadata like the algorithm (alg) and token type. The payload contains claims like user ID (sub), expiration (exp), and issuer (iss). The signature proves the token has not been tampered with.

The JWT Debugging Checklist

When a JWT fails, work through this checklist systematically:

  • Check expiration (exp) - Is the token still valid? Compare Unix timestamp to current time
  • Verify algorithm (alg) - Does it match what your verifier expects? Watch for 'none' algorithm attacks
  • Validate issuer (iss) - Is the token from the expected source?
  • Check audience (aud) - Is the token intended for your service?
  • Verify signature - Do you have the correct secret key or public key?
  • Inspect custom claims - Are required claims present and valid?

Common JWT Errors and Solutions

Here are the most common JWT errors and how to fix them:

TokenExpiredError

The exp claim is in the past. Solution: Refresh the token using your refresh token flow. If you do not have refresh tokens, make the user log in again.

JsonWebTokenError: invalid signature

The signature does not match. Causes: Wrong secret key, token was tampered with, or algorithm mismatch. Verify you are using the same key that signed the token.

Wrong algorithm

Your verifier expects HS256 but the token uses RS256, or vice versa. Check the alg header and ensure your verification code matches.

Security Best Practices

Never put sensitive data in the JWT payload. The payload is Base64 encoded, not encrypted. Anyone can decode it.

Use short expiration times. 15 minutes is standard for access tokens. Use refresh tokens for longer sessions.

When refreshing tokens, invalidate the old refresh token. Do not let users accumulate valid tokens.

Store tokens securely. In browsers, use httpOnly cookies. In mobile apps, use secure storage like Keychain or Keystore.

Warning: Never paste production JWTs into online decoders you do not trust. Our decoder runs entirely in your browser—your token never reaches our servers.

JWT in Practice

Here is how to work with JWTs in popular programming languages:

Node.js (jsonwebtoken)

600 dark:text-purple-400">const jwt = 600 dark:text-blue-400">require(600 dark:text-purple-400">class="text-green-600 dark:text-green-400">'jsonwebtoken');
 
600 dark:text-purple-400">class=600 dark:text-purple-400">class="text-green-600 dark:text-green-400">"text-gray-500 dark:text-gray-400">// Verify token
600 dark:text-purple-400">const decoded = jwt.600 dark:text-blue-400">verify(token, secretKey);
console.600 dark:text-blue-400">log(decoded.sub); 600 dark:text-purple-400">class=600 dark:text-purple-400">class="text-green-600 dark:text-green-400">"text-gray-500 dark:text-gray-400">// User ID
console.600 dark:text-blue-400">log(decoded.exp); 600 dark:text-purple-400">class=600 dark:text-purple-400">class="text-green-600 dark:text-green-400">"text-gray-500 dark:text-gray-400">// Expiration

Python (PyJWT)

600 dark:text-purple-400">import jwt
 
600 dark:text-purple-400">class=600 dark:text-purple-400">class="text-green-600 dark:text-green-400">"text-gray-500 dark:text-gray-400"># Verify token
decoded = jwt.600 dark:text-blue-400">decode(token, secret_key, algorithms=[600 dark:text-purple-400">class="text-green-600 dark:text-green-400">'HS256'])
600 dark:text-blue-400">print(decoded[600 dark:text-purple-400">class="text-green-600 dark:text-green-400">'sub']) 600 dark:text-purple-400">class=600 dark:text-purple-400">class="text-green-600 dark:text-green-400">"text-gray-500 dark:text-gray-400"># User ID
600 dark:text-blue-400">print(decoded[600 dark:text-purple-400">class="text-green-600 dark:text-green-400">'exp']) 600 dark:text-purple-400">class=600 dark:text-purple-400">class="text-green-600 dark:text-green-400">"text-gray-500 dark:text-gray-400"># Expiration

Frequently Asked Questions

Can I verify the signature in your tool?

We show the signature but do not verify it. To verify, you need the secret key. Never paste production secrets into online tools. For local verification, use libraries like jsonwebtoken (Node.js) or PyJWT (Python) with your secret key.

Is my JWT data safe in your decoder?

Yes. Our decoder runs entirely in your browser using JavaScript. The token never reaches our servers. You can verify this by opening developer tools (F12), clicking the Network tab, and decoding a token. You will see zero network requests to our servers.

What claims should I include in my JWT?

Always include sub (subject/user ID), exp (expiration timestamp), and iat (issued at timestamp). Optionally include iss (issuer), aud (audience), jti (unique token ID), and custom claims like roles or permissions. Never include passwords or sensitive personal information.

Why does my token expire so quickly?

Short expiration (15-30 minutes) is a security feature. If a token is stolen, the attacker has limited time to use it. Use refresh tokens with longer expiration (7-30 days) to maintain sessions. When the access token expires, use the refresh token to get a new one without re-authenticating.

HS256 vs RS256: Which should I use?

HS256 (HMAC with SHA-256) uses a shared secret key—simple for single-server applications. RS256 (RSA with SHA-256) uses public/private key pairs—better for distributed systems where multiple services need to verify tokens. RS256 allows services to verify tokens without knowing the private key used to sign them.

Where should I store JWTs in the browser?

For maximum security, store tokens in httpOnly cookies (not accessible to JavaScript). If using localStorage or sessionStorage, be aware of XSS attacks. Never store tokens in plain text or expose them in URLs. For mobile apps, use Keychain (iOS) or Keystore (Android).

How do I debug 'Invalid token' errors?

First, decode the token to inspect its contents. Check if exp (expiration) is in the past. Verify the alg (algorithm) matches your verifier. Ensure iss (issuer) and aud (audience) are correct. If signature verification fails, confirm you are using the exact same secret key that signed the token.

What is the difference between access and refresh tokens?

Access tokens are short-lived (minutes) and grant access to resources. Refresh tokens are long-lived (days/weeks) and only used to obtain new access tokens. Never send refresh tokens with API requests. Store them more securely than access tokens. When refreshing, invalidate the old refresh token to prevent token accumulation.

JWTtoken debuggingauthenticationJWT decoderdeveloper tools