6 Common Mistakes to Avoid When Deploying Express.js
Arunangshu Das

Arunangshu Das @arunangshu_das

About: Software Developer

Location:
India
Joined:
Mar 20, 2025

6 Common Mistakes to Avoid When Deploying Express.js

Publish Date: Jun 5
0 0

Express.js is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. From startups to large-scale enterprises, it's a go-to framework for building APIs and web services. However, when it comes to deploying Express.js applications into production, developers often fall into a few traps—some minor, some catastrophic.

1. Not Setting NODE_ENV=production

What Happens

One of the most overlooked deployment best practices is setting the environment variable NODE_ENV to "production". Express uses this variable to determine the environment your app is running in.

By default, if NODE_ENV is not set, Express assumes a development environment. This means your app could:

  • Log excessive information (which can expose sensitive data)
  • Skip caching templates or other optimizations
  • Not leverage compression or performance improvements

Why It Matters

Running in production mode enables several internal optimizations, such as caching view templates and reducing error message verbosity, which directly affect performance and security.

How to Fix It

In your deployment script or service configuration, always set:

NODE_ENV=production
Enter fullscreen mode Exit fullscreen mode

For example, in a typical Linux environment:

NODE_ENV=production node app.js
Enter fullscreen mode Exit fullscreen mode

In Docker, define it in your Dockerfile:

ENV NODE_ENV=production
Enter fullscreen mode Exit fullscreen mode

Or if you're using PM2:

pm2 start app.js --env production
Enter fullscreen mode Exit fullscreen mode

Pro Tip

You can verify it in your code:

console.log('Environment:', process.env.NODE_ENV);
Enter fullscreen mode Exit fullscreen mode

2. Forgetting to Secure HTTP Headers

What Happens

Out of the box, Express doesn't secure your app against common web vulnerabilities like XSS, content sniffing, clickjacking, etc. That’s because security is a developer's responsibility, and Express stays minimal by default.

Why It Matters

Without proper headers:

  • Browsers may allow unsafe rendering behavior
  • Attackers can more easily exploit known web vulnerabilities
  • Your server might reveal information like X-Powered-By: Express, giving away your tech stack

How to Fix It

Use the helmet middleware. It’s a collection of security headers you can plug in with a single line of code:

npm install helmet
Enter fullscreen mode Exit fullscreen mode
const helmet = require('helmet');
app.use(helmet());
Enter fullscreen mode Exit fullscreen mode

This helps set important headers like:

  • Content-Security-Policy
  • X-Content-Type-Options
  • Strict-Transport-Security
  • X-Frame-Options

Pro Tip

Turn off Express' X-Powered-By header to hide your server type:

app.disable('x-powered-by');
Enter fullscreen mode Exit fullscreen mode

3. Not Using a Process Manager

What Happens

If you start your Express app with:

node app.js
Enter fullscreen mode Exit fullscreen mode

...then your app will crash the moment:

  • It runs out of memory
  • An unhandled exception occurs
  • Your server reboots

Why It Matters

In production, you must ensure your app restarts automatically on crash, supports zero-downtime deployments, and can scale across CPU cores.

How to Fix It

Use a process manager like PM2, which:

  • Restarts your app on crash
  • Manages logs
  • Supports cluster mode (multi-core)
  • Allows graceful reloads

Install PM2:

npm install -g pm2
Enter fullscreen mode Exit fullscreen mode

Start your app:

pm2 start app.js --name my-express-app
Enter fullscreen mode Exit fullscreen mode

Save your process list:

pm2 save
Enter fullscreen mode Exit fullscreen mode

Startup script on system reboot:

pm2 startup
Enter fullscreen mode Exit fullscreen mode

Pro Tip

Enable cluster mode to use multiple cores:

pm2 start app.js -i max
Enter fullscreen mode Exit fullscreen mode

4. Serving Static Files Inefficiently

What Happens

If you're serving static assets like images, stylesheets, or frontend JavaScript directly through Express with:

app.use(express.static('public'));
Enter fullscreen mode Exit fullscreen mode

…it might work for small apps, but it’s not optimized for production. Express isn’t a full-fledged static asset server like Nginx or a CDN.

Why It Matters

Static files can:

  • Consume server memory and bandwidth
  • Slow down your backend responsiveness
  • Prevent HTTP caching and compression

How to Fix It

Use Nginx or a CDN (Cloudflare, AWS CloudFront, etc.)

Let a reverse proxy like Nginx handle your static files:

location /static/ {
    root /var/www/your-app;
    expires 30d;
    add_header Cache-Control "public";
}
Enter fullscreen mode Exit fullscreen mode

Or set strong cache headers in Express:

app.use(express.static('public', {
  maxAge: '30d',
  etag: false
}));
Enter fullscreen mode Exit fullscreen mode

Also, compress files using compression middleware:

npm install compression
Enter fullscreen mode Exit fullscreen mode
const compression = require('compression');
app.use(compression());
Enter fullscreen mode Exit fullscreen mode

Pro Tip

Always move large files (videos, high-res images) to a cloud storage (S3, GCP Buckets) or CDN.

5. Ignoring Proper Logging and Monitoring

What Happens

Developers often deploy Express apps with console.log() as their main logging tool. While it’s fine during development, it’s insufficient for production.

You’ll face issues like:

  • No error tracking
  • No performance insights
  • No audit trail of issues leading to crashes

Why It Matters

Without monitoring, you can’t diagnose issues, detect anomalies, or even know when your app goes down.

How to Fix It

Use a logging library:

Example with Winston:

npm install winston
Enter fullscreen mode Exit fullscreen mode
const winston = require('winston');
 
const logger = winston.createLogger({
  level: 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json()
  ),
  transports: [new winston.transports.Console()]
});
 
app.use((req, res, next) => {
  logger.info(`${req.method} ${req.url}`);
  next();
});
Enter fullscreen mode Exit fullscreen mode

Integrate Monitoring Tools

For example, with Sentry:

npm install @sentry/node
Enter fullscreen mode Exit fullscreen mode
const Sentry = require("@sentry/node");
Sentry.init({ dsn: "YOUR_SENTRY_DSN" });
app.use(Sentry.Handlers.requestHandler());
Enter fullscreen mode Exit fullscreen mode

Pro Tip

Don’t forget health checks:

app.get('/health', (req, res) => res.status(200).send('OK'));
Enter fullscreen mode Exit fullscreen mode

Use tools like UptimeRobot or BetterUptime to monitor them.

6. Hardcoding Secrets and Config in Codebase

What Happens

One of the most dangerous mistakes is hardcoding sensitive values like:

const dbPassword = "mySuperSecretPassword123";
const jwtSecret = "thisIsTopSecret";
Enter fullscreen mode Exit fullscreen mode

This becomes especially risky when code is pushed to GitHub or shared across environments.

Why It Matters

Leaked credentials can:

  • Give attackers access to your database
  • Expose your API keys or tokens
  • Lead to financial and reputational loss

How to Fix It

Use Environment Variables

Use a .env file in development (never in production):

DB_PASSWORD=mySuperSecretPassword123
JWT_SECRET=thisIsTopSecret
Enter fullscreen mode Exit fullscreen mode

Read them in your code:

npm install dotenv
Enter fullscreen mode Exit fullscreen mode
require('dotenv').config();
 
const dbPassword = process.env.DB_PASSWORD;
const jwtSecret = process.env.JWT_SECRET;
Enter fullscreen mode Exit fullscreen mode

On production servers (or in Docker, CI/CD), set environment variables outside the code.

Use Secret Managers in Production

  • AWS Secrets Manager
  • HashiCorp Vault
  • Google Secret Manager
  • Docker Secrets

Example in Docker:

ENV JWT_SECRET=topsecret
Enter fullscreen mode Exit fullscreen mode

Pro Tip

Always add .env to .gitignore:

.env
Enter fullscreen mode Exit fullscreen mode

Final Thoughts

Deploying an Express.js application isn't just about running node app.js on a server. To truly go production-grade, you need to think like a DevOps engineer, security specialist, and performance optimizer—all at once.

Recap of What You Should Do:

Mistake                          Solution                                     
Not setting NODE_ENV           Use NODE_ENV=production                    
No security headers              Use helmet, disable x-powered-by         
No process manager               Use PM2 or similar                         
Inefficient static file handling Offload to Nginx/CDN, use compression      
Poor logging                     Use winston, sentry, and monitoring tools
Hardcoded secrets                Use .env files and secret managers         

You may also like:

  1. Top 10 Large Companies Using Node.js for Backend

  2. Why 85% of Developers Use Express.js Wrongly

  3. Top 10 Node.js Middleware for Efficient Coding

  4. 5 Key Differences: Worker Threads vs Child Processes in Node.js

  5. 5 Effective Caching Strategies for Node.js Applications

  6. 5 Mongoose Performance Mistakes That Slow Your App

  7. Building Your Own Mini Load Balancer in Node.js

  8. 7 Tips for Serverless Node.js API Deployment

  9. How to Host a Mongoose-Powered App on Fly.io

  10. The Real Reason Node.js Is So Fast

  11. 10 Must-Know Node.js Patterns for Application Growth

  12. How to Deploy a Dockerized Node.js App on Google Cloud Run

  13. Can Node.js Handle Millions of Users?

  14. How to Deploy a Node.js App on Vercel

  15. 6 Common Misconceptions About Node.js Event Loop

  16. 7 Common Garbage Collection Issues in Node.js

  17. How Do I Fix Performance Bottlenecks in Node.js?

  18. What Are the Advantages of Serverless Node.js Solutions?

  19. High-Traffic Node.js: Strategies for Success

Read more blogs from Here

Share your experiences in the comments, and let's discuss how to tackle them!

Follow me on LinkedIn

Comments 0 total

    Add comment