Security Best Practices
A checklist of habits that keep an Express app safe in production.
Security is layered. Combine these habits:
- Use
helmet()for secure headers. - Validate and sanitize all input.
- Keep secrets in environment variables, never in code or git.
- Hash passwords with bcrypt; never store them in plain text.
- Serve everything over HTTPS.
- Apply rate limiting to auth and public endpoints.
- Keep dependencies updated; run
npm audit.
Example
const bcrypt = require('bcrypt');
app.post('/register', async (req, res) => {
const hash = await bcrypt.hash(req.body.password, 12);
// store 'hash', never the raw password
res.status(201).json({ ok: true });
});When to use it
- A security audit checklist for an Express API includes verifying that Helmet is active, JWT secrets are not hard-coded, and rate limiting is on all public routes.
- A developer avoids sending stack traces to clients in production by checking process.env.NODE_ENV inside the error middleware.
- A team sets the X-Powered-By header to disabled using app.disable('x-powered-by') to prevent fingerprinting the tech stack.
More examples
Disable X-Powered-By header
Disables the X-Powered-By: Express header and adds Helmet, removing two common fingerprinting vectors.
const express = require('express');
const app = express();
app.disable('x-powered-by'); // don't advertise Express
app.use(require('helmet')());
app.get('/', (req, res) => res.send('OK'));
app.listen(3000);Hide error details in production
Includes the stack trace in error responses only in development mode, hiding implementation details from production clients.
app.use((err, req, res, next) => {
const isDev = process.env.NODE_ENV !== 'production';
res.status(err.status || 500).json({
error: err.message,
...(isDev && { stack: err.stack }),
});
});Prevent parameter pollution
Layers hpp (HTTP parameter pollution prevention), Helmet, rate limiting, and a body-size cap to harden the app comprehensively.
const hpp = require('hpp');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const express = require('express');
const app = express();
app.use(helmet());
app.use(hpp()); // removes duplicate query params
app.use(rateLimit({ windowMs: 15*60*1000, max: 200 }));
app.use(express.json({ limit: '10kb' })); // cap body size
app.listen(3000);
Discussion