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 Requestswhen 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)