Express.js Middleware: Security and Architecture Best Practices
Express powers countless Node.js APIs. Learn how to structure middleware chains, harden routes with helmet and rate limiting, validate input, and avoid common security vulnerabilities in production Express applications.
Middleware Is the Spine of Express
Every Express application is a stack of functions (req, res, next) executed in order. Authentication, logging, body parsing, and error handling all live in this pipeline. Poor ordering, such as parsing bodies before rate limiting or mounting routes before security headers, creates exploitable gaps.
Recommended Middleware Order
- Trust proxy and request ID (for accurate IPs behind load balancers)
- Security headers (
helmet) - CORS (strict origin allowlists in production)
- Rate limiting and bot protection
- Body parsers with size limits
- Authentication and authorization
- Route handlers
- Centralized error middleware (four-argument handler)
import express from 'express';
import helmet from 'helmet';
import rateLimit from 'express-rate-limit';
const app = express();
app.set('trust proxy', 1);
app.use(helmet());
app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));
app.use(express.json({ limit: '100kb' }));
app.use('/api', apiRouter);
app.use((err, req, res, next) => {
logger.error(err);
res.status(err.status || 500).json({ error: 'Internal server error' });
});
Input Validation and Injection Defense
Never trust req.body, req.query, or req.params. Use schema validators (Zod, Joi) in dedicated middleware. Parameterize database queries. NoSQL injection via MongoDB operators in user input remains a common Express vulnerability.
Authentication Patterns
Prefer short-lived JWT access tokens with refresh rotation, or session cookies with httpOnly, secure, and sameSite flags. Attach user context in middleware and enforce authorization per route, not globally assuming "logged in" equals "allowed."
Observability
Structured logging (Pino) with correlation IDs, metrics on status codes and latency, and graceful shutdown handlers keep Express APIs operable at scale. Treat middleware as reusable, testable units, not inline closures copied across routes.
Express is minimal by design; production security comes from the middleware ecosystem you assemble and the discipline with which you order and test it.