Back to Blog

10 Essential Node.js Production & Security Best Practices

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
Advertisement

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! 🚀