Bird
Raised Fist0
Ruby on Railsframework~10 mins

Rate limiting in Ruby on Rails - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - Rate limiting
Request Received
↓
Check Rate Limit
↓
Reject
↓
Send 429
↓
Update Count
↓
End
When a request comes in, the system checks if the user has exceeded allowed requests. If yes, it rejects with an error. Otherwise, it processes and updates the count.
Execution Sample
Ruby on Rails
class ApplicationController < ActionController::Base
  before_action :check_rate_limit

  def check_rate_limit
    key = "user:#{request.ip}:requests"
    count = Rails.cache.read(key) || 0
    if count >= 5
      render plain: "Rate limit exceeded", status: 429
    else
      Rails.cache.write(key, count + 1, expires_in: 1.minute)
    end
  end
end
This code limits each IP to 5 requests per minute, rejecting requests beyond that with a 429 error.
Execution Table
StepRequest #Cache Read (count)Condition (count >= 5)ActionResponse
110FalseIncrement count to 1Allow request
221FalseIncrement count to 2Allow request
332FalseIncrement count to 3Allow request
443FalseIncrement count to 4Allow request
554FalseIncrement count to 5Allow request
665TrueReject request429 Rate limit exceeded
775TrueReject request429 Rate limit exceeded
Exit----Requests blocked after 5 allowed in 1 minute
💡 After 5 requests, count reaches 5 and condition becomes true, so further requests are rejected.
Variable Tracker
VariableStartAfter 1After 2After 3After 4After 5After 6Final
count01234555
request #N/A1234567
Key Moments - 2 Insights
Why does the count stay at 5 after the 6th request instead of increasing?
Because the condition count >= 5 is true at step 6, the request is rejected and the count is not incremented, as shown in execution_table rows 6 and 7.
What happens if the cache expires after 1 minute?
The count resets to 0 because the cache key expires, allowing new requests to be counted fresh, restarting the rate limit window.
Visual Quiz - 3 Questions
Test your understanding
Look at the execution table, what is the count value read from cache at request #4?
A3
B4
C2
D5
💡 Hint
Check the 'Cache Read (count)' column at Step 4 in the execution_table.
At which request number does the condition count >= 5 become true?
A5
B6
C4
D7
💡 Hint
Look at the 'Condition (count >= 5)' column in the execution_table to find when it first becomes true.
If the limit was changed to 3 requests per minute, what would happen at request #4?
ARequest #4 allowed, count increments to 4
BRequest #4 allowed, count resets to 1
CRequest #4 rejected with 429 error
DRequest #4 ignored
💡 Hint
Refer to how condition triggers rejection when count reaches the limit in execution_table.
Concept Snapshot
Rate limiting in Rails:
Use a before_action to check request count.
Store count in cache with expiration (e.g., 1 minute).
If count >= limit, reject with 429 status.
Else increment count and allow request.
Prevents too many requests in short time.
Full Transcript
Rate limiting in Rails works by counting how many requests come from a user or IP in a set time. When a request arrives, the system reads the current count from cache. If the count is less than the limit, it increases the count and lets the request continue. If the count reaches or exceeds the limit, the system rejects the request with a 429 error, telling the user to slow down. The count resets after the cache expires, usually after a minute. This helps protect the app from too many requests at once.

Practice

(1/5)
1. What is the main purpose of rate limiting in a Rails application?
easy
A. To control how often users can make requests to the server
B. To speed up database queries automatically
C. To style the user interface dynamically
D. To manage user sessions securely

Solution

  1. Step 1: Understand rate limiting concept

    Rate limiting is about controlling the number of requests a user can make in a time period.
  2. Step 2: Identify the main purpose in Rails context

    It helps protect the server from overload and abuse by limiting request frequency.
  3. Final Answer:

    To control how often users can make requests to the server -> Option A
  4. Quick Check:

    Rate limiting = controlling request frequency [OK]
Hint: Rate limiting controls request frequency to protect servers [OK]
Common Mistakes:
  • Confusing rate limiting with UI styling
  • Thinking it speeds up database queries
  • Mixing it with session management
2. Which of the following is the correct way to configure a simple rate limit using Rack::Attack in Rails?
easy
A. Rack::Attack.throttle('req/ip', limit: 5, period: 1.minute) { |req| req.ip }
B. Rack::Attack.limit('req/ip', 5, 1.minute) { |req| req.ip }
C. Rack::Attack.throttle('req/ip', 1.minute, limit: 5) { |req| req.ip }
D. Rack::Attack.block('req/ip', limit: 5, period: 1.minute) { |req| req.ip }

Solution

  1. Step 1: Recall Rack::Attack throttle syntax

    The correct method is throttle with arguments: name, limit, period, and a block returning the key.
  2. Step 2: Match syntax with options

    Rack::Attack.throttle('req/ip', limit: 5, period: 1.minute) { |req| req.ip } uses throttle with named parameters limit: 5 and period: 1.minute, and block returning req.ip, which is correct.
  3. Final Answer:

    Rack::Attack.throttle('req/ip', limit: 5, period: 1.minute) { |req| req.ip } -> Option A
  4. Quick Check:

    Correct method and parameters = Rack::Attack.throttle('req/ip', limit: 5, period: 1.minute) { |req| req.ip } [OK]
Hint: Use throttle with named limit and period parameters [OK]
Common Mistakes:
  • Using wrong method name like limit or block
  • Swapping limit and period order
  • Not passing a block returning the key
3. Given this Rack::Attack throttle configuration:
Rack::Attack.throttle('logins/ip', limit: 3, period: 20.seconds) { |req| req.ip if req.path == '/login' && req.post? }

What happens if a user makes 4 POST requests to '/login' within 20 seconds from the same IP?
medium
A. Only the 1st request is blocked
B. The 4th request is blocked due to rate limit
C. All 4 requests are allowed without restriction
D. The 4th request resets the limit counter

Solution

  1. Step 1: Understand throttle parameters

    The limit is 3 requests per 20 seconds per IP for POST requests to '/login'.
  2. Step 2: Analyze 4 POST requests from same IP

    The first 3 requests pass, the 4th exceeds the limit and is blocked.
  3. Final Answer:

    The 4th request is blocked due to rate limit -> Option B
  4. Quick Check:

    Requests > limit = block excess [OK]
Hint: Requests over limit within period get blocked [OK]
Common Mistakes:
  • Assuming all requests pass
  • Thinking limit resets on 4th request
  • Confusing which requests count
4. You wrote this code to limit API requests:
Rack::Attack.throttle('api/ip', limit: 10, period: 1.minute) do |req|
req.ip if req.path.start_with?('/api')
end

But users report no limits are applied. What is the likely problem?
medium
A. The period should be in seconds, not minutes
B. The limit value 10 is too high to trigger blocking
C. The block returns nil for non-API paths, so no key is generated for those requests
D. The method throttle is not supported in Rack::Attack

Solution

  1. Step 1: Check block return value logic

    The block returns req.ip only if path starts with '/api', else returns nil.
  2. Step 2: Understand throttle key generation

    If block returns nil, no key is generated and no limit applies for those requests.
  3. Final Answer:

    The block returns nil for non-API paths, so no key is generated for those requests -> Option C
  4. Quick Check:

    Nil key means no rate limiting applied [OK]
Hint: Block must return key for all requests to apply limits [OK]
Common Mistakes:
  • Thinking limit 10 is too high
  • Confusing period units
  • Believing throttle method is unsupported
5. You want to limit login attempts per user email and IP address separately using Rack::Attack. Which configuration correctly applies this?
hard
A. Rack::Attack.throttle('logins/email', limit: 5, period: 1.hour) { |req| req.params['email'].presence }
Rack::Attack.throttle('logins/ip', limit: 10, period: 1.hour) { |req| req.params['email'] }
B. Rack::Attack.throttle('logins/email_ip', limit: 5, period: 1.hour) { |req| req.params['email'] + req.ip }
C. Rack::Attack.throttle('logins/email', limit: 5, period: 1.hour) { |req| req.ip }
Rack::Attack.throttle('logins/ip', limit: 10, period: 1.hour) { |req| req.params['email'] }
D. Rack::Attack.throttle('logins/email', limit: 5, period: 1.hour) { |req| req.params['email'] }
Rack::Attack.throttle('logins/ip', limit: 10, period: 1.hour) { |req| req.ip }

Solution

  1. Step 1: Understand separate throttles for email and IP

    We want one throttle keyed by email, another keyed by IP address.
  2. Step 2: Match keys to correct throttle names

    Rack::Attack.throttle('logins/email', limit: 5, period: 1.hour) { |req| req.params['email'] }
    Rack::Attack.throttle('logins/ip', limit: 10, period: 1.hour) { |req| req.ip } correctly uses req.params['email'] for email throttle and req.ip for IP throttle with proper limits.
  3. Final Answer:

    Rack::Attack.throttle('logins/email', limit: 5, period: 1.hour) { |req| req.params['email'] }
    Rack::Attack.throttle('logins/ip', limit: 10, period: 1.hour) { |req| req.ip }
    -> Option D
  4. Quick Check:

    Separate keys for email and IP = Rack::Attack.throttle('logins/email', limit: 5, period: 1.hour) { |req| req.params['email'] }
    Rack::Attack.throttle('logins/ip', limit: 10, period: 1.hour) { |req| req.ip } [OK]
Hint: Use separate throttle blocks keyed by email and IP [OK]
Common Mistakes:
  • Mixing email and IP keys in wrong throttles
  • Concatenating email and IP as one key
  • Using IP where email key is needed
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard