How to Generate a JWT Secret Key (Step-by-Step)
Every JWT signed with HMAC algorithms (HS256, HS384, HS512) depends on a secret key. If that secret is weak, predictable, or leaked, your entire authentication system is compromised. This guide focuses on programmatic generation — the approach you want in CI/CD, Docker entrypoints, and secrets-manager bootstrap scripts.
> Prefer not to write the code? Generate a secure secret instantly with our free browser tool → — all randomness stays on your device.
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. For depth on entropy, see JWT secret entropy.
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 what size JWT secret you need.
Step 2: Generate Programmatically
Always use a CSPRNG (crypto.randomBytes, secrets, crypto/rand, SecureRandom). Never Math.random(), random.random(), or a human-typed passphrase.
Node.js
const crypto = require('crypto');
// 32 bytes = 256 bits → 64 hex characters
const secret = crypto.randomBytes(32).toString('hex');
console.log(secret);
// Or base64 if your stack expects it
const secretB64 = crypto.randomBytes(32).toString('base64');Python
import secrets
secret = secrets.token_hex(32) # 256-bit hex
print(secret)
# Or URL-safe base64
secret_b64 = secrets.token_urlsafe(32)Go
package main
import (
"crypto/rand"
"encoding/hex"
"fmt"
)
func main() {
b := make([]byte, 32) // 256-bit
if _, err := rand.Read(b); err != nil {
panic(err)
}
fmt.Println(hex.EncodeToString(b))
}Java
import java.security.SecureRandom;
import java.util.HexFormat;
public class JwtSecret {
public static void main(String[] args) {
byte[] bytes = new byte[32]; // 256-bit
new SecureRandom().nextBytes(bytes);
System.out.println(HexFormat.of().formatHex(bytes));
}
}OpenSSL (CLI / scripts)
openssl rand -hex 32Wire any of these into your deploy pipeline: generate once, store in your secrets backend, inject as JWT_SECRET at runtime.
Step 3: Store It Securely
Never hardcode the secret in source code:
JWT_SECRET=your-generated-hex-key-hereFor production, use a secrets manager. Read how to store JWT secrets securely.
Step 4: Sign and Verify a Test Token
const jwt = require('jsonwebtoken');
const token = jwt.sign({ userId: 1 }, process.env.JWT_SECRET, {
algorithm: 'HS256',
expiresIn: '1h',
});Paste the token and secret into the JWT Validator to confirm the signature is valid. You can also build a token visually with the JWT Encoder.
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.
- Same secret everywhere: Every service that verifies HS256 tokens must possess the signing secret
- No encryption: The payload is only base64-encoded — never store passwords or PII in claims
- Algorithm matters: Always specify
algorithms: ['HS256']to prevent algorithm confusion attacks
For microservices where many teams verify tokens, see HS256 vs RS256. For monoliths and small APIs, HS256 with a strong secret is the right default.
Separate Secrets Per Environment
# .env.development
JWT_SECRET=dev-only-secret-never-use-in-prod
# Production (hosting platform or secrets manager)
JWT_SECRET=production-256-bit-random-secretGenerate a fresh secret for each environment — with code in CI, or via the JWT Secret Generator.
When to Rotate Your Secret
Rotate immediately if you suspect the secret was committed to git, logged, or breached. For routine hygiene, rotate on a schedule using the kid header — see how to rotate JWT secrets.
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 256 bits for HS256
- Using
Math.random()/ non-crypto RNGs
Frequently Asked Questions
Can I use a UUID as my JWT secret? A UUID v4 has 122 bits of randomness — below the 256-bit minimum for HS256. Generate 32 random bytes instead.
Should I base64-encode my secret? Hex is common for HS256. If your library expects base64, encode 32 random bytes — 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.
Need a secret right now without writing code? Use the JWT Secret Generator.