Modern API Security: Rate Limiting, CORS, and Zero Trust
Exposing an API to the public internet is akin to opening a storefront on the busiest, most dangerous street in the world. Within minutes of deployment, automated botnets, vulnerability scanners, and malicious actors will begin probing your endpoints for weaknesses.
To build APIs that power enterprise applications, simply validating a JWT is no longer enough. You must implement defensive, zero-trust architectures at every layer of the network stack.
Here is how elite engineering teams secure highly scalable APIs in 2026.
The API Gateway: Your First Line of Defense
Never expose your application servers directly to the internet. All incoming traffic must first pass through an API Gateway or a Web Application Firewall (WAF) such as Cloudflare, AWS API Gateway, or Kong.
The API Gateway is responsible for terminating SSL/TLS connections, filtering out malformed requests, and blocking known malicious IP ranges before they ever reach your Node.js or Express runtime. This offloads immense CPU overhead from your application logic.
Advanced Rate Limiting Strategies
A simple rate limiter restricts an IP address to $N$ requests per minute. While necessary, this naive approach is easily bypassed by attackers rotating through distributed proxy networks.
Modern rate limiting must be multi-dimensional:
- IP-Based Throttling: The baseline. Limit requests per IP address to prevent brute-force attacks.
- User-Based Throttling: Limit requests per authenticated
user_id. This prevents a malicious user from routing their attacks through 500 different IP proxies. - Endpoint Specific Quotas: An endpoint like
GET /postscan handle 100 req/sec. An endpoint likePOST /auth/loginorPOST /reset-passwordshould be hard-capped at 3 req/minute to prevent brute-forcing.
Implementing Redis-backed Rate Limiting
In distributed systems, an in-memory rate limiter on a single Node instance will fail. You must use a centralized fast-storage system like Redis to track quotas globally.
1import rateLimit from 'express-rate-limit';
2import RedisStore from 'rate-limit-redis';
3import { redisClient } from './redis.js';
4
5export const loginRateLimiter = rateLimit({
6 store: new RedisStore({
7 sendCommand: (...args) => redisClient.sendCommand(args),
8 }),
9 windowMs: 15 * 60 * 1000, // 15 minutes
10 max: 5, // Limit each IP to 5 login requests per window
11 standardHeaders: true,
12 legacyHeaders: false,
13 message: { error: 'Too many login attempts. Please try again later.' }
14});CORS: The Misunderstood Shield
Cross-Origin Resource Sharing (CORS) is a browser security mechanism, not a backend firewall. It prevents a malicious website (e.g., evil.com) from making authenticated AJAX requests to your API (api.yourdomain.com) on behalf of a victim user.
Many developers, frustrated by CORS errors during development, set Access-Control-Allow-Origin: *. This is catastrophic for authenticated APIs.
Strict CORS configuration is mandatory:
1import cors from 'cors';
2
3const allowedOrigins = [
4 'https://gaganshukla.in',
5 'https://admin.gaganshukla.in'
6];
7
8app.use(cors({
9 origin: function (origin, callback) {
10 // Allow requests with no origin (like mobile apps or curl)
11 if (!origin) return callback(null, true);
12 if (allowedOrigins.indexOf(origin) === -1) {
13 return callback(new Error('Blocked by CORS Policy'), false);
14 }
15 return callback(null, true);
16 },
17 credentials: true, // Crucial for accepting HttpOnly cookies
18 methods: ['GET', 'POST', 'PUT', 'DELETE']
19}));Preventing Data Exposure through Pagination
One of the most devastating forms of API abuse is data scraping. If you expose an endpoint like GET /users, an attacker can write a script to download your entire database.
Pagination is not just for UI/UX; it is a critical security measure.
- Hard Limits: Always enforce a maximum
limit(e.g.,limit=100). If a client requestslimit=10000, truncate it to 100 on the backend. - Cursor Pagination: For massive datasets, offset/limit pagination degrades the database because it requires scanning all preceding rows. Cursor-based pagination (
?after=cursor_id) relies on indexes and operates in $O(1)$ time complexity.
Zero Trust and Input Sanitization
The core tenet of Zero Trust is: Never trust the client.
Every single byte of data entering your API must be validated, sanitized, and typed before it touches your database. If you expect a string, ensure it isn't an object containing a MongoDB $ne operator (NoSQL injection). If you expect an integer, ensure it isn't a massive buffer designed to crash your Node runtime.
Libraries like Zod or Joi are non-negotiable for modern REST APIs.
1import { z } from 'zod';
2
3const userSchema = z.object({
4 email: z.string().email().max(255),
5 password: z.string().min(12).max(100),
6 age: z.number().int().min(18).max(120).optional()
7});
8
9export const validatePayload = (req, res, next) => {
10 try {
11 req.body = userSchema.parse(req.body);
12 next();
13 } catch (error) {
14 res.status(400).json({ error: error.errors });
15 }
16};Conclusion
API Security is a layered defense model. By utilizing edge-based firewalls, implementing sophisticated rate limiting, rigidly enforcing CORS policies, and strictly validating payloads, you insulate your core application logic from the chaos of the public internet. Secure by default is the only way to build for the future.
Frequently Asked Questions
Can CORS prevent server-to-server attacks?
No. CORS is enforced by the web browser. If an attacker uses cURL, Postman, or a Python script to hit your API, CORS is completely bypassed. This is why rate limiting and authentication are vital.
What is the difference between a WAF and an API Gateway? An API Gateway focuses on routing, rate limiting, and analytics (handling traffic). A Web Application Firewall (WAF) inspects the actual payload of the traffic, blocking SQL injections, cross-site scripting attempts, and known malicious IPs before they reach the gateway.
Why should I limit the size of incoming JSON payloads?
If you don't limit payload sizes (e.g., express.json({ limit: '10kb' })), an attacker can send a 500MB JSON object. Parsing this massive object in memory will block Node's single thread and crash your server, resulting in an immediate Denial of Service (DoS).

Written by Gagan Shukla
Full Stack Mobile App Developer specializing in Flutter, React Native, and cross-platform application development.
Read Next
Designing Secure Authentication Architecture for Modern Web Apps
A deep dive into modern authentication patterns: JWTs, HttpOnly Cookies, OAuth2, and Zero Trust security models for enterprise-grade web applications.
Securing Cross-Platform Mobile Applications: Best Practices for Flutter
Stop storing API keys in your code. A masterclass in securing Flutter and React Native apps against reverse engineering and man-in-the-middle attacks.