A JWT is one of those strings that looks scary the first time you see it. It often appears in a login response, browser storage, or an API request header:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjMiLCJyb2xlIjoidXNlciJ9...

At first glance it looks like random text. The helpful thing to know is that a JWT is usually three pieces joined with dots:

header.payload.signature

The JWT Parser opens those pieces and shows the readable JSON inside. You do not need to install anything or write a script.

This is the way I check a JWT when a login or API request is not behaving as I expect.

Before starting: what problem are we trying to solve?

A JWT is commonly used as a temporary proof that a user has logged in. The frontend sends it to the backend, usually like this:

Authorization: Bearer <token>

When something breaks, the symptom is usually one of these:

  • the app says I am logged in, but the API returns 401 Unauthorized;
  • the token worked a few minutes ago, but now it does not;
  • the wrong user id or role seems to be used;
  • staging and production tokens are mixed up;
  • someone says “the JWT is invalid” but does not say which part is invalid.

The goal is not to become a JWT expert. The goal is to answer simple debugging questions:

  1. Can the token be opened?
  2. Who or what does the token claim to represent?
  3. Has it expired?
  4. Was it signed with the expected key?
  5. Is the app sending it in the right place?

Step 1: copy the whole token

First, copy the JWT exactly as it appears.

A JWT normally has two dots in it:

xxxxx.yyyyy.zzzzz

If you only copy one part, the parser will not be able to read it properly. Be careful not to include extra text like Bearer unless the tool says it accepts it. If the token comes from an HTTP header, the full header may look like this:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

The token is the long part after Bearer.

Step 2: paste it into the JWT Parser

Open JWT Parser and paste the token into the input box.

If the token is well formed, the tool will split it into three areas:

  • Header
  • Payload
  • Signature

If it cannot be split, check for the common copy mistakes:

  • missing beginning or end of the token;
  • spaces inserted into the token;
  • quotation marks copied from JSON;
  • only the payload copied instead of the full token;
  • line breaks added by a terminal or chat app.

Step 3: read the header

The header tells you what kind of token it is and what signing algorithm it claims to use. It may look like this:

{
  "alg": "HS256",
  "typ": "JWT"
}

The most important field here is alg.

Common examples:

  • HS256: signed with a shared secret;
  • RS256: signed with a private key and checked with a public key;
  • PS256: similar to RSA, but with RSA-PSS;
  • ES256: signed with an elliptic-curve key.

As a beginner, I mostly use this field to answer one question:

What kind of key do I need if I want to verify the signature?

For HS256, I need the secret. For RS256 and similar algorithms, I need the public key.

Step 4: read the payload

The payload is the part that usually explains who the token is for and what it is allowed to do.

A small payload might look like this:

{
  "sub": "123",
  "email": "user@example.com",
  "role": "admin",
  "iat": 1790467200,
  "exp": 1790470800
}

The exact fields depend on the application. There is no single payload shape that every JWT must use.

Fields I usually look for:

  • sub: the subject, often the user id;
  • email: the user email, if the app includes it;
  • role: the user’s role, if the app uses role-based access;
  • iss: issuer, meaning who created the token;
  • aud: audience, meaning who the token is meant for;
  • iat: issued at;
  • nbf: not valid before;
  • exp: expires at.

This step often catches simple problems. For example, the token may belong to a different user, a different environment, or an old login session.

Step 5: understand the important warning

Reading the payload does not mean the token is trusted.

Anyone can decode a JWT payload. Anyone can also create a fake payload that says:

{
  "sub": "1",
  "role": "admin"
}

That fake payload may look valid when decoded. The part that prevents people from changing it safely is the signature.

A simple way to think about it:

  • the payload is the message;
  • the signature is the seal;
  • decoding reads the message;
  • verification checks whether the seal still matches.

So the JWT Parser is useful for reading, but the token should only be trusted after signature verification and application-level checks.

Step 6: check the expiry time

The most common JWT problem I see is an expired token.

Look for exp in the payload:

{
  "exp": 1790470800
}

That number is usually a Unix timestamp. It is not easy to read by eye, so copy it into the Timestamp Converter.

Then check:

  1. Is the expiry time in the past?
  2. Is it in the expected timezone when displayed as local time?
  3. Is the token lifetime too short for the flow you are testing?

If exp is already past, the backend should reject the token. That is true even if the signature is valid.

Also check nbf if it exists. nbf means “not before”. If that time is still in the future, the token is not valid yet.

Step 7: verify the signature if you have the key

To verify the signature, you need the right key for the algorithm.

For HMAC tokens such as HS256, paste the shared secret. This is usually something from the backend configuration, environment variables, or test setup.

For public/private key tokens such as RS256, paste the public key. It often looks like this:

-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----

After you provide the key, the parser recomputes the signature and compares it with the signature in the token.

If verification passes, it means:

  • the token has not been changed since it was signed;
  • the key you entered matches the token;
  • the header and payload still match the signature.

If verification fails, common causes are:

  • the wrong secret or public key was used;
  • the token was copied incorrectly;
  • the token was edited;
  • the frontend is using a token from another environment;
  • the backend changed keys and the token was signed with an old key.

Step 8: do not stop at “signature valid”

A valid signature is important, but it is not the whole decision.

An application should still check things like:

  • is the token expired?
  • is the issuer trusted?
  • is the audience correct?
  • does the user still exist?
  • is the role still allowed?
  • was the token issued for this app, or for another service?

This is why a token can be cryptographically valid but still rejected by the API. For example, the signature may pass, but the aud value may be for a different service.

Step 9: check how the token is sent

If the token itself looks fine, check the request.

Most APIs expect this header:

Authorization: Bearer <token>

Common mistakes:

  • sending Bearer without a space;
  • sending the token in the wrong header;
  • sending an old token from browser storage;
  • forgetting to include the header on one request;
  • using a staging token against production API;
  • using a production token against local development API.

A JWT can be perfectly valid and still fail if it is sent to the wrong place.

Step 10: use the right JWT tool for the job

There are three JWT-related tools on this site:

  • JWT Parser — best for opening and checking an existing token;
  • JWT Encode — best for creating a test token from header and payload JSON;
  • JWT Expiry Editor — best when you only want to adjust iat, nbf, or exp.

If I am debugging a real token, I start with the parser. If I am creating a token for a test case, I use the editor. If I only need to test expiry behaviour, I use the expiry editor.

My beginner checklist

When a JWT problem appears, I go through this checklist:

  1. Copy the full token, not just one part.
  2. Paste it into JWT Parser.
  3. Confirm the header has the algorithm I expected.
  4. Read the payload and check the user id, email, role, issuer, and audience.
  5. Convert exp, iat, and nbf with the Timestamp Converter.
  6. If I have the secret or public key, verify the signature.
  7. Check that the API request sends Authorization: Bearer <token>.
  8. If it still fails, compare environment: local, staging, production.

Most JWT issues are not mysterious. They are usually an expired token, a copied partial token, the wrong environment, or a key mismatch.

The most useful distinction is this:

Decoding shows what the token says. Verification checks whether the token should be believed.

Comments

Comments are welcome — please read the comment policy first. Powered by giscus and GitHub Discussions.