Practice · Reading the Source · Card 4
Why is the unauthorized user still hitting your action?
A before_action that reads like it should block non-admins. The action still runs. The bug is one missing line.
The code
A non-admin sends DELETE /posts/42. The before_action runs, but the post still gets destroyed.
class PostsController < ApplicationController
before_action :require_admin, only: [:destroy]
def destroy
@post = Post.find(params[:id])
@post.destroy
redirect_to posts_path
end
private
def require_admin
flash[:alert] = "Admins only" unless current_user.admin?
end
end The question
What's the minimal change to require_admin that actually halts the callback chain?
Take a moment. Pick the best answer. Wrong picks reveal why they're wrong, which is half the point.
✅ Answer breakdown
✗ A. Return false from the callback.
Returning false halted the chain in Rails 4 and earlier. In Rails 5+ the return value of a before_action is ignored. This is the most common stale advice for this exact bug.
✓ B. Call render or redirect_to when the user isn't admin.
Rails checks one thing between callbacks: has a response been set? render and redirect_to set it. flash[:alert] = "..." mutates the flash without setting a response, so the chain keeps going.
✗ C. Raise an exception, e.g. raise "Forbidden".
Raising stops execution, but it stops it as a 500 error. Authorization isn't a server bug; it shouldn't look like one in your error tracker. Use a proper response.
✗ D. Add halt: true to the before_action declaration.
That option doesn't exist in Rails. Halting is a runtime behavior based on whether the callback set a response, not a declaration option.
💡 The principle
The halting rule is one sentence: between callbacks, if a response has been set, halt the chain. Otherwise keep going.
Calling render or redirect_to sets a response. Mutating flash, setting instance variables, logging, or returning false does not. throw :abort also halts, but you almost never need it because render / redirect_to cover the cases where you want the user to see something specific.
This is the most common authorization-bug shape in Rails: a callback that "looks" like it blocks but only mutates state.
📚 Theory
For the full walkthrough, read Reading the Source · Card 6 — How before_action chains run.