Blog
3 min read

How to Generate a Secret Key (for JWTs, Sessions and APIs)

Apps need long random secrets for signing sessions and JWTs, encrypting data, and verifying webhooks. How to generate a strong one with openssl rand, Node.js, Python or a password manager, how long it should be, where to store it, and the mistakes that make secrets guessable.

Your app's setup guide says "set SESSION_SECRET to a long random string" — or JWT_SECRET, SECRET_KEY, NEXTAUTH_SECRET, ENCRYPTION_KEY. Here's how to make one properly.

The short answer:

openssl rand -base64 32

That prints 32 random bytes as text, like q3H7x9vN2kLp...=. Copy it into your environment variables. Done.

Why it has to be random

These secrets are used to sign or encrypt things. If someone can guess your session secret, they can forge a login cookie and become any user. If they can guess your JWT secret, they can mint tokens with "role": "admin". (What Is a JWT?)

So it must come from a cryptographically secure random generator — not something you typed, not Math.random(), not a word plus a number.

How long?

32 random bytes (256 bits) is a good default for many session secrets and HMAC signing keys. That's 44 characters in base64, or 64 in hex. Follow your library's requirements: encryption algorithms can require an exact key length and encoding, so a longer string isn't automatically a valid key.

Ways to generate one

openssl (Mac, Linux, Git Bash on Windows):

openssl rand -base64 32     # base64 text
openssl rand -hex 32        # hex text (only 0-9 a-f; safe everywhere)

Node.js:

node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"

Python:

python3 -c "import secrets; print(secrets.token_urlsafe(32))"

PowerShell (Windows):

$bytes = New-Object byte[] 32
[Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
[Convert]::ToBase64String($bytes)

Don't use Get-Random for this — it isn't a cryptographically secure generator.

A password manager — 1Password, Bitwarden and others can generate long random strings. Set the length to 40+ characters.

Framework helpers: some tools have their own, such as npx auth secret for Auth.js. Use them when the docs recommend it.

Hex, base64 or URL-safe?

  • Hex uses only 0–9 and a–f, so it never causes quoting problems in .env files or URLs.
  • Base64 is shorter but contains +, / and =, which occasionally need quoting.
  • URL-safe base64 (Python's token_urlsafe) swaps those for - and _.

All are equally secure for the same number of bytes. (What Is Base64?)

Where to put it

Mistakes to avoid

  • Copying the example value from a tutorial (supersecret, changeme) into production.
  • Letting an AI tool "make up" a secret in the code — it isn't random and it's now in your git history.
  • Reusing one secret for everything.
  • Committing .env. (.gitignore Explained)

Rotating a secret

If a secret leaks, generate a new one and replace it. Note what changing it breaks: a new session secret logs everyone out; a new encryption key means old data can't be decrypted unless you re-encrypt it first. (Leaked API Key: What to Do)

The summary

  • openssl rand -base64 32 (or -hex 32) gives you a strong secret.
  • 32 random bytes from a secure generator — never something you typed.
  • Store it in environment variables, never in code or git.

EasySpawn keeps your app's secrets in server-side environment variables, out of your repository and out of the browser, with Claude Code to wire them in correctly. See how it works or join the waitlist.

Related: Secrets Management Beyond .env Files · Password Hashing Explained · Session vs JWT · Secret Scanning

Keep reading