A heavy vault door with glowing digital locks, representing a hardened and  secure API
Back to Blog
Security
2026-02-21
5 min read

Hardening Node.js APIs: Rate Limiting, CORS, and Helmet

A

Abhay Vachhani

Developer

API Protection Checklist

  • JWT Revocation (Redis Blacklist)
  • Zod Validation on all inputs
  • Distributed Rate Limiting
  • HSTS and CSP headers active

An API exposed to the internet is a target from the second it goes live. Automated bots constant scan for misconfigured headers and unprotected endpoints. To move beyond "hobbyist" code, you must implement a multi-layered defense. In this guide, we explore the fundamental tools for hardening Node.js APIs against the most common threats.

1. Auth Security: JWT Revocation Lists

The biggest flaw of JWTs is that they are stateless. If a user's token is stolen, you can't "de-authorize" them without waiting for the token to expire. The Pro Fix: Implement a Revocation List (Blacklist) in Redis. Every time a user logs out or changes their password, store their token ID (JTI) in Redis with a TTL matching the token's expiration. Your auth middleware must check this list on every request.

2. Validation: Strict Schemas with Zod

Never trust `req.body`. Use a validation library like **Zod** to enforce strict types, lengths, and formats. This prevents many classes of attacks, including NoSQL injection and prototype pollution.

// Strict Schema Validation

const CreateUserSchema = z.object({
username: z.string().min(3).max(20).regex(/^[a-zA-Z0-9]+$/),
email: z.string().email(),
password: z.string().min(12),
}).strict(); // Rejects extra keys

3. Infrastructure Security: HSTS and CSP

Basic headers aren't enough. Production APIs should use:

  • HTTP Strict Transport Security (HSTS): Tells the browser to *only* communicate via HTTPS, preventing SSL stripping attacks.
  • Content Security Policy (CSP): Prevents XSS by restricting where scripts, styles, and images can be loaded from. For an API, set default-src 'none' to prevent it from ever being rendered as a webpage.

4. Distributed Rate Limiting: The Redis Pattern

Memory-based rate limiting (like express-rate-limit) only works for a single server. If you have 5 API servers, an attacker can get 5x the allowed requests. Use **Redis-backed limiters** (like rate-limiter-flexible) to maintain a global counter. This ensures that a user is limited across your entire infrastructure, not just one instance.

5. Pro Pattern: The Multi-Layered Security Middleware

Combine your security tools into a single, reusable middleware stack.

// Security Middleware Stack

const securityStack = [
helmet(), // 15+ Secure Headers
cors(corsOptions), // Strict Origin Control
redisRateLimiter, // Global Rate Limiting
validate(AuthSchema), // Payload Sanitization
];
app.use('/v1/auth', ...securityStack);

Conclusion

API security is not about one single tool; it is about "Defense in Depth." By combining JWT revocation, strict Zod schemas, and distributed rate limiting, you create an environment where the "cost" of an attack is too high for the average bot. Protecting your data is protecting your users. Professional engineering starts with security.

FAQs

Why is Rate Limiting important?

Rate limiting protects your server from Denial of Service (DoS) attacks and brute-force attempts by limiting the number of requests a single IP can make in a specific timeframe.

What does Helmet.js actually do?

Helmet is a collection of 15 smaller middleware functions that set standard HTTP response headers (like X-Frame-Options and Content-Security-Policy) to protect against common attacks like XSS and clickjacking.

How should I handle CORS in production?

Never use '*'. Always specify an explicit 'allowlist' of domains that are allowed to access your API. This prevents malicious websites from making cross-origin requests to your server on behalf of users.