JWT Generator_

Build a header and a payload, sign with a shared secret, and read the token back. Everything happens in this tab — but the more useful point is what a signed token does and does not prove, because the answer decides whether HS256 is the right algorithm for your system at all.

A symmetric signature has one property people rarely think through: anybody who can verify your tokens can also mint them. The secret does both jobs. If you have handed it to a service so it can check tokens, you have handed it the ability to forge them too — and that is exactly the problem RS256 was invented to solve.

toolkit.codes/jwt-generator
Signed token

The secret above never leaves this tab. Even so: a token minted from a secret you typed into a browser is a test artefact. Production tokens should come from the service that owns the key.

UTF-8
Ready
100% LOCAL
Input
A header and a payload as JSON, plus a shared secret. The secret can be plain text or base64, which matters because half the libraries in circulation assume one and half assume the other.
Output
A complete JWT: header, payload and signature, base64url-encoded and joined by dots. Valid input to any HS256 verifier that holds the same secret.
Processing
Signed in this tab with the browser's own HMAC implementation. No key is generated, stored or transmitted.
Limits
HS256, HS384 and HS512 only. RS256 and ES256 need a private key, and a page that asked you to paste one would be asking for the wrong thing.
The payload is readable by anyone
A JWT is signed, not encrypted. The middle segment is base64url — not a cipher — so any recipient, any proxy and anyone reading a log can decode it in one step. Signing proves the token was not altered; it does nothing to keep the contents private. Email addresses, internal identifiers and role names in a payload are all public to whoever holds the token.

Signing with a shared secret is an architectural choice

Whoever can verify can also forge

HS256 uses one secret for both operations. Signing computes an HMAC over the header and payload; verifying computes the same HMAC and compares. That means the capability to check a token is identical to the capability to create one, and there is no way to grant the first without the second. Share the secret with a partner so their gateway can validate your tokens, and their gateway can now issue tokens your own service will accept as genuine. Nothing in the token records which side made it.

Which is what RS256 is for

An asymmetric algorithm splits the two capabilities. The private key signs and never leaves the issuer; the public key verifies and can be published to anybody, which is what a JWKS endpoint is. If more than one party needs to validate your tokens, or if the validator is a service you do not control, that split is the whole reason to prefer RS256 over HS256 — not performance, and not key length. This page signs with HMAC only, and if that architecture does not fit your system, the answer is a different algorithm rather than a longer secret.

The claims that decide whether a token is safe

exp is the one that matters most and the one most easily left out: a token without it is valid forever, and a leaked one stays valid forever with it. iat records when it was issued, nbf when it becomes usable. aud and iss are what stop a token minted for one service being replayed against another that happens to share the secret — and a verifier that checks the signature without checking those two is accepting anything correctly signed. All of them are seconds since 1970, not milliseconds, which is the single most common off-by-a-thousand in this format.

The algorithm in the header is not a fact

The header travels with the token and is written by whoever made it, so a verifier that reads alg and then trusts it is taking instructions from the attacker. The historic version of this bug accepted alg: none and skipped verification entirely; the subtler one accepts an HS256 token where RS256 was expected, using the public key as the HMAC secret — and the public key is public. Verifiers should pin the algorithm they expect rather than read it.

Where the secret should actually come from

An HMAC secret should be random bytes from a cryptographic source, sized to the algorithm consuming it — the secret key generator states the requirement and produces one — and stored where application secrets are stored rather than in a repository. A memorable phrase is a password, and passwords are guessable at a rate that makes signature forgery a dictionary attack. The default in the box above is deliberately a sentence rather than a plausible-looking key, so that nothing on this page can be mistaken for something to deploy.

Edit the claims, sign, copy

  1. 01Edit the payload. The button above it fills in iat and exp with real timestamps, which is faster than working them out and harder to get wrong.
  2. 02Put in your secret. If your library stores it base64-encoded, tick the box — signing the wrong interpretation produces a token that fails verification with no clue why.
  3. 03Read the advice under the panes. It appears when a claim is missing or shaped wrongly, which is not visible from looking at the token.
  4. 04Copy the token. To read one back apart, the decoder page shows the claims and checks a signature.

A test token for a protected endpoint

You need a valid Authorization header to exercise an API locally. Minting one with the development secret is faster than logging in through the whole flow.

Payload
{
  "sub": "user-42",
  "role": "admin",
  "exp": 1785200000
}
Header to send
Authorization: Bearer eyJhbGciOi…

A token that never expires

Leaving out exp is the commonest mistake and it is invisible in the output — every JWT looks the same from the outside. A leaked token with no expiry is a permanent credential.

Missing the claim
{ "sub": "user-42" }
What the page tells you
No exp claim. This token is valid
forever, including after it leaks.

Milliseconds where seconds were expected

JavaScript hands you milliseconds and JWT wants seconds. The token is well-formed, and every verifier reads the expiry as roughly the year 58000.

Date.now() straight in
{ "exp": 1785200000000 }
What it should be
{ "exp": 1785200000 }

Math.floor(Date.now() / 1000)

Sharing verification without sharing minting

A partner needs to validate your tokens. With HS256 that means giving them the ability to issue tokens too, which is usually not what was intended.

HS256
one secret · signs AND verifies
RS256
private key signs · public key verifies
publish the public half at /.well-known/jwks.json

The registered claims, and what each one prevents

ClaimMeaningWhat goes wrong without it
expExpiry, in seconds since 1970The token is valid forever — including after it leaks. The one claim never to omit.
iatIssued atNo way to reject tokens older than a policy allows, or to detect a clock problem.
nbfNot valid beforeA token intended for later use works immediately. Rarely needed, occasionally essential.
audIntended audienceA token minted for one service is accepted by another that shares the secret.
issIssuerA verifier cannot tell which system produced the token, so it cannot pin trust to one.
subSubject — who the token is aboutThe application has to infer identity from custom claims, which every service does differently.
jtiUnique token identifierNo way to revoke one token, because there is nothing to name in a deny list.

All the time claims are seconds since 1970, not milliseconds. Passing Date.now() directly is the most common error in this format and produces a token that expires several thousand years from now without any warning.

Minting tokens without regret

  • Always set exp, and keep it short. A refresh token exists so that the access token can be measured in minutes rather than months.
  • Use random bytes for the secret, at least 32 of them for HS256. A memorable phrase is a password, and forging a signature then becomes a dictionary attack.
  • Check whether your library expects the secret as text or as base64. Signing one interpretation and verifying the other fails with no useful message on either side.
  • Pin the algorithm in your verifier rather than reading it from the header. The header is written by whoever made the token, including an attacker.
  • Put nothing in the payload you would not publish. It is base64, not encryption, and anyone holding the token can read every claim.

What a signature does not do

It does not hide anything

The payload is base64url-encoded, which is an encoding rather than a cipher. Any holder, proxy or log reader decodes it in one step.

It does not identify which party signed

With a shared secret, every holder of that secret produces identical signatures. A token proves the secret was involved, not who used it.

It does not expire on its own

Expiry is a claim a verifier chooses to check. A token with no exp, or a verifier that ignores it, produces a credential that never ages.

It does not survive being revoked

There is nothing to revoke. A signed token stays valid until it expires, which is why short lifetimes and a jti deny list exist.

Algorithms, encoding and keys

Algorithms
HS256, HS384 and HS512, computed with the browser's own HMAC. The header is written to match whichever you select, so the token is self-consistent.
Not offered
RS256 and ES256. Both need a private key, and a page inviting you to paste one would be teaching the wrong habit — those belong to the service that owns the key.
Secret handling
Interpreted as UTF-8 text by default, or decoded from base64 when the box is ticked. Nothing is stored, generated or sent.
Encoding
base64url for all three segments — the URL-safe alphabet with padding removed, joined by dots, exactly as RFC 7519 specifies.
Claims
Whatever you write. Registered claims are checked for shape and flagged when they look wrong; nothing is added or removed without you asking.
Network
None from tool code. A test sweep calls every function this page uses with fetch and XMLHttpRequest replaced by stubs that throw, so a stray request fails the build instead of shipping. Disconnect from the network and the page still works.

Questions about creating JWTs

Is it safe to generate a JWT in a browser?

For a test token, yes — nothing here is transmitted. For anything production, the objection is not the page but the practice: a token signed with a secret you typed by hand is a test artefact, and real tokens should be issued by the service that owns the key so that the key never travels.

What is the difference between HS256 and RS256?

HS256 uses one shared secret for signing and verifying, so anyone who can validate a token can also create one. RS256 splits them: a private key signs and a public key verifies, so validation can be delegated to anybody without granting them the ability to issue. That is the reason to choose between them — not speed, and not key length.

How long should a JWT secret be?

RFC 7518 ties it to the algorithm: 32 random bytes for HS256, 64 for HS512. What matters more here is where they come from — a memorable phrase turns signature forgery into a dictionary attack, whatever its length. The secret key generator produces a correctly sized one.

Why does my token fail verification?

Most often the secret is being interpreted differently on the two sides: one treats it as UTF-8 text and the other decodes it as base64. After that, check that the algorithm matches, that no whitespace crept into the copied token, and that exp is in seconds rather than milliseconds.

Should exp be in seconds or milliseconds?

Seconds since 1970, for every time claim in the specification. JavaScript gives milliseconds, so Math.floor(Date.now() / 1000) is the conversion — pass Date.now() directly and the token expires thousands of years from now with nothing to indicate a problem.

Can I put sensitive data in the payload?

No. The payload is base64url-encoded, which anyone holding the token can reverse instantly, and tokens end up in logs, proxies and browser storage. Sign what you must prove; store what must stay private on the server.

How do I revoke a JWT?

You cannot, in the general case — that statelessness is the point of the format. The practical answers are short expiry times so a leaked token dies quickly, a jti claim plus a deny list for the tokens you must kill early, and a refresh token that can itself be revoked.

What is the alg: none attack?

A verifier that reads the algorithm from the header rather than pinning its own can be handed a token claiming no signature at all, and older libraries accepted it. The modern variant hands an RS256 verifier an HS256 token so that the public key gets used as the HMAC secret. Both are fixed the same way: decide the algorithm in the verifier.

Does my secret leave the browser?

No. The work is JavaScript running in this tab. Every function it calls is covered by a test that stubs fetch and XMLHttpRequest to throw, so a request that slipped in would break the build rather than reach a server — and you can confirm it for yourself by disconnecting and carrying on.