AMZ Cart Share

Share (Amazon)Export (Amazon)Export (AliExpress)FAQInstall ↗
← Blog

Last updated 28th April, 2026

Using native Rails rate-limits in production

Did you know that Rails now ships with native rate-limiting?

Previously, to protect your app from a barrage of requests, you'd use rack-attack, a popular rate-limiting gem. That functionality is now builtin to Rails, as of v7.2!

This native rate-limiting is what I currently use in production for AMZ Cart Share to protect our backend API endpoints.

Note: Rack::Attack still has its place, for advanced rules or Rack-layer blocking. This new, built-in rate-limiting is simpler. For most cases though, as you'll see, it's plenty.

For some quick context, this app has 2 systems which speak to each other: a cart-sharing Chrome extension + a backend API. The backend API is what I've protected with rate-limits.

Rate limiting a controller endpoint, the easy way

It couldn't be simpler to add rate-limits to your controllers (below is copied almost verbatim from our codebase).

A single line will do it:

class Api::MyController < ApplicationController
    rate_limit to: 10, within: 3.minutes, only: :create

    def create; end
end

Breaking down the call to rate_limit:

I'd highly recommend reading the full API reference for more info and some neat examples. For instance, you can define multiple rate-limits per controller over different time horizons:

# app/controllers/sessions_controller.rb

class SessionsController < ApplicationController
    rate_limit to: 3, within: 2.seconds, name: "short-term"
    rate_limit to: 10, within: 5.minutes, name: "long-term"

    def show; end
    def create; end
end

There's more too: you can define custom uniqueness constraints with by: (default is per-ip), rate-limits spanning multiple controllers with scope:, custom error responses using with:... the list goes on!

Note: While controller-based rate limits were introduced in Rails 7.2, name: and scope: were only introduced in Rails 8. Double-check your app version so you know which arguments you can pass.

Using rate_limit is essentially plug-and-play, apart from one bit of setup you need to do - ensuring you've configured a production cache store:

Configuring a cache store in production

Rails needs somewhere to actually store all the info for tracking these rate limits.

From the API docs:

Rate limiting relies on a backing ActiveSupport::Cache store and defaults to config.actioncontroller.cachestore, which itself defaults to the global config.cache_store.

Essentially, you want to make sure you've set config.cache_store in your production config, config/environments/production.rb to something sensible.

If you leave this un-configured, Rails will default to file_store, which can work, but with some caveats:

If you want to use Redis instead, you can set it like this:

config.cache_store = :redis_cache_store, { url: ENV.fetch("REDIS_URL") }

Note: Rails 8 defaults to Solid Cache instead, which doesn't have the file_store drawbacks. If you're already using Solid Cache, you're already all set up!

Conclusion

For further reading, check out this short write-up, which digs a little bit deeper into this native rate limiting.

And don't forget to read the API docs! Seriously, they're great — short, sweet, and full of great examples.

You can also explore the implementation directly on GitHub.

First published on 28th April, 2026