{"id":25948828,"url":"https://github.com/nakhli/rack-defense","last_synced_at":"2025-07-25T09:35:31.511Z","repository":{"id":21544680,"uuid":"24864253","full_name":"nakhli/rack-defense","owner":"nakhli","description":"A rack middleware for throttling and filtering requests","archived":false,"fork":false,"pushed_at":"2018-11-09T17:27:06.000Z","size":61,"stargazers_count":83,"open_issues_count":1,"forks_count":4,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-01-30T07:47:26.162Z","etag":null,"topics":["rack","rack-middleware","rails","request-filtering","request-rate","ruby"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nakhli.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-10-06T20:50:06.000Z","updated_at":"2024-03-08T20:53:55.000Z","dependencies_parsed_at":"2022-08-21T03:20:08.128Z","dependency_job_id":null,"html_url":"https://github.com/nakhli/rack-defense","commit_stats":null,"previous_names":["sinbadsoft/rack-defense"],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nakhli%2Frack-defense","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nakhli%2Frack-defense/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nakhli%2Frack-defense/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nakhli%2Frack-defense/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nakhli","download_url":"https://codeload.github.com/nakhli/rack-defense/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241780462,"owners_count":20019061,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["rack","rack-middleware","rails","request-filtering","request-rate","ruby"],"created_at":"2025-03-04T11:23:50.298Z","updated_at":"2025-03-04T11:23:50.849Z","avatar_url":"https://github.com/nakhli.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"Rack::Defense\n=============\n\nA Rack middleware for throttling and filtering requests.\n\n[![Build Status](https://travis-ci.org/nakhli/rack-defense.svg)](https://travis-ci.org/nakhli/rack-defense)\n[![Security](https://hakiri.io/github/nakhli/rack-defense/master.svg)](https://hakiri.io/github/nakhli/rack-defense/master)\n[![Code Climate](https://codeclimate.com/github/nakhli/rack-defense/badges/gpa.svg)](https://codeclimate.com/github/nakhli/rack-defense)\n[![Gem Version](https://badge.fury.io/rb/rack-defense.svg)](http://badge.fury.io/rb/rack-defense)\n\nRack::Defense is a Rack middleware that allows to easily add request rate limiting and request filtering to your Rack based application (Ruby On Rails, Sinatra etc.).\n\n* [Request throttling](#throttling) (aka rate limiting) happens on __sliding window__ using the provided period, request criteria and maximum request number. It uses Redis to track the request rate.\n\n* [Request filtering](#filtering) bans (rejects) requests based on provided criteria.\n\nRack::Defense has a small footprint and only two dependencies: [rack](https://github.com/rack/rack) and [redis](https://github.com/redis/redis-rb).\n\nRack::Defense is inspired from the [Rack::Attack](https://github.com/kickstarter/rack-attack) project. The main difference is the throttling algorithm: Rack::Attack uses a counter reset at the end of each period, therefore allowing up to 2 times more requests than the maximum rate specified. We use a sliding window algorithm allowing a precise request rate limiting.\n\n## Getting started\n\nInstall the rack-defense gem; or add it to you Gemfile with bundler:\n\n```ruby\n# In your Gemfile\ngem 'rack-defense'\n```\n\nTell your app to use the Rack::Defense middleware. For Rails 3+ apps:\n\n```ruby\n# In config/application.rb\nconfig.middleware.use Rack::Defense\n```\n\nOr for Rackup files:\n\n```ruby\n# In config.ru\nuse Rack::Defense\n```\n\nAdd a `rack-defense.rb` file to `config/initializers/`:\n\n```ruby\n# In config/initializers/rack-defense.rb\nRack::Defense.setup do |config|\n  # your configuration here\nend\n```\n\n## Throttling\n\nThe Rack::Defense middleware evaluates the throttling criteria (lambdas) against the incoming request.\nIf the return value is falsy, the request is not throttled. Otherwise, the returned value is used as a key to\nthrottle the request. The returned key could be the request IP, user name, API token or any discriminator to throttle\nthe requests against.\n\n### Examples\n\nThrottle POST requests for path `/login` with a maximum rate of 3 request per minute per IP:\n\n```ruby\nRack::Defense.setup do |config|\n  config.throttle('login', 3, 60 * 1000) do |req|\n    req.ip if req.path == '/login' \u0026\u0026 req.post?\n  end\nend\n```\n\nThrottle GET requests for path `/api/*` with a maximum rate of 50 request per second per API token:\n\n```ruby\nRack::Defense.setup do |config|\n  config.throttle('api', 50, 1000) do |req|\n    req.env['HTTP_AUTHORIZATION'] if %r{^/api/} =~ req.path\n  end \nend\n```\n\nThrottle POST requests for path `/aggregate/report` with a maximum rate of 10 requests per hour for a given logged in user. We assume here that we are using the [Warden](https://github.com/hassox/warden) middleware for authentication or any Warden based authentication wrapper, like [Devise](https://github.com/plataformatec/devise) in Rails.\n\n```ruby\nRack::Defense.setup do |config|\n  config.throttle('aggregate_report', 10, 1.hour.in_milliseconds) do |req|\n    req.env['warden'].user.id if req.path == '/aggregate/report' \u0026\u0026 req.env['warden'].user\n  end \nend\n```\n\n### Redis Configuration\n\nRack::Defense uses Redis to track request rates. By default, the `REDIS_URL` environment variable is used to setup\nthe store. If not set, it falls back to host `127.0.0.1` port `6379`.\nThe redis store can be setup with either a connection url: \n\n```ruby\nRack::Defense.setup do |config|\n  config.store = \"redis://:p4ssw0rd@10.0.1.1:6380/15\"\nend\n```\n\nor directly with a connection object:\n\n```ruby\nRack::Defense.setup do |config|\n  config.store = Redis.new(host: \"10.0.1.1\", port: 6380, db: 15)\nend\n```\n\n## Filtering\n\nRack::Defense can reject requests based on arbitrary properties of the request. Matching requests are filtered out.\n\n### Examples\n\nAllow only a whitelist of IPs for a given path:\n\n```ruby\nRack::Defense.setup do |config|\n  config.ban('ip_whitelist') do |req|\n    req.path == '/protected' \u0026\u0026 !['192.168.0.1', '127.0.0.1'].include?(req.ip)\n  end\nend\n```\n\nDeny access to a blacklist of application users. Again, we assume here that \n[Warden](https://github.com/hassox/warden) or any Warden based authentication wrapper, like [Devise](https://github.com/plataformatec/devise), is used:\n\n```ruby\nRack::Defense.setup do |config|\n  config.ban('user_blacklist') do |req|\n    ['hacker@example.com', 'badguy@example.com'].include? req.env['warden'].user.email\n  end \nend\n```\n\nAllow only requests with a known API authorization token:\n\n```ruby\nRack::Defense.setup do |config|\n  config.ban('validate_api_token') do |req|\n    %r{^/api/} =~ req.path \u0026\u0026 Redis.current.sismember('apitokens', req.env['HTTP_AUTHORIZATION'])\n  end\nend\n```\n\nThe previous example uses redis to keep track of valid api tokens, but any store (database, key-value store etc.) would do here.\n\n## Response configuration\n\nBy default, Rack::Defense returns `429 Too Many Requests` and `403 Forbidden` respectively for throttled and banned requests.\nThese responses can be fully configured in the setup:\n\n```ruby\nRack::Defense.setup do |config|\n  config.banned_response =\n    -\u003e(env) { [404, {'Content-Type' =\u003e 'text/plain'}, [\"Not Found\\n\"]] }\n  config.throttled_response =\n    -\u003e(env) { [503, {'Content-Type' =\u003e 'text/plain'}, [\"Service Unavailable\\n\"]] }\nend\n```\n\n## Notifications\n\nYou can be notified when requests are throttled or banned. The callback receives the throttled request object and data\nabout the event context.\n\nFor banned request callbacks, the triggered rule name is passed: \n\n```ruby\nRack::Defense.setup do |config|\n  config.after_ban do |req, rule|\n    logger.info \"[Banned] #{rule} #{req.path} #{req.ip}\"\n  end\nend\n```\n\nFor throttled request callbacks, a hash having triggered rule names as keys and the corresponding throttle keys\nas values is passed. \n\n```ruby\nRack::Defense.setup do |config|\n  config.after_throttle do |req, rules|\n    logger.info rules.map { |e| \"[Throttled] rule name: #{e[0]} - rule throttle key: #{e[1]}\" }.join ', '\n  end\nend\n```\n\n## Advanced Examples\n\n### Temporarily suspend access to suspicious IPs\n\nIn this example, when an IP is exceeding the permitted request rate, we would like to ban this IP for a given period of time:\n\n```ruby\nRack::Defense.setup do |config|\n  config.throttle('reset_password', 10, 10.minutes.in_milliseconds) do |req|\n    req.ip if req.path == '/api/users/password' \u0026\u0026 req.post?\n  end\n\n  config.after_throttle do |req, rules|\n    config.store.setex(\"ban:ip:#{req.ip}\", 1.hour, 1) if rules.key? 'reset_password'\n  end\n\n  config.ban('blacklist') do |req|\n    config.store.exists(\"ban:ip:#{req.ip}\")\n  end\nend\n```\n\nThe first rule named `reset_password` defines the maximum permitted rate per IP for post requests on path\n`/api/users/password`. Once a user exceeds this limit, it gets throttled and denied access to the resource.\nThis raises a throttle event and triggers the `after_throttle` callback defined above. The callback sets a key in the redis store post-fixed with the user IP and having 1 hour an expiration time.\n\nThe last rule named `blacklist` looks up each incoming request IP and checks if it has a corresponding ban key\nin redis. If the request IP matches a ban key it gets denied.\n\n## License\n\nLicensed under the [MIT License](http://opensource.org/licenses/MIT).\n\nCopyright Chaker Nakhli.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnakhli%2Frack-defense","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnakhli%2Frack-defense","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnakhli%2Frack-defense/lists"}