If you're running an API β whether it powers a mobile app, third-party integrations, or internal tools β you're always at risk of misuse.
Sometimes itβs malicious: DDoS attacks, brute-force login attempts, or data scrapers.
Other times, itβs accidental: a buggy client stuck in a loop, or QA testing without throttling.
Either way, without rate limiting, your API becomes a wide-open door for traffic floods that can:
- π₯ Overload your servers and databases
- πΈ Skyrocket your cloud costs
- π Degrade performance for real users
- π Expose security vulnerabilities
The good news? There's a simple, proactive defense: Rate Limiting.
π¦ What Is Rate Limiting?
Rate limiting sets a maximum number of requests a client can make to your API within a given time window.
Example: "Each IP can make 100 requests per 10 minutes."
Itβs not just about protection β itβs also about fairness.
You donβt want one aggressive user (or bot) consuming all your resources while others get timeouts.
π‘ Why Rate Limiting Matters
βοΈ Performance Protection
A sudden burst of requests can queue up, slow down, or even crash your app. Rate limiting prevents this by smoothing out traffic spikes.
π Security Layer
Brute-force attacks, credential stuffing, and scraping become much harder when request volume is capped.
π° Cost Control
Cloud infrastructure scales with traffic β but so do your bills. Preventing junk traffic keeps costs predictable.
β Quality of Service
Legitimate users get consistent, fast responses instead of frustrating timeouts or errors.
β Common Mistakes Without Rate Limiting
- A single misconfigured client breaks the experience for everyone because its loop forgot
sleep()
. - Botnets consume all your API capacity during an attack.
- Your own QA team accidentally triggers a self-inflicted DoS by hammering endpoints without throttling.
π₯ One uncontrolled script can bring your entire service to its knees.
β
Solution 1: Rack::Attack
+ Redis (Great for Rails APIs)
Rack::Attack is a battle-tested gem for rate limiting in Ruby on Rails apps. Paired with Redis, it scales across multiple servers and handles distributed traffic.
How It Works:
- Define limits per IP, user token, or endpoint
- Automatically return
429 Too Many Requests
when exceeded - Allow safe-lists for health checks or internal services
- Log and monitor throttled requests for visibility
Setup
# Gemfile
gem "rack-attack"
gem "redis"
# config/application.rb
config.middleware.use Rack::Attack
# config/initializers/rack_attack.rb
Rack::Attack.cache.store = ActiveSupport::Cache::RedisCacheStore.new(url: ENV["REDIS_URL"])
Rack::Attack.throttle("requests per IP", limit: 100, period: 10.minutes) do |req|
req.ip
end
When the limit is hit, clients receive:
{
"error": "rate_limited",
"message": "Too many requests. Please retry later."
}
With HTTP status 429 Too Many Requests
.
π‘ Pro tip: Use custom throttling keys for authenticated users (req.session['user_id']
) or API keys.
β Solution 2: Rails 7.2 Built-In Rate Limiting
Starting in Rails 7.2, you can add rate limiting directly in controllers β no extra gems needed!
This feature is built into ActionController
and uses your existing cache store (Redis, Memcached, or memory).
Example 1: Limit Login Attempts
class SessionsController < ApplicationController
rate_limit to: 5, within: 1.minute, only: :create
end
Prevents brute-force attacks by limiting login attempts to 5 per minute per session/IP.
Example 2: Limit API Calls Per User
class Api::WidgetsController < ApplicationController
rate_limit to: 500,
within: 1.day,
by: -> { current_user&.id || request.remote_ip },
with: -> { render json: { error: "Rate limit exceeded" }, status: :too_many_requests },
only: :index
end
Key Options:
Option | Purpose |
---|---|
to: |
Max number of allowed requests |
within: |
Time window (e.g., 1.hour ) |
by: |
Discriminator (user ID, IP, API key) |
with: |
Custom response block when throttled |
only: / except:
|
Apply to specific actions |
β Perfect for simple, declarative, per-action limits without middleware complexity.
π‘ Final Thoughts: Rate Limiting = API Armor
Rate limiting isnβt just a performance tweak β itβs essential infrastructure hygiene.
It:
- Prevents single points of failure
- Defends against common attacks
- Keeps your cloud costs under control
- Ensures fair access for all users
Whether you're using Rack::Attack
or Rails 7.2βs native tools, adding rate limiting is one of the highest-impact, lowest-effort improvements you can make to your API.
π Start small. Protect critical endpoints. Scale as needed.
Your future self will thank you.
π¬ Have questions?
Are you using rate limiting in production? What strategy works best for your app?
Let me know in the comments π
Top comments (0)