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

Header
Payload
Secret
Secret is

This secret is 0 bits. RFC 7518 requires at least 256 bits for HS256.

Signed token

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

  1. Write the header, at least {"alg":"HS256","typ":"JWT"}, and the payload with your claims.
  2. Serialize each as JSON (UTF-8) and Base64URL-encode it without padding.
  3. Join them with a dot. This string, header.payload, is the signing input.
  4. Sign the signing input with the algorithm from the header, and Base64URL-encode the signature.
  5. 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

algKeySignature sizeGood for
HS256Shared secret, at least 256 bits32 bytesOne service that issues and checks its own tokens
RS256RSA key pair, 2048 bits or moreKey size (256 bytes at 2048 bits)The most widely supported choice for tokens others verify
PS256RSA key pairKey sizeLike RS256 with the newer RSA-PSS padding
ES256EC key pair on P-25664 bytesShort tokens and small keys, widely supported
EdDSAEd25519 key pair64 bytesFast 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

ClaimSet it toWhy
expnow + lifetime, in secondsWithout it the token never expires.
iatnow, in secondsLets servers reject tokens that are too old.
issYour issuer URL or nameServers check who issued the token.
audThe API or client the token is forStops a token for one service being used at another.
subThe user or client IDWho the token is about.
jtiA random unique IDLets 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 kid for each key, so you can rotate keys without breaking tokens that are still valid. The generator gives you that JWKS.

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.