Where to Store JWT Secret Keys Safely

Generating a strong key is only half the job. Where you put it matters just as much.

My first Node.js project had the JWT secret key sitting right there in the code. Just hardcoded. Like this:

const token = jwt.sign({ userId: user.id }, "mysecretkey123");

I pushed it to GitHub. Public repo. Everyone could see it.

Not my finest hour.

If you've ever done something similar, you're not alone. It's one of the most common beginner mistakes. But it's also one of the easiest to fix once you know what you're doing.

Why Hardcoding is Such a Bad Idea

When your secret key is inside your code, it goes everywhere your code goes. Into your Git history. Into your GitHub repo. Into every zip file you send to a teammate. Into every deployment log someone can read.

Even if you delete it later, Git remembers. Anyone who has ever cloned your repo can scroll back through the history and find it.

That's not paranoia. That's just how version control works.

The Right Place: Environment Variables

The fix is simple. Stop putting secrets in your code. Put them in environment variables instead.

An environment variable is just a value that lives outside your code, on the machine running your app. Your code reads it when it starts up. The secret never gets written into any file that gets committed to Git.

Here's how it works in practice.

Step 1: Create a .env File

In the root of your project, create a file called .env. Inside it, add your JWT secret:

JWT_SECRET=a3f8c2d1e7b9044f2c68a5d3e1f7b290c4a8e6d2f0b1c3a5e7d9f2b4c6a8e0d3

That's it. One line. Your secret lives here, not in your code.

I always generate the key first using jwtsecretkeygenerator.com, then paste it straight in. Takes about 10 seconds total.

Step 2: Add .env to Your .gitignore

This is the step people forget. And it's the most important one.

Open your .gitignore file (or create one if you don't have it) and add this line:

.env

Now Git won't track that file. It won't get committed. It won't show up on GitHub. It stays on your machine only.

Do this before your first commit. Not after. Before.

Step 3: Read It in Your Code

Install the dotenv package:

npm install dotenv

Then at the top of your main file, load it:

require('dotenv').config();

Now you can use your secret key anywhere in your code like this:

const token = jwt.sign( { userId: user.id }, process.env.JWT_SECRET, { expiresIn: '1h' } );

Clean. No secrets in the code. No accidental leaks.

What About When You Deploy?

Good question. Your .env file won't go to your server since it's in .gitignore. So how does your production app get the secret?

Every hosting platform has a way to set environment variables directly on the server. You just go into your dashboard and add them there.

  • Heroku: Settings → Config Vars
  • Vercel: Project Settings → Environment Variables
  • Railway: Variables tab in your project
  • AWS / DigitalOcean: Set them in your server config or secrets manager
  • VPS (direct server): Add them to your systemd service file or shell profile

The secret goes directly into the platform, never through your code or Git. That's exactly how it should work.

Use Different Keys for Dev and Production

This one is easy to skip but worth doing.

Your local development key and your production key should be different. Always.

If someone somehow gets your dev key, they can't use it against your real users. And if you need to rotate your production key, you don't have to touch your development setup at all.

Just generate two separate keys. One goes in your local .env, the other goes into your production platform's environment variables.

What if I Already Committed the Key to GitHub?

First: don't panic. It happens more than you'd think.

But act fast. Here's what to do:

  • Generate a completely new key right now
  • Update your app to use the new key
  • Delete the old key from everywhere
  • Move the new key to environment variables properly
  • Consider the old key permanently compromised

You can also try to scrub it from your Git history using tools like git filter-repo, but honestly that's complicated. The safer move is just to treat the old key as dead and move on with a new one.

The key is compromised the moment it hits a public repo. Even if you push a fix 30 seconds later, automated bots have already scanned it.

Yes, really. Bots scan GitHub continuously looking for exposed secrets. It's not a hypothetical.

Other Places You Should Never Store Your Key

Since we're here, let's cover the full list of bad spots:

  • Inside your source code files (any of them)
  • In a comment in your code ("// old key: abc123")
  • In a config file that gets committed to Git
  • In a Slack or Teams message
  • In a plain text note or sticky note on your desk
  • In your browser's bookmark or note tool
  • In a shared Google Doc or Notion page
  • Emailed to yourself or a coworker

All of those are real places I've seen developers store secrets. All of them are bad ideas.

Going Further: Secret Managers

If you're working on a bigger app or a team project, environment variables on a hosting platform are still a bit manual. Every developer needs access, and there's no audit trail of who changed what.

That's where secret managers come in. Tools like:

  • AWS Secrets Manager — great if you're already on AWS
  • HashiCorp Vault — powerful, self-hosted option
  • Doppler — easy to use, works with most platforms
  • 1Password Secrets Automation — good for teams already using 1Password

These tools let you store secrets in one place, control who can access them, and automatically inject them into your app at runtime.

For a solo side project, this is probably overkill. For anything with a real team or real users, it's worth looking into.

The Short Version

You went through the effort of generating a strong, random JWT secret key. Don't throw that away by storing it somewhere careless.

The rules are simple:

  • Never put it in your code
  • Always use environment variables
  • Always add .env to .gitignore
  • Use different keys for dev and production
  • If it gets exposed, replace it immediately

Follow those five rules and you're ahead of a huge chunk of developers out there.

It's not complicated. It just takes a few extra minutes to set up. And those few minutes can save you from a really bad day down the road.