How to Rate Limit Your Ruby On Rails Controllers
Rails 8 introduced a new rate_limit feature for controllers, which makes throttling requests much easier to implement and manage than that was back in the day. Instead of relying on middleware or third-party gems, you can now declare rate limits directly in your controllers and centralize how you respond when clients exceed them 🙂
A common pattern is to define a base rule in a parent controller. In this example, ApiController limits requests by IP to five per minute:
class ApiController < ApplicationController
rate_limit to: 5, within: 1.minute, by: :ip, with: -> { handle_rate_limit }
private
def handle_rate_limit(message: "Server is busy. Please try again later.")
redirect_to busy_path(error: message)
end
end
The handle_rate_limit method provides a centralized place to decide what happens when the limit is reached. Instead of sprinkling custom responses across multiple controllers, you have one consistent handler.
Controllers that inherit from ApiController can declare their own limits without losing the shared handler. For example, a UsersController might restrict signup attempts by domain:
class UsersController < ApiController
rate_limit to: 1000, within: 10.seconds,
by: -> { request.domain },
with: -> { handle_rate_limit(message: "Too many signups on your domain.") },
only: :create
def create
# signup logic
end
end
Here, the base IP-based rule still applies to all actions, but the create action has an additional domain-based limit with its own message. This flexibility means you can apply different throttling strategies depending on context.
Finally, the BusyController provides a clean, consistent response:
class BusyController < ApplicationController
def index
render json: { error: params[:error] }, status: :too_many_requests
end
end
This ensures clients always receive a proper 429 Too Many Requests response with an error message. You could extend this later with logging, alerting, or custom JSON structures without touching every controller.
By combining rate_limit with a centralized handler and a dedicated controller for responses, Rails 8 turns rate limiting into a straightforward solution. You get both flexibility and consistency while keeping your controllers focused on their core purpose.