Lightweight Express middleware for rate limiting, unique client fingerprinting, Cloudflare-aware IP trust, and essential security headers 🛡️
Now with auto-updated Cloudflare IP lists, hot-reload without restart, and zero dependency design for production-grade security and flexibility.
- 🔁 Auto-updated Cloudflare IPs: Automatically keeps the IP whitelist in sync with Cloudflare’s official ranges (IPv4/IPv6) and hot-reloads without restart.
- 🚦 Rate Limiting: Configurable time window and maximum requests per IP address (with Cloudflare trusted IP logic).
- 🔒 Fingerprinting: Generates a unique, consistent client fingerprint for better request tracking.
- 🛡️ Security Headers: Automatically applies crucial HTTP security headers (
X-Content-Type-Options
,Strict-Transport-Security
,X-Frame-Options
,Referrer-Policy
, and more). - 🎨 Custom Error Responses: Override default 429 (Too Many Requests) handler with custom logic/format.
- 📦 Modular Design: Redis is optional; rate limiting is skipped if no Redis is supplied.
- ⚙️ TypeScript Support: Built-in
.d.ts
types, fully typed exports. - 🔄 Zero External Dependency: No deps except Express and (optionally) ioredis for rate-limit.
npm install @neabyte/express-shield
Requires Node.js >=18.20.8
- After install, Cloudflare IPs are fetched to
data/
folder. - To update manually:
node ./script/update-cloudflare-ip.js
const express = require('express')
const Redis = require('ioredis')
const { createExpressShield } = require('@neabyte/express-shield')
const app = express()
const redisClient = new Redis()
app.use(
createExpressShield(
redisClient,
{
app,
windowSec: 60,
maxRequests: 10,
showHeader: true,
showLog: false,
applySecurity: true,
customErrorResponse: (req, res, ttl) => {
res.status(429).json({
error: 'Too many requests',
ip: req.fingerprint.ip,
retryAfterSec: ttl
})
}
}
)
)
app.get('/', (req, res) => {
res.send('Hello, world! 🎉 This route is protected with rate limiting and security headers.')
})
app.listen(3000, () => console.log('Server running on port 3000 🚀'))
const express = require('express')
const { createExpressShield } = require('@neabyte/express-shield')
const app = express()
app.use(
createExpressShield(
null,
{
app,
applySecurity: true
}
)
)
app.get('/', (req, res) => {
res.send('Hello, world! 🎉 This route is protected with security headers.')
})
app.listen(3000, () => console.log('Server running on port 3000 🚀'))
-
The IP whitelist used for trusting
cf-connecting-ip
is auto-updated using Cloudflare’s published range. -
After install, files
data/cloudflare_ipv4.json
anddata/cloudflare_ipv6.json
are created/updated. -
IPs are hot-reloaded at runtime (no restart needed).
-
To force reload in app code:
const { reloadCloudflareIpList } = require('@neabyte/express-shield') reloadCloudflareIpList()
Parameter | Type | Default | Description |
---|---|---|---|
redisClient |
object? | null |
Optional ioredis instance. If falsy, disables rate limiting. |
options.app |
Express | null |
Express app instance for header patching. |
options.windowSec |
number | 60 |
Duration of the rate limit window (seconds). |
options.maxRequests |
number | 10 |
Max allowed requests per IP within the window. |
options.showHeader |
boolean | true |
Show X-Fingerprint-Id header. |
options.showLog |
boolean | false |
Enable debug console logging. |
options.applySecurity |
boolean | true |
Apply HTTP security headers. |
options.customErrorResponse |
function | default 429 JSON | Custom 429 handler. (req, res, ttl) => void |
options.whitelist |
string[] | [] |
Skip rate-limit for these IPs. |
options.ipResolver |
function | getClientIpAddress |
Custom IP resolver (rarely needed). |
-
createExpressShield(redisClient, options)
Creates the Express middleware. Handles security headers, fingerprinting, rate limit (if Redis provided). -
getClientIpAddress(req)
Returns the trusted client IP. Automatically checkscf-connecting-ip
if request comes from Cloudflare IP range. -
generateFingerprintHash(req)
Returns a SHA256 hash of client IP + headers (used for fingerprinting). -
reloadCloudflareIpList()
Forces reload of Cloudflare IPv4 + IPv6 lists fromdata/
folder. Useful for manual reload or custom schedulers. -
watchCloudflareIpList()
Starts file watcher. Automatically reloads Cloudflare IPs whendata/cloudflare_ipv4.json
ordata/cloudflare_ipv6.json
change. -
CLOUDFLARE_IPV4
/CLOUDFLARE_IPV6
Exposes current trusted Cloudflare IP lists (arrays of CIDR). -
checkIpInCidrList(ip, cidrList)
Utility to check if an IP (IPv4/IPv6) is within any CIDR in the list. -
checkIpv4InCidr(ip, cidr)
/checkIpv6InCidr(ip, cidr)
Low-level CIDR match utilities for IPv4 / IPv6. -
convertIpv4ToNumber(ip)
/convertIpv6ToBigint(ip)
Internal helpers to convert IP addresses for CIDR math (exposed for testability).
Note: All utility functions are exposed primarily for testing and advanced use cases.
Typical users only needcreateExpressShield
and optionallyreloadCloudflareIpList
.
npm install
npm test
- Fork the repo 🔱
- Create your feature branch (
git checkout -b feature/your-feature-name
) - Commit your changes (
git commit -am 'Add some your-feature-name'
) - Push to the branch (
git push origin feature/your-feature-name
) - Open a Pull Request 📬
For questions, issues or feature requests, please open an issue or start a discussion.
See CHANGELOG.md for all updates.
MIT License © 2025 NeaByteLab