AMZ Cart Share

ShareExportExport (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.

Author

🙋‍♂️ Harrison Broadbent

Harrison is the founder/creator of AMZ Cart Share, a Chrome extension used by 10,000+ Amazon shoppers, schools, and businesses to share Amazon carts as links. He writes about ways to share, organize, and collaborate using shared Amazon shopping carts.

About AMZ Cart ShareView all extensions

First published on 28th April, 2026