How to Generate a JWT Secret Key (Step-by-Step)
Every JWT signed with HMAC algorithms depends on a secret key. If that secret is weak, predictable, or leaked, your entire authentication system is compromised. This guide walks you through generating a production-ready JWT secret the right way.
Why Secret Quality Matters
A JWT secret is not a password you invent — it is raw cryptographic entropy. Attackers who capture any valid token can attempt offline brute-force attacks against weak secrets at millions of guesses per second. A 256-bit randomly generated secret makes this attack computationally infeasible.
Step 1: Choose the Right Key Length
| Algorithm | Minimum Key Size | Recommended |
|---|---|---|
| HS256 | 256 bits (32 bytes) | 256 bits |
| HS384 | 384 bits (48 bytes) | 384 bits |
| HS512 | 512 bits (64 bytes) | 512 bits |
For most applications, 256 bits for HS256 is the correct default. See our guide on JWT secret key length for the full comparison.
Step 2: Generate the Secret
Option A: Browser Tool (Fastest)
Open the JWT Secret Generator, select 256-bit, and click Generate. The key is created using crypto.getRandomValues() entirely in your browser — nothing is sent to a server.
Option B: Node.js
const crypto = require('crypto');
const secret = crypto.randomBytes(32).toString('hex');
console.log(secret); // 64-character hex stringOption C: Python
import secrets
secret = secrets.token_hex(32)
print(secret)Option D: OpenSSL (CLI)
openssl rand -hex 32Step 3: Store It Securely
Never hardcode the secret in source code. Add it to your environment:
JWT_SECRET=your-generated-hex-key-hereFor production, use a secrets manager. Read how to store JWT secrets securely for a full comparison of env vars, Vault, and KMS.
Step 4: Sign and Verify a Test Token
Sign with Node.js:
const jwt = require('jsonwebtoken');
const token = jwt.sign({ userId: 1 }, process.env.JWT_SECRET, {
algorithm: 'HS256',
expiresIn: '1h',
});Verify with our tool: Paste the token and secret into the JWT Validator to confirm the signature is valid.
Understanding HMAC Signing
JWT secrets used with HS256 are inputs to HMAC-SHA256. The algorithm combines your secret with the base64url-encoded header and payload to produce a signature. Verification repeats the same computation — if the result matches the token's third segment, the token is authentic.
This design means:
- Same secret everywhere: Every service that verifies HS256 tokens must possess the signing secret
- No encryption: The payload is only base64-encoded, not encrypted — never store passwords or PII in claims
- Algorithm matters: Always specify
algorithms: ['HS256']in your library to prevent algorithm confusion attacks
If you are building microservices where multiple teams verify tokens, see the HS256 vs RS256 comparison to decide whether RS256 is a better long-term fit. For monoliths and small APIs, HS256 with a strong secret is the right default.
Separate Secrets Per Environment
A common mistake is copying the same secret from development into production. Treat each environment as a separate security boundary:
# .env.development
JWT_SECRET=dev-only-secret-never-use-in-prod
# Production (set via hosting platform or secrets manager)
JWT_SECRET=production-256-bit-random-secretIf a developer laptop is compromised, a dev-only secret limits exposure to non-production data. Generate a fresh secret for each environment using the JWT Secret Generator.
Verify in Other Languages
Python verification:
import jwt, os
payload = jwt.decode(token, os.environ['JWT_SECRET'], algorithms=['HS256'])
print(payload)Go verification:
token, err := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {
return []byte(os.Getenv("JWT_SECRET")), nil
})Run these against tokens you build with the JWT Encoder to confirm your stack is wired correctly before deploying.
When to Rotate Your Secret
Rotate immediately if you suspect the secret was committed to git, logged in an error message, or exposed in a breach. For routine hygiene, rotate on a schedule (quarterly is reasonable for most apps) using the kid header — see our key rotation guide for zero-downtime steps.
Common Mistakes to Avoid
- Using a human-readable passphrase instead of random bytes
- Reusing the same secret across development and production
- Committing secrets to git (even in private repos)
- Using keys shorter than 128 bits
Next Steps
- Follow the JWT Best Practices Checklist before going live
- Set up key rotation for long-running services
- Compare HS256 vs RS256 if you are choosing an algorithm
How Browser-Based Generation Works
The JWT Secret Generator uses the Web Crypto API (crypto.getRandomValues()) to produce cryptographically secure random bytes entirely in your browser. Nothing is transmitted to a server — your secret never leaves your machine.
This matters because:
- No network exposure — the secret cannot be intercepted in transit
- No server-side logging — no risk of secrets appearing in server logs or databases
- Instant results — generate, copy, and move on in under a second
- Multiple formats — hex, Base64, and raw byte representations
What Gets Generated
| Setting | Value | Why |
|---|---|---|
| Default length | 256 bits (32 bytes) | Matches HS256 minimum requirement |
| Hex output | 64 characters | Most common format for env vars |
| Entropy source | OS CSPRNG via Web Crypto | Same quality as `crypto.randomBytes()` |
HS256 vs RS256: Which Algorithm Needs a Secret?
If you are choosing between symmetric and asymmetric signing, the decision affects whether you need a shared secret at all.
| HS256 | RS256 | |
|---|---|---|
| Key type | Single shared secret | Private/public key pair |
| Signing | HMAC with secret | RSA with private key |
| Verification | Same secret | Public key only |
| Best for | Monoliths, small teams | Microservices, third-party verification |
Read the full HS256 vs RS256 comparison for architecture guidance. If you choose HS256, the browser generator produces exactly the key type you need.
Security Best Practices After Generation
Generating a strong secret is only the first step. Follow these rules before going to production:
- Never commit secrets to git — use
.envfiles locally and a secrets manager in production - Use separate secrets per environment — development, staging, and production must differ
- Rotate on schedule — quarterly rotation with the
kidheader claim prevents downtime - Verify tokens server-side — use the JWT Validator during development to confirm signatures
When NOT to Use a Shared Secret
HS256 is not always the right choice. Consider RS256 when:
- Multiple independent services verify tokens without sharing secrets
- Third parties need to verify tokens but must not sign them
- You want to distribute only a public key to edge services
For most single-service applications and early-stage projects, HS256 with a 256-bit browser-generated secret is the pragmatic default.
Frequently Asked Questions
Can I use a UUID as my JWT secret?
A UUID v4 has about 122 bits of randomness — below the 256-bit minimum for HS256. Use the JWT Secret Generator or crypto.randomBytes(32) instead.
Should I base64-encode my secret?
Hex encoding is the standard for HS256 secrets in environment variables. If your library expects Base64, generate 32 random bytes and encode those — do not Base64-encode a short passphrase.
How do I know my secret is working?
Sign a test token and verify it with the JWT Validator. If verification succeeds with an explicit algorithms: ['HS256'] allowlist, your secret and algorithm configuration are correct.
Is the online generator safe for production secrets?
Yes, when it runs entirely in your browser with the Web Crypto API. Copy the output into a secrets manager or hosting environment variables and never paste production secrets into tickets or chat.
What else should I do after generating a secret?
Store it outside git, use separate values per environment, pin the algorithm at verify time, and follow the JWT best practices checklist. Schedule rotation with how to rotate JWT secrets.