Course:Node.js & Express/
Lesson

Shipping an Express app to production without security hardening is like leaving your front door open with a sign that says "help yourself." The good news is that the Node.js ecosystem has excellent, battle-tested tools that handle most of the heavy lifting for you. This lesson walks you through the four pillars of Express production security.

Security headers with HelmetWhat is helmet?An Express middleware package that sets recommended HTTP security headers (CSP, HSTS, X-Frame-Options) in one line.

HTTPWhat is http?The protocol browsers and servers use to exchange web pages, API data, and other resources, defining how requests and responses are formatted. headers control how browsers behave when they load your app. Left at their defaults, browsers make permissive choices that attackers can exploit, things like loading scripts from any domain or allowing your pages to be embedded in iframes on malicious sites. Helmet sets safe defaults for all of these in one line.

import helmet from 'helmet';

app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      scriptSrc: ["'self'"],
      imgSrc: ["'self'", 'data:', 'https:']
    }
  },
  hsts: {
    maxAge: 31536000,        // 1 year in seconds
    includeSubDomains: true,
    preload: true
  }
}));
The Content Security Policy (CSP) header is the most powerful one Helmet sets. It tells the browser exactly which sources are allowed to load scripts, styles, and images. If an attacker somehow injects a <script> tag pointing to their own server, CSP stops the browser from running it.

Key headers Helmet sets

HeaderWhat it does
X-Frame-OptionsPrevents your site from being embedded in iframes (clickjacking)
X-Content-Type-OptionsStops browsers from guessing file types (MIME sniffing)
Strict-Transport-SecurityForces HTTPS for the configured duration
Content-Security-PolicyAllowlists trusted sources for scripts, styles, images
Referrer-PolicyControls how much URL info leaks to other sites
02

Secure CORSWhat is cors?Cross-Origin Resource Sharing - a browser security rule that blocks web pages from making requests to a different domain unless that domain explicitly allows it. configuration

CORS (Cross-Origin Resource Sharing) controls which other websites can make requests to your APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses. from a user's browser. Calling app.use(cors()) with no arguments opens your API to every originWhat is origin?The combination of protocol, domain, and port that defines a security boundary in the browser, like https://example.com:443. on the internet, any website could read your users' data through their browsers.

import cors from 'cors';

const whitelist = ['https://my-app.com', 'https://admin.my-app.com'];

const corsOptions = {
  origin: (origin, callback) => {
    if (whitelist.includes(origin) || !origin) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
  allowedHeaders: ['Content-Type', 'Authorization']
};

app.use(cors(corsOptions));

The !origin check allows server-to-server requests (like from Postman or curl) which don't send an Origin header. In a more strict environment you might want to remove that.

03

Rate limitingWhat is rate limiting?Restricting how many requests a client can make within a time window. Prevents brute-force attacks and protects your API from being overwhelmed.

Think of rate limiting like a bouncer at the door. Each IP addressWhat is ip address?A numerical label (e.g., 172.217.14.206) that identifies a device on a network - DNS translates domain names into IP addresses. gets a limited number of attempts within a time window, and once they hit the limit, they wait outside. This stops bots from hammering your login endpointWhat is endpoint?A specific URL path on a server that handles a particular type of request, like GET /api/users. thousands of times a second trying to guess passwords.

import rateLimit from 'express-rate-limit';

// General limit for all routes
const generalLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,  // 15-minute window
  max: 100,
  message: 'Too many requests from this IP, please try again later'
});
app.use(generalLimiter);

// Stricter limit for authentication endpoints
const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 5,
  skipSuccessfulRequests: true  // Only count failed attempts
});
app.use('/auth/', authLimiter);
`skipSuccessfulRequests
true` on auth routes is a nice touch, legitimate users who successfully log in don't burn through their allowance, only failed attempts count.
04

Input validation and XSSWhat is xss?Cross-Site Scripting - an attack where malicious JavaScript is injected into a web page and runs in other users' browsers, stealing data or hijacking sessions. protection

Your database should never see raw, unvalidated user input. Two libraries cover the most common attack vectors: NoSQLWhat is nosql?A category of databases that store data without fixed table schemas, using documents, key-value pairs, or graphs instead of rows and columns. injection and cross-site scripting (XSS).

import mongoSanitize from 'express-mongo-sanitize';
import xss from 'xss-clean';

// Strip MongoDB operator characters from user input
app.use(mongoSanitize());

// Clean HTML tags from user input to prevent XSS
app.use(xss());

For more structured validation, use zod or joi to define exactly what shape your data should be before your route handlers ever touch it. Reject anything that doesn't fit the schemaWhat is schema?A formal definition of the structure your data must follow - which fields exist, what types they have, and which are required. with a 400 error.

05

Quick reference

RiskDefensePackage
Clickjacking, MIME sniffingSecurity headershelmet
Cross-origin data theftCORS whitelistcors
Brute force, DoSRate limitingexpress-rate-limit
NoSQL injectionInput sanitizationexpress-mongo-sanitize
XSS attacksHTML cleaningxss-clean
Schema violationsInput validationzod / joi