Secure Your Express API — Helmet, Rate Limiting & Validation


🔐 Why API Security is Non-Negotiable in 2025

The internet is a minefield—especially if you’re shipping production APIs. With rising threats like XSS, brute force attacks, and data poisoning, security must be part of your Express.js foundation, not an afterthought.

In this post, I’ll show how I secure every Express API I build using 3 key tools:

  • Helmet for headers

  • Express-rate-limit for throttling

  • Joi / Zod for validation


🛡️ 1. Helmet: Secure HTTP Headers Made Simple

Helmet sets HTTP headers that protect your app from common attacks (like XSS, clickjacking, etc.)

Installation:

npm install helmet

Usage:

const helmet = require("helmet");
app.use(helmet());

Why it matters:

  • Sets X-Frame-Options to avoid clickjacking

  • Configures Content-Security-Policy

  • Blocks sniffing via X-Content-Type-Options

You can customize the headers:

app.use(helmet({ contentSecurityPolicy: false }));

⚠️ 2. Rate Limiting: Stop Brute Force Attacks

To avoid abuse, throttle the number of requests an IP can make in a time window.

Installation:

npm install express-rate-limit

Usage:

Pro Tip: Use different limits for /auth routes (e.g. 5 requests/min).

const rateLimit = require("express-rate-limit");

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // limit each IP to 100 requests
});

app.use("/api/", limiter);

✅ 3. Validate Everything (with Joi or Zod)

Never trust user input. Validate both body and query params.

Example using Joi:

npm install joi
const Joi = require("joi");

const userSchema = Joi.object({
  name: Joi.string().min(3).required(),
  email: Joi.string().email().required(),
});

app.post("/register", (req, res) => {
  const { error } = userSchema.validate(req.body);
  if (error) return res.status(400).send(error.details[0].message);

  // Proceed safely
});

Zod Alternative (for TS users):

import { z } from "zod";

const loginSchema = z.object({
  email: z.string().email(),
  password: z.string().min(6),
});

✅ Works great with TypeScript + cleaner syntax!


🧪 Real-World Example: "Expense Tracker API"

I launched an expense tracker API that was open to the public. Within 2 weeks:

  • Helmet blocked several XSS attempts

  • Rate limiter stopped an IP making 10,000+ auth requests

  • Validation prevented MongoDB injection in filters

With just 20 extra lines of code, I avoided chaos.


🧠 Final Thoughts

Security isn’t flashy—it’s foundational. These three tools (Helmet, rate limiting, validation) are my baseline for any public Express API. They protect your server, your users, and your peace of mind.

💬 What other Express security practices do you use? Let’s learn from each other in the comments.

8
Subscribe to my newsletter

Read articles from Workspace Ronnie directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Workspace Ronnie
Workspace Ronnie