rack/rack-attack
Rack middleware for blocking & throttling
Healthy across the board
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 5w ago
- ✓25+ active contributors
- ✓Distributed ownership (top contributor 39% of recent commits)
Show 3 more →Show less
- ✓MIT licensed
- ✓CI configured
- ✓Tests present
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/rack/rack-attack)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/rack/rack-attack on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: rack/rack-attack
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/rack/rack-attack shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across the board
- Last commit 5w ago
- 25+ active contributors
- Distributed ownership (top contributor 39% of recent commits)
- MIT licensed
- CI configured
- Tests present
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live rack/rack-attack
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/rack/rack-attack.
What it runs against: a local clone of rack/rack-attack — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in rack/rack-attack | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch main exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 64 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of rack/rack-attack. If you don't
# have one yet, run these first:
#
# git clone https://github.com/rack/rack-attack.git
# cd rack-attack
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of rack/rack-attack and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "rack/rack-attack(\\.git)?\\b" \\
&& ok "origin remote is rack/rack-attack" \\
|| miss "origin remote is not rack/rack-attack (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
&& ok "default branch main exists" \\
|| miss "default branch main no longer exists"
# 4. Critical files exist
test -f "lib/rack/attack.rb" \\
&& ok "lib/rack/attack.rb" \\
|| miss "missing critical file: lib/rack/attack.rb"
test -f "lib/rack/attack/check.rb" \\
&& ok "lib/rack/attack/check.rb" \\
|| miss "missing critical file: lib/rack/attack/check.rb"
test -f "lib/rack/attack/request.rb" \\
&& ok "lib/rack/attack/request.rb" \\
|| miss "missing critical file: lib/rack/attack/request.rb"
test -f "lib/rack/attack/cache.rb" \\
&& ok "lib/rack/attack/cache.rb" \\
|| miss "missing critical file: lib/rack/attack/cache.rb"
test -f "lib/rack/attack/configuration.rb" \\
&& ok "lib/rack/attack/configuration.rb" \\
|| miss "missing critical file: lib/rack/attack/configuration.rb"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 64 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~34d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/rack/rack-attack"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Rack::Attack is Rack middleware that protects Rails and Rack applications from abusive clients by enabling fine-grained control over when to allow, block, and throttle requests based on request properties. It uses in-memory or distributed caches (Redis, Memcached, Rails cache stores) to track request patterns and enforce rate limits, IP-based blocklists/safelists, and dynamic fail2ban/allow2ban banning strategies. Monolithic gem structure: lib/rack/attack.rb is the main entry point, with modular components under lib/rack/attack/ including checks (safelist.rb, blocklist.rb), throttling (throttle.rb), banning strategies (fail2ban.rb, allow2ban.rb), and pluggable store proxies (store_proxy/ directory supporting Redis, Memcached, Rails cache stores). Configuration lives in lib/rack/attack/configuration.rb, with examples in examples/ and docs in docs/.
👥Who it's for
Rails and Rack application developers who need production-grade DDoS protection, API rate limiting, and abuse prevention without external third-party services. Specifically DevOps engineers, API maintainers, and security-conscious web framework users who want middleware-level request filtering.
🌱Maturity & risk
Highly mature and production-ready. The project has a stable v6 release branch (6-stable), comprehensive CI via GitHub Actions (build.yml), extensive test coverage, and active maintenance with support for 8+ Rails versions and 5+ Redis versions. Last activity visible in Appraisals and gemfiles indicates recent updates.
Low risk. Single-maintainer dependency is possible but mitigated by strong test suite, stable API, and wide adoption in production Rails apps. The project maintains compatibility across many cache backends (Redis, Memcached, Rails 7-8, Dalli 3+) which adds complexity but is well-managed via appraisals. No obvious security vulnerabilities based on structure, though cache store selection can impact performance.
Active areas of work
Active development on multi-version compatibility: gemfiles/ contains appraisals for Rails 7.0-8.1 and Redis 4-5, indicating ongoing testing across dependency versions. The branch in README says viewers are on development branch with unreleased features. No specific milestones visible, but the structure shows continuous effort to maintain compatibility.
🚀Get running
git clone https://github.com/rack/rack-attack.git
cd rack-attack
bundle install
bundle exec rake test
Daily commands:
bundle exec rake test # Run full test suite
bundle exec rubocop # Check style compliance
rake appraisal:install # Install all gemfile variants
rake appraisal:test # Test against all dependency versions
🗺️Map of the codebase
lib/rack/attack.rb— Main entry point and middleware orchestrator that initializes all checks, handles request filtering, and coordinates the entire blocking/throttling pipeline.lib/rack/attack/check.rb— Abstract base class for all check types (blocklist, safelist, throttle, track); defines the interface and lifecycle every rule must implement.lib/rack/attack/request.rb— Extended Rack request wrapper that provides convenient accessors for IP, path, and custom properties used throughout rule matching.lib/rack/attack/cache.rb— Unified cache abstraction layer managing backend stores (Redis, Memcached, etc.) for throttle counters and fail2ban/allow2ban state.lib/rack/attack/configuration.rb— Configuration DSL parser and validator; processes block-based rule definitions and manages global settings for the middleware.lib/rack/attack/throttle.rb— Rate-limiting rule implementation with per-key counters; drives the core throttling behavior and response logic.lib/rack/attack/fail2ban.rb— Automatic IP banning via counter threshold; escalates throttles into blocks after repeated violations within a time window.
🛠️How to make changes
Add a new blocklist rule
- In your configuration file or initializer, call
Rack::Attack.blocklist()with a name and block returning truthy for requests to block (examples/rack_attack.rb) - The block receives a Rack::Attack::Request and should use properties like
request.ip,request.path, or custom accessors (lib/rack/attack/request.rb) - Blocked requests are immediately halted; configure a custom response via
Rack::Attack.blocklist_responseif needed (lib/rack/attack.rb)
Add a new throttle rule with rate limits
- Call
Rack::Attack.throttle()with a name, limit, period, and discriminator block (examples/rack_attack.rb) - The discriminator block returns a unique key (e.g., IP, user_id); Rack::Attack increments a counter per key in cache (
lib/rack/attack/throttle.rb) - When count exceeds limit within the period, requests are throttled; customize the response with
Rack::Attack.throttled_response(lib/rack/attack.rb) - Cache backend defaults to in-memory; configure Redis or Memcached via
Rack::Attack.cache.store =(lib/rack/attack/cache.rb)
Integrate Fail2Ban for auto-escalation
- Define a throttle rule as normal (e.g., limit per IP per endpoint) (
examples/rack_attack.rb) - Separately, call
Rack::Attack.fail2ban()specifying the same discriminator, a failure threshold, and ban duration (lib/rack/attack/fail2ban.rb) - Fail2Ban monitors throttle hits; when threshold is crossed, it adds the key to a hard blocklist automatically (
lib/rack/attack.rb) - Optionally define a safelist to exempt trusted IPs from Fail2Ban escalation (
lib/rack/attack/safelist.rb)
Add a custom cache store backend
- Create a new proxy class in
lib/rack/attack/store_proxy/inheriting fromBaseProxy(lib/rack/attack/base_proxy.rb) - Implement
#incrementand#readmethods to wrap your store's API (e.g., Redis INCR, GET) (lib/rack/attack/store_proxy/redis_proxy.rb) - Register the proxy in the cache initialization logic so it's auto-detected, or manually set
Rack::Attack.cache.store = YourProxy.new(client)(lib/rack/attack/cache.rb)
🔧Why these technologies
- Rack Middleware — Provides standard, language-agnostic interface for HTTP filtering; integrates seamlessly with Rails and any Rack-based framework without code changes
- Pluggable Cache Backends (Redis, Memcached, ActiveSupport) — Distributed rate-limiting counters and ban state must be shared across processes; supports production deployments at scale and development-friendly in-memory stores
- Time-window based Throttling — Simple, predictable rate-limiting model; avoids leaky bucket complexity while providing intuitive per-second/per-minute limits
- Fail2Ban Escalation Pattern — Automatic adaptive blocking: punishes repeated attackers without manual intervention; threshold-based to avoid false positives from temporary spikes
⚖️Trade-offs already made
-
Single-process in-memory cache as default
- Why: Zero dependencies; easy setup for development and small deployments
- Consequence: Not suitable for multi-process or distributed deployments; counters reset on process restart
-
Per-discriminator (IP, user_id, etc.) counters rather than per-route buckets
- Why: Simpler, more flexible rule configuration; natural match to real-world threat models
- Consequence: Cannot easily express per-user-per-endpoint limits without custom code; requires multiple throttle rules
-
Fail2Ban as separate explicit rule, not automatic on throttle
- Why: Gives operators explicit control over escalation; avoids accidental auto-bans from misconfigured throttles
- Consequence: Requires additional configuration; users must remember to pair throttles with fail2ban for auto-escalation
-
Synchronous cache increment/read in request path
- Why: Guarantees accurate counters; no queueing latency or eventual consistency issues
- Consequence: Request latency bound by cache latency (5ms+ for Redis over network
🪤Traps & gotchas
- Cache store initialization: Must configure
Rack::Attack.cache.store =before middleware processes requests, or use Rails cache auto-detection (requires Rails). 2. Redis connection requirements: Redis/Memcached must be running for throttle state; without it, middleware fails open (allows traffic) by default. 3. IP detection: Request IP detection depends on header configuration (X-Forwarded-For, CF-Connecting-IP); must align with reverse proxy setup. 4. Thread safety: Cache store proxies must be thread-safe; some (like plain Redis) may need connection pooling. 5. Rack vs Rails: Middleware works with bare Rack but Rails Railtie (lib/rack/attack/railtie.rb) auto-inserts it; avoid double-insertion if manually adding.
🏗️Architecture
💡Concepts to learn
- Token Bucket Rate Limiting — Core algorithm in throttle.rb; understanding how tokens refill per period and are consumed per request is critical to configuring effective rate limits
- Fail2Ban / Allow2Ban Dynamic Banning — Rack::Attack's fail2ban.rb and allow2ban.rb implement automatic escalating bans based on request patterns; understanding the state machine (strikes, bans, resets) is key to preventing false positives
- IP Subnet Matching (CIDR Notation) — Safelists and blocklists use CIDR subnets (e.g., 192.168.0.0/16); understanding IP masking and range checking prevents misconfiguration of network-level rules
- Distributed Cache Coherency — When using Redis/Memcached backends, throttle counts and ban state are shared across multiple app processes; cache store selection impacts correctness of rate limits under load
- Rack Middleware Execution Order — Rack::Attack must run early in middleware stack to block before app code executes; placement relative to Rails middleware (auth, logging) affects rule matching and performance
- Proxy Header Trust (X-Forwarded-For) — Real client IP detection relies on reverse proxy headers; misconfigured header trust can lead to blocking wrong clients or spoofing by attackers
- Store Proxy Adapter Pattern — Rack::Attack abstracts cache backends via pluggable proxies (Redis, Memcached, Rails cache); understanding how store_proxy/ adapts different backend APIs to common interface is key to extending backends
🔗Related repos
redis/redis-rb— Official Redis Ruby client; required for Redis-backed cache store in Rack::Attackmemcached/memcached— Memcached protocol server; alternative distributed cache backend supported via Dalli adapterrails/rails— Rack::Attack ships with Railtie for Rails integration and uses Rails cache store adapterskickstarter/rack-ssl-enforcer— Complementary Rack middleware for HTTPS enforcement; often used alongside Rack::Attack for securityjordansissel/fpm— Not a direct relationship, but fail2ban and allow2ban strategies in Rack::Attack are inspired by Linux fail2ban tool concepts
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive integration tests for all store_proxy implementations
The repo supports multiple cache backends (Redis, Memcached, ActiveSupport::Cache) via store_proxy classes, but spec/acceptance lacks targeted tests for each proxy type. Currently cache_store_config_*_spec.rb files exist but they test high-level behavior. Missing are specific unit tests for each store_proxy implementation (RedisProxy, DalliProxy, MemCacheStoreProxy, RedisCacheStoreProxy, RedisStoreProxy) covering edge cases like connection failures, serialization, TTL handling, and concurrent access patterns.
- [ ] Create spec/unit/rack/attack/store_proxy/ directory structure
- [ ] Add spec/unit/rack/attack/store_proxy/redis_proxy_spec.rb with tests for get/set/delete operations
- [ ] Add spec/unit/rack/attack/store_proxy/dalli_proxy_spec.rb covering Memcached-specific behavior
- [ ] Add spec/unit/rack/attack/store_proxy/redis_cache_store_proxy_spec.rb and other cache store variants
- [ ] Test error handling when backend is unavailable
- [ ] Test TTL and expiration behavior across all proxies
Add comprehensive documentation for PathNormalizer and Request customization
The codebase includes lib/rack/attack/path_normalizer.rb and lib/rack/attack/request.rb which enable powerful request filtering and path-based throttling, but docs/advanced_configuration.md doesn't cover these features. spec/acceptance/extending_request_object_spec.rb exists showing usage patterns, but users lack clear guidance on when and how to use these features.
- [ ] Create docs/request_customization.md documenting lib/rack/attack/request.rb
- [ ] Document lib/rack/attack/path_normalizer.rb with real-world examples (e.g., normalizing API versioning, query parameter ordering)
- [ ] Add examples to docs/ showing how to extend Rack::Attack::Request for custom matching logic
- [ ] Include performance considerations for custom request properties
- [ ] Reference existing spec/acceptance/extending_request_object_spec.rb patterns in documentation
Add support matrix and CI workflow for newer Ruby/Rails/Rack versions
The gemfiles/ directory shows extensive version testing setup (Rails 8.1, Ruby 3.x support expected), but .github/workflows/build.yml doesn't explicitly document which Ruby/Rails/Rack version combinations are officially tested. The Appraisals file suggests multi-version testing is configured but CI output doesn't clearly show a support matrix. Adding explicit version compatibility tests and documentation would help contributors understand what they need to test.
- [ ] Add explicit Ruby version matrix to .github/workflows/build.yml (e.g., 3.1, 3.2, 3.3, 3.4)
- [ ] Update .github/workflows/build.yml to test with multiple Rack versions explicitly (rack_2.gemfile, rack_3.gemfile)
- [ ] Create docs/compatibility_matrix.md documenting officially tested combinations
- [ ] Add Ruby version requirement check to rack-attack.gemspec if not already present
- [ ] Add CI workflow that runs bundler with each gemfile/ configuration and reports results
🌿Good first issues
- Add integration tests for
lib/rack/attack/path_normalizer.rb: File exists but is not clearly tested in visible test structure. Normalize path handling for routes with query strings or fragments. - Expand example configurations in
examples/rack_attack.rb: Add real-world patterns like tiered throttling (different limits for authenticated vs guest users), API endpoint-specific rules, and webhook signature validation examples. - Document cache store proxy behavior: Add troubleshooting guide in
docs/explaining what happens when Redis/Memcached is down, fallback modes, and performance characteristics of each store_proxy adapter.
⭐Top contributors
Click to expand
Top contributors
- @grzuy — 39 commits
- @santib — 29 commits
- @ioquatix — 4 commits
- @floehopper — 3 commits
- @byroot — 2 commits
📝Recent commits
Click to expand
Recent commits
728d610— Eagerly check for store validity (#714) (byroot)6c16764— Test against Ruby 4.0 (#715) (byroot)e938879— Upgrade rubocop (#710) (santib)d1a979d— Remove unnecessary pooled specs (#708) (santib)77195a5— lowercase header key on README responder example for Rack 3 compatibility (#705) (r-plus)1155c26— ci: test against a compatible connection_pool version (#706) (santib)4a021df— Inherit Request class from ActionDispatch if available (bdewater-thatch)b2ebab9— build(deps): bump actions/checkout from 5 to 6 (#702) (dependabot[bot])676ba26— ci: updates ruby versions (grzuy)6a9209f— ci: updates rails versions (grzuy)
🔒Security observations
Rack::Attack is a mature security middleware project with a well-organized codebase. No critical vulnerabilities were identified from the static file structure analysis. Primary concerns are around dependency version management across multiple supported versions and potential cache backend configuration risks. The project lacks explicit security best practices documentation but otherwise demonstrates good architectural separation of concerns with dedicated modules for different protection mechanisms (blocklist, safelist, throttle, fail2ban, allow2ban). Recommended actions include enabling automated dependency vulnerability scanning, documenting secure configuration for supported cache backends, and adding security best practices guidelines.
- Medium · Dependency Version Management Risk —
gemfiles/ directory (multiple gemfiles). The codebase supports multiple versions of dependencies (Rails 7.0-8.1, Redis 4-5, Active Support 7.0-8.1, etc.) through Appraisals gemfiles. While this enables broad compatibility testing, it increases the attack surface if older dependency versions contain known vulnerabilities. The gemfiles directory shows support for potentially outdated versions that may have unpatched security issues. Fix: Implement a dependency vulnerability scanner in CI/CD (e.g., Dependabot, Snyk). Set minimum version requirements for dependencies with known CVEs. Consider deprecating support for versions older than 2 years. Review .github/dependabot.yml configuration to ensure automated security updates are enabled. - Medium · Potential Cache Backend Security Issues —
lib/rack/attack/store_proxy/ directory and lib/rack/attack/cache.rb. The codebase supports multiple cache backends (Redis, Dalli/Memcached, Active Support cache stores) through store proxy implementations. Different backends may have varying security properties (e.g., Memcached lacks authentication by default, Redis may be exposed). Improper configuration could expose the cache store to unauthorized access. Fix: Document security requirements for each cache backend. Recommend TLS/SSL for Redis, proper authentication configuration, and network isolation. Provide configuration examples with security best practices. Add validation warnings when insecure defaults are detected. - Low · Missing Security Headers Documentation —
README.md and docs/. While Rack::Attack handles request blocking and throttling, the README and documentation do not explicitly document the security headers being set or recommended additional security middleware to work in conjunction. Fix: Add a security best practices section documenting recommended complementary security headers and middleware. Include configuration examples for common security scenarios (e.g., rate limiting API endpoints, blocking known malicious IPs). - Low · No Evidence of Input Validation on Request Parameters —
lib/rack/attack/request.rb and lib/rack/attack/path_normalizer.rb. The codebase contains a custom Request object (lib/rack/attack/request.rb) and path normalizer (lib/rack/attack/path_normalizer.rb). Without visible input validation implementation, there could be edge cases where specially crafted requests bypass protection mechanisms. Fix: Review path normalization logic for bypasses (e.g., double URL encoding, Unicode normalization). Add tests for edge cases. Document any known limitations in path matching and wildcard handling.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.