JWT Generator
Sign a JSON Web Token of your own to test an endpoint, with the same twelve algorithms a real issuer uses — and without the key ever leaving your browser.
Sign a token of your own to test an endpoint. Everything is computed in this page.
The key and the finished token stay in your browser; neither is sent anywhere, and neither is carried in a share link.
Verify this token in your own code. The snippet follows the algorithm in the header.
Create a Signed JSON Web Token
Most work with tokens is reading them, which is what the JWT Decoder does. Sometimes you need the other direction: a token that does not exist yet, to see how a service reacts to it. A staging API that rejects everything, a middleware you are writing, a test fixture that has to expire in five minutes — all of them need a token you control, and this page signs one.
It opens on the Build a token tab. Everything there — the algorithm menu, the payload, the key — is assembled and signed by your own browser through crypto.subtle.sign. There is no request to a server at any point, so the key you paste is as private as the tab it is typed into.
What the header and payload end up holding
The header is written for you: the algorithm you chose and typ: "JWT", plus a kid if you filled that field in. A key ID matters when the receiving service holds several keys and picks one by name; leave it empty and the header stays at two fields.
The payload is yours to edit. Two claims are not: signing always stamps iat with the current time, and sets exp from the Expires in menu — five minutes, an hour, a day, thirty days, or nothing at all. When the token appears, the payload box is rewritten with exactly what was signed, so the timestamps you see are the timestamps inside it. If those two claims matter to the thing you are testing, the expiry checker explains how a server reads them.
Choosing the algorithm
The twelve algorithms fall into two families, and the difference is not strength but who has to hold the key.
| Family | Key you paste | Use it when |
|---|---|---|
| HS256 / HS384 / HS512 | A shared secret, as text or Base64 | One service issues and verifies its own tokens |
| RS256 / RS384 / RS512 | PKCS#8 private key (PEM) or JWK | Many services verify; they only ever see the public half |
| PS256 / PS384 / PS512 | The same RSA private key | RSA with the newer PSS padding, when the receiver asks for it |
| ES256 / ES384 / ES512 | An EC private key, P-256 / P-384 / P-521 | You want short signatures and small keys |
HS256 is the default here because it is the one people are usually testing against: a secret in an environment variable, one service on both ends. The moment more than one service needs to check the token, a public-key algorithm is the better shape, because the verifier never has to be trusted with anything that could mint tokens.
What a generated token is good for — and what it is not
A token signed here is a real token: paste it into the decoder above, press Verify with the same key, and the signature checks out, because the page computes it the way any library would. That makes it useful for fixtures, for reproducing a bug with an expired or a not-yet-valid token, and for finding out whether your middleware really validates aud or merely reads it.
What it is not is a way in. A token is only accepted by a service that holds the matching key, so signing one with a secret of your own proves nothing to anybody else. That is the whole point of the signature, and it is the reason the difference between a token and a session cookie comes down to who has to store what.
Keys, formats and the errors they cause
For the HS family, paste the secret as plain text, or tick Secret is Base64 if your configuration stores it encoded — the two produce entirely different signatures from the same string, which is a common reason a token verifies in one place and fails in another.
For RS, PS and ES, the browser accepts a PKCS#8 PEM — the block that begins -----BEGIN PRIVATE KEY----- — or a JWK, the JSON form a key set hands out. An older -----BEGIN RSA PRIVATE KEY----- block is PKCS#1 and has to be converted first. If you paste a public key by mistake the page says so instead of failing with a cryptic error, because a public key can check a signature but never make one.