npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@veloze/ban

v0.1.0

Published

IP ban middleware

Downloads

76

Readme

npm-badge actions-badge types-badge

@veloze/ban

Rate limiting and brute-force protection middleware for veloze. Works also with express

Table of Contents

Installation

npm i @veloze/ban

For Redis support, also install ioredis:

npm i ioredis

Quick Start

Ban clients by IP address after too many failed login attempts:

import { Server, response, HttpError } from 'veloze'
import { banIp, banIpOnFailure, MemoryStore } from '@veloze/ban'

// use the memory store only for testing
const store = new MemoryStore()

const app = new Server()

app.use(
  // Check if IP is banned (allow max 5 failures)
  banIp(store, { max: 5, isBehindProxy: false, expiresInMs: '15min' }),

  // Your route handlers
  function handler(req, res, next) {
    // Simulate login check
    const authenticated = Math.random() > 0.7
    if (!authenticated) {
      return next(new HttpError(403)) 
    }
    response.send(res, 200, { message: 'Welcome!' })
  },

  // Ban IP on authentication errors (401, 403)
  banIpOnFailure({ statusCodes: [401, 403] })
  
  function finalErrorHandler(err, req, res, _next) {
    res.statusCode = err.status || 500
    res.end(err.message)
  }
)

app.listen(3000)

Features

  • Multiple banning strategies: Ban by IP address, device fingerprint, or custom key
  • Flexible storage backends: In-memory storage or Redis for distributed systems
  • Automatic expiration: Bans automatically expire after a configurable duration
  • Failure-triggered bans: Automatically ban clients after error responses
  • Proxy support: Handles X-Forwarded-For header for proxied requests
  • Framework agnostic: Works with veloze, express, or any Node.js HTTP middleware

Usage

Storing Bans

MemoryStore

In-memory store suitable for development and single-instance deployments.

Note: Do not use in production if scaling beyond one node instance is required or if running in cluster mode.

import { MemoryStore, banIp } from '@veloze/ban'

const store = new MemoryStore()
const banMiddleware = banIp(store, { max: 10 })

RedisStore

Distributed Redis store for production deployments across multiple instances.

Requires: npm i ioredis

import Redis from 'ioredis'
import { RedisStore, banIp } from '@veloze/ban'

const redis = new Redis({
  host: '127.0.0.1',
  port: 6379
  // keyPrefix is automatically set to 'ban:' but can be overridden
})

const store = new RedisStore({ client: redis })
const banMiddleware = banIp(store, { max: 10 })

Banning Strategies

Ban by IP Address

Ban clients based on their IP address. Handles proxied requests with the X-Forwarded-For header.

import { Server } from 'veloze'
import { banIp, MemoryStore } from '@veloze/ban'

const store = new MemoryStore()
const app = new Server()

// Ban after 10 failed attempts from the same IP
app.use(
  banIp(store, {
    max: 10,
    isBehindProxy: false, // Set to true if behind a reverse proxy
    expiresInMs: '30min' // uses "ms" syntax
  })
)

// Ban IP on 401/403 errors
app.use(
  async (req, res) => {
    /* auth logic */
    if (!authenticated) {
      throw new HttpError(401)
    }
  },
  banIpOnFailure({
    statusCodes: [401, 403]
  })
)

Ban by Device Fingerprint

Ban clients based on their device fingerprint (combination of IP, User-Agent, and headers).

import {
  banFingerprint,
  banFingerprintOnFailure,
  MemoryStore
} from '@veloze/ban'

const store = new MemoryStore()

// Ban after 5 suspicious requests from the same device
app.use(
  banFingerprint(store, {
    max: 5,
    isBehindProxy: false,
    expiresInMs: '5 min'
  })
)

// Ban device on 401/403 errors
app.use(
  async (req, res) => {
    /* auth logic */
    if (!authenticated) {
      throw new HttpError(401)
    }
  },
  banFingerprintOnFailure({
    statusCodes: [401, 403]
  })
)

Custom Ban Logic

Implement custom banning logic by using the generic ban() handler with a custom getKey() function.

import { ban, banOnFailure, MemoryStore } from '@veloze/ban'

const store = new MemoryStore()

// Ban by user ID
const banByUserId = ban(store, {
  max: 20,
  getKey: (req) => req.user?.id || null
})

// Ban user on errors
const banUserOnFailure = banOnFailure({
  statusCodes: [401, 403]
})

app.use(banByUserId)
app.use(async (req, res) => {
  /* handler */
})
app.use(banUserOnFailure)

API

banIp()

Ban clients by IP address.

function banIp(
  store: Store,
  options: {
    max?: number // Default: 10
    isBehindProxy?: boolean // Default: false
  }
): Handler

Parameters:

  • store: A ban store instance (MemoryStore or RedisStore)
  • options:
    • max: Maximum number of failures before banning (default: 10)
    • isBehindProxy: If true, extracts client IP from X-Forwarded-For header

Response: Calls next() if IP is not banned, calls next(error) with 429 status if banning exceeds max failures.

banIpOnFailure()

Increments ban counter for IP on request failure. Should be placed after your route handlers.

function banIpOnFailure(options?: {
  statusCodes?: number[] // Default: [401, 403]
}): ErrorHandler

Parameters:

  • options (optional):
    • statusCodes: HTTP status codes that trigger a ban increment

banFingerprint()

Ban clients by device fingerprint (combination of IP, User-Agent, and HTTP headers).

function banFingerprint(
  store: Store,
  options: {
    max?: number // Default: 10
    isBehindProxy?: boolean // Default: false
  }
): Handler

banFingerprintOnFailure()

Increments ban counter for device fingerprint on request failure.

function banFingerprintOnFailure(options?: {
  statusCodes?: number[] // Default: [401, 403]
}): ErrorHandler

ban()

Generic ban middleware for custom banning logic.

function ban(
  store: Store,
  options: {
    max?: number // Default: 10
    getKey: (req) => string
    expiresInMs?: string | number // Default: '1min'
    getExpiration?: (count: number) => number
  }
): Handler

Parameters:

  • store: A ban store instance (MemoryStore or RedisStore)
  • options:
    • getKey: Function that extracts a unique identifier from the request (e.g., user ID, IP, or fingerprint)
    • max: Maximum number of failures before banning (default: 10)
    • expiresInMs: Time until ban expires (e.g., "15min", "1 hour", 900000). Default: '1min'
    • getExpiration: Optional function to calculate custom expiration based on ban count

banOnFailure()

Generic error middleware for custom banning logic on failure.

function banOnFailure(options?: {
  statusCodes?: number[] // HTTP status codes that trigger ban
}): ErrorHandler

Parameters:

  • options (optional):
    • statusCodes: HTTP status codes that trigger a ban increment

MemoryStore Class

In-memory ban storage.

class MemoryStore {
  // Get ban count for a key
  get(key: string): Promise<number>

  // Increment ban count and set expiration
  incr(key: string, expires: number): Promise<number>

  // Get number of active bans
  size(): Promise<number>

  // Clear all bans
  clear(): Promise<void>
}

RedisStore Class

Redis-based ban storage for distributed deployments.

class RedisStore {
  constructor(opts: { client: Redis })

  // Get ban count for a key
  get(key: string): Promise<number>

  // Increment ban count and set expiration
  incr(key: string, expires: number): Promise<number>

  // Get number of active bans (requires scanning all keys)
  size(): Promise<number>

  // Clear all bans (requires scanning all keys)
  clear(): Promise<void>
}

License

MIT licensed