Building a Node.js application that runs on your local machine is one thing; deploying it to production where it faces real-world traffic, security threats, and performance demands is another. To help you cross the finish line with confidence, we've compiled a production checklist of the 10 most crucial security and performance best practices for Node.js developers.
1. Set NODE_ENV to "production"
This is the simplest and most impactful performance optimization. By setting the environment variable NODE_ENV=production, popular packages like Express, React, and databases run in highly optimized modes, caching views, omitting verbose debug logs, and generating significantly faster builds.
💡 Impact: Setting NODE_ENV=production can improve Express server throughput by up to 3x!
2. Use a Process Manager (like PM2)
Node.js runs on a single thread. If an unhandled exception is thrown, the process will crash and shut down, taking your website offline. A process manager like PM2 acts as a safety net by automatically restarting your application if it crashes, monitoring memory usage, and managing log rotation.
# Install PM2 globally
npm install pm2 -g
# Start your app in cluster mode to use all CPU cores
pm2 start app.js -i max
3. Secure Headers with Helmet
Web applications are vulnerable to standard web attacks like Cross-Site Scripting (XSS), Clickjacking, and MIME sniffing. If you're using Express, the Helmet middleware helps secure your app by setting various HTTP headers appropriately:
const express = require('express');
const helmet = require('helmet');
const app = express();
// Use Helmet middleware
app.use(helmet());
4. Implement Rate Limiting
To protect your APIs from Denial of Service (DoS) attacks, brute-force login attempts, or scraper bots, you should limit how many requests a user can make in a given timeframe. Use the express-rate-limit package to implement simple rate limiting:
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per windowMs
message: 'Too many requests from this IP, please try again later.'
});
// Apply to all requests
app.use(limiter);
5. Always Use Environment Variables
Never hardcode API keys, database credentials, or secret keys in your source code. If you commit these keys to GitHub, bots will find and exploit them in minutes. Store sensitive credentials in a .env file and read them via process.env using the dotenv package.
6. Enable Gzip Compression
Gzip compression shrinks your response payloads before sending them to the browser, significantly reducing page load times and bandwidth costs. In Express, you can use the compression middleware:
const compression = require('compression');
app.use(compression());
7. Run as a Non-Root User in Docker
If you pack your Node.js application in a Docker container, it runs as the root user by default. This is a severe security risk; if an attacker compromises your application, they gain root access to the entire container. Always add a non-root user in your Dockerfile:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --only=production
COPY . .
# Run container as Node user (built-in)
USER node
CMD ["node", "app.js"]
8. Use a Structured Logger (like Pino or Winston)
Using console.log() in production is bad practice. It is synchronous (blocking), lacks timestamps, and doesn't provide structured log formats like JSON that can be fed into monitoring platforms. Use a logger like Pino or Winston for lightweight, asynchronous, JSON-formatted logging.
9. Sanitize User Input
Protect against SQL Injection, NoSQL Injection, and XSS by sanitizing all incoming user input (queries, body payloads, headers). Never trust the client. Use validation libraries like Joi or express-validator to strictly enforce schemas on incoming data.
10. Production Checklist Summary Table
Here is a quick reference table of our checklist:
| Practice | Type | Recommendation |
|---|---|---|
| NODE_ENV | Performance | Set to "production" in environments |
| PM2 | Reliability | Autostart and cluster management |
| Helmet | Security | Protect HTTP headers |
| Rate Limiter | Security | Prevent brute force and DDoS |
| Dotenv | Security | Keep secrets out of version control |
| Compression | Performance | Enable gzip middleware |
Conclusion
By checking off these 10 items, you protect your application from common vulnerabilities and prepare it to scale efficiently. Production readiness is an ongoing process, but these foundational practices keep your users and data secure. Deploy safely! 🚀