Free tool
JWT generator
Build a JSON Web Token for testing: edit the claims, pick an expiry, and sign with a secret or a private key. Need keys? Generate an RSA, EC or Ed25519 key pair and get the public key as PEM and JWKS too. Nothing leaves your browser.
Free · no sign-up · runs in your browser, nothing is uploaded
This secret is 0 bits. RFC 7518 requires at least 256 bits for HS256.
The token appears here as you type.
Signing and key generation run in your browser with the Web Crypto API. Keys and claims are never sent anywhere or put in the URL. Use these keys for testing only.
How a JWT is signed
- Write the header, at least
{"alg":"HS256","typ":"JWT"}, and the payload with your claims. - Serialize each as JSON (UTF-8) and Base64URL-encode it without padding.
- Join them with a dot. This string,
header.payload, is the signing input. - Sign the signing input with the algorithm from the header, and Base64URL-encode the signature.
- The token is
header.payload.signature.
The verifier repeats step 4 over the exact characters it received, so any change to the header or payload, even reordering JSON keys, breaks the signature. Paste the result into the JWT decoder to read it back and check it with the key.
Which algorithm to choose
| alg | Key | Signature size | Good for |
|---|---|---|---|
| HS256 | Shared secret, at least 256 bits | 32 bytes | One service that issues and checks its own tokens |
| RS256 | RSA key pair, 2048 bits or more | Key size (256 bytes at 2048 bits) | The most widely supported choice for tokens others verify |
| PS256 | RSA key pair | Key size | Like RS256 with the newer RSA-PSS padding |
| ES256 | EC key pair on P-256 | 64 bytes | Short tokens and small keys, widely supported |
| EdDSA | Ed25519 key pair | 64 bytes | Fast and simple, but check that your library supports it |
RFC 7518 makes HS256 the one algorithm every implementation must support and recommends RS256 and ES256. HS384/HS512, RS384/RS512, PS384/PS512 and ES384/ES512 use SHA-384 or SHA-512 and bigger keys or curves; ES512 runs on P-521, not P-512. The ECDSA signature in a JWT is the R and S values joined (64 bytes for ES256), not the DER format OpenSSL prints.
Generate a JWT in code
Node.js with jsonwebtoken (HS256 is its default):
const jwt = require('jsonwebtoken');
const token = jwt.sign({ sub: 'user_123', role: 'admin' }, process.env.JWT_SECRET, { expiresIn: '1h' });
// RS256 with a PEM private key
const rs = jwt.sign({ sub: 'user_123' }, privatePem, { algorithm: 'RS256', keyid: 'key-1', expiresIn: '15m' });JavaScript with jose (Node, browsers, edge):
import { SignJWT, importPKCS8 } from 'jose';
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
const token = await new SignJWT({ role: 'admin' })
.setProtectedHeader({ alg: 'HS256' })
.setSubject('user_123')
.setIssuedAt()
.setExpirationTime('1h')
.sign(secret);
const key = await importPKCS8(privatePem, 'ES256');
const es = await new SignJWT({}).setProtectedHeader({ alg: 'ES256', kid: 'key-1' }).setExpirationTime('15m').sign(key);Python with PyJWT (pip install pyjwt[crypto] for RS and ES):
import jwt, datetime as dt
now = dt.datetime.now(dt.timezone.utc)
token = jwt.encode({"sub": "user_123", "iat": now, "exp": now + dt.timedelta(hours=1)}, secret, algorithm="HS256")Go with golang-jwt v5:
claims := jwt.MapClaims{"sub": "user_123", "exp": time.Now().Add(time.Hour).Unix()}
token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(secret))OpenSSL, for secrets and key pairs:
openssl rand -base64 32 # HS256 secret openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private.pem # RS256 / PS256 openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out private.pem # ES256 openssl genpkey -algorithm ed25519 -out private.pem # EdDSA openssl pkey -in private.pem -pubout -out public.pem # public key
Claims to set
| Claim | Set it to | Why |
|---|---|---|
| exp | now + lifetime, in seconds | Without it the token never expires. |
| iat | now, in seconds | Lets servers reject tokens that are too old. |
| iss | Your issuer URL or name | Servers check who issued the token. |
| aud | The API or client the token is for | Stops a token for one service being used at another. |
| sub | The user or client ID | Who the token is about. |
| jti | A random unique ID | Lets servers detect a replayed token. |
Times are seconds since 1970, not milliseconds: in JavaScript that is Math.floor(Date.now() / 1000). The payload is readable by anyone who has the token, so never put passwords or other secrets in it. For random IDs and secrets elsewhere, see the UUID generator and the password generator.
Test tokens and security
- Keys made on any website, this one included, are for testing. Generate production keys on the server or in a key manager, and never share the private key.
- Do not use a human-memorable password as an HMAC secret. RFC 8725 says so because short secrets can be brute-forced offline from a single token.
- Verifiers must pin the algorithms they accept and reject alg "none" unless it was asked for on purpose.
- Publish public keys as a JWKS with a
kidfor each key, so you can rotate keys without breaking tokens that are still valid. The generator gives you that JWKS.
More free developer tools
Looking for a full product instead? See security tools on DevHunt.
FAQ
How do I generate a JWT token?+
Pick an algorithm, write the payload claims as JSON, and sign the Base64URL header and payload with a secret (HS256) or a private key (RS256, ES256). The generator above does it as you type. In code, use jsonwebtoken's jwt.sign() in Node, jose's SignJWT in any JavaScript runtime, or jwt.encode() from PyJWT in Python.
How do I generate a secure JWT secret key?+
Use random bytes, not a password. RFC 7518 requires an HMAC key at least as long as the hash: 32 bytes for HS256, 48 for HS384, 64 for HS512. openssl rand -base64 32 prints a good HS256 secret, and the New random secret button here makes one with the browser's crypto.getRandomValues().
What is the difference between HS256 and RS256?+
HS256 signs with a shared secret: whoever can verify the token can also create one. RS256 signs with an RSA private key and verifies with the public key, so many services can check tokens while only the issuer can make them. Use HS256 when one service both issues and checks tokens, and RS256 or ES256 when others must verify them.
How long should a JWT be valid?+
No RFC sets a number. Access tokens are commonly given minutes to an hour, because a JWT usually cannot be revoked before exp, and a refresh token is used to get a new one. Pick the shortest lifetime your app can live with; the expiry presets here go from 5 minutes to 30 days.
Can I create a JWT without a signature?+
Yes, with alg "none" the token ends with a dot and has no signature. RFC 7518 defines it, but RFC 8725 says libraries should not accept it unless the caller asks for it, because anyone can forge such a token. It is useful for testing that your server rejects it.
What is kid and do I need it?+
kid (key ID) is a header field naming the key that signed the token. You need it once you have more than one key, for example while rotating keys: the verifier looks up the kid in your JWKS. When you generate a key pair here, kid is set to the key's RFC 7638 thumbprint, a stable hash of the public key.
How do I generate an RSA or EC key pair for JWT?+
With OpenSSL: openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private.pem for RS256, or openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out private.pem for ES256, then openssl pkey -in private.pem -pubout -out public.pem. This generator makes a key pair in the browser as soon as you pick RS256, PS256, ES256 or EdDSA, and gives the public key as PEM and as a JWKS.
Sources: RFC 7519 (JWT) · RFC 7518 (JWA algorithms) · RFC 7517 (JWK) · RFC 7638 (JWK thumbprint) · RFC 8037 (EdDSA in JOSE) · RFC 8725 (JWT best practices) · jsonwebtoken (Node.js) · jose (JavaScript) · PyJWT · golang-jwt · OpenSSL genpkey · MDN: SubtleCrypto.sign(). Checked 2026-10-30.