How to Deploy an Express.js App to Production
Getting an Express API from localhost to a real server: production settings, listening on PORT, trust proxy, security middleware, a process manager, a reverse proxy with HTTPS, Docker as an option, and the checks that prevent the usual 502s and crashes.
Express is the most common way to build a Node.js backend. It runs the same in production as in development — node server.js — but a few settings and supporting pieces make the difference between "works" and "stays up". (What is Express?, What is Node.js?)
Step 1: make the app production-ready
import express from 'express'
import helmet from 'helmet'
const app = express()
app.set('trust proxy', 1) // behind Nginx/Caddy/Cloudflare
app.use(helmet()) // sensible security headers
app.use(express.json({ limit: '1mb' }))
app.get('/health', (req, res) => res.json({ ok: true }))
// ...your routes
app.use((err, req, res, next) => {
console.error(err)
res.status(500).json({ error: 'Something went wrong' }) // don't leak stack traces
})
const port = Number(process.env.PORT) || 3000
app.listen(port, () => console.log(`Listening on ${port}`))
What each bit is for:
process.env.PORT— hosts tell your app which port to use.trust proxy— soreq.ipandreq.securereflect the real client behind a reverse proxy (needed for rate limiting and secure cookies).helmet— security headers. (HTTP security headers)- A
/healthroute — for uptime checks and zero-downtime deploys. (Health check endpoints) - An error handler — users see a generic message; details go to your logs.
Step 2: environment variables
Set NODE_ENV=production (Express and many libraries behave more efficiently with it) and keep secrets — database URL, API keys — in environment variables, not code. (Environment variables explained)
Step 3: install and build on the server
git clone https://github.com/you/api.git /srv/api
cd /srv/api
npm ci --omit=dev
npm run build # if you use TypeScript
npm ci installs exactly what's in your lock file. (package-lock.json explained)
Step 4: keep it running
If you just run node server.js over SSH, it stops when you log out or it crashes. Use a process manager:
PM2:
npm install -g pm2
pm2 start dist/server.js --name api
pm2 save
pm2 startup # follow the printed command so it starts on boot
or systemd (built in) with Restart=always. (PM2 vs systemd, systemd service file)
Step 5: HTTPS with a reverse proxy
Don't expose Node directly on port 80/443. Put Caddy or Nginx in front:
api.example.com {
reverse_proxy 127.0.0.1:3000
}
(What is Caddy?, Nginx reverse proxy config)
Then close every port except 22, 80 and 443 in the firewall. (UFW firewall basics)
Option: Docker
FROM node:24-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
ENV NODE_ENV=production
USER node
CMD ["node", "server.js"]
Listen on 0.0.0.0 inside the container. (Production Dockerfile for Node.js)
Before you call it done
- CORS configured for your real front-end domain, not
*with credentials. (CORS errors explained) - Rate limiting on login and expensive routes. (Implementing rate limiting)
- Input validation on every route. (Validating input with Zod)
- Graceful shutdown so deploys don't cut off requests. (Graceful shutdown in Node.js)
- Logs you can search and error monitoring. (Structured logging)
- Database backups. (Postgres backup and restore)
Common problems
- 502 Bad Gateway → app not running or wrong port. (502 Bad Gateway)
- Works locally, crashes on the server → missing env vars, different Node version, case-sensitive imports. (Works locally but not in production)
- Memory keeps growing → a leak. (Node.js memory leaks)
EasySpawn runs your Express API behind HTTPS, keeps it running and restarts it on crashes, with Postgres and daily backups alongside. See how it works or join the waitlist.
Related: Deploy a Node.js App to a VPS · What Is Express? · PM2 vs systemd · Designing a REST API
Keep reading
What Is Vercel? What It Does, What It Costs You, and When to Use Something Else
Vercel is a hosting platform built around frontend frameworks, especially Next.js: push to GitHub and your site is live. What it actually does, what serverless functions mean for your app, where the limits are, and when a regular server is a better fit.
What Is Nginx? The Web Server in Front of Half the Internet
Nginx ("engine-x") is a web server that serves files, forwards requests to your app, handles HTTPS and balances load. What it does, what a basic config looks like, where the files live, the commands you'll need, and whether you need it at all.