Add ActionDispatch::Request#bearer_token to extract the bearer token from the Authorization header. Bearer tokens are commonly used for API and MCP requests.
DHH
Add block support to ActionController::Parameters#merge
ActionController::Parameters#merge now accepts a block to resolve conflicts, consistent with
Hash#mergeandParameters#merge!.params1 = ActionController::Parameters.new(a: 1, b: 2) params2 = ActionController::Parameters.new(b: 3, c: 4) params1.merge(params2) { |key, old_val, new_val| old_val + new_val } # => #<ActionController::Parameters {"a"=>1, "b"=>5, "c"=>4} permitted: false>Said Kaldybaev
Yield key to ActionController::Parameters#fetch block
key = params.fetch(:missing) { |missing_key| missing_key } key # => :missing key = params.fetch("missing") { |missing_key| missing_key } key # => "missing"Sean Doyle
Add
config.action_controller.live_streaming_excluded_keysto control execution state sharing in::ActionController::Live.When using ActionController::Live, actions are executed in a separate thread that shares state from the parent thread. This new configuration allows applications to opt-out specific state keys that should not be shared.
This is useful when streaming inside a
connected_toblock, where you may want the streaming thread to use its own database connection context.# config/application.rb config.action_controller.live_streaming_excluded_keys = [:active_record_connected_to_stack]By default, all keys are shared.
Eileen M. Uchitelle
Add controller action source location to routes inspector.
The routes inspector now shows where controller actions are defined. In
rails routes --expanded, a new "Action Location" field displays the file and line number of each action method.On the routing error page, when
RAILS_EDITORorEDITORis set, a clickable ✏️ icon appears next to each Controller#Action that opens the action directly in the editor.Guillermo Iguaran
Active Support notifications for CSRF warnings.
Switches from direct logging to event-driven logging, allowing others to subscribe to and act on CSRF events:
csrf_token_fallback.action_controllercsrf_request_blocked.action_controllercsrf_javascript_blocked.action_controller
Jeremy Daer
Modern header-based CSRF protection.
Modern browsers send the
Sec-Fetch-Siteheader to indicate the relationship between request initiator and target origins. Rails now uses this header to verify same-origin requests without requiring authenticity tokens.Two verification strategies are available via
protect_from_forgery using:::header_only- UsesSec-Fetch-Siteheader only. Rejects requests without a valid header. Default for new Rails 8.2 applications.:header_or_legacy_token- UsesSec-Fetch-Siteheader when present, falls back to authenticity token verification for older browsers.
Configure trusted origins for legitimate cross-site requests (OAuth callbacks, third-party embeds) with
trusted_origins::protect_from_forgery trusted_origins: %w[ https://accounts.google.com ]InvalidAuthenticityTokenis deprecated in favor ofInvalidCrossOriginRequest.Rosa Gutierrez
Fix
action_dispatch_requestearly load hook call when building Rails app middleware.Gannon McGibbon
Emit a structured event when
action_on_open_redirectis set to:notifyin addition to the existing Active Support Notification.Adrianna Chang, Hartley McGuire
Support
text/markdownformat inDebugExceptionsmiddleware.When
text/markdownis requested via the Accept header, error responses are returned withContent-Type: text/markdowninstead of HTML. The existing text templates are reused for markdown output, allowing CLI tools and other clients to receive byte-efficient error information.Guillermo Iguaran
Support dynamic
to:andwithin:options inrate_limit.The
to:andwithin:options now accept callables (lambdas or procs) and method names (as symbols), in addition to static values. This allows for dynamic rate limiting based on user attributes or other runtime conditions.class APIController < ApplicationController rate_limit to: :max_requests, within: :time_window, by: -> { current_user.id } private def max_requests current_user.premium? ? 1000 : 100 end def time_window current_user.premium? ? 1.hour : 1.minute end endMurilo Duarte
Define ActionController::Parameters#deconstruct_keys to support pattern matching
if params in { search:, page: } Article.search(search).limit(page) else … end case (value = params[:string_or_hash_with_nested_key]) in String # do something with a String `value`… in { nested_key: } # do something with `nested_key` or `value` else # … endSean Doyle
Submit test requests using
as: :htmlwithContent-Type: x-www-form-urlencodedSean Doyle
Add
svg:renderer:class Page def to_svg body end end class PagesController < ActionController::Base def show @page = Page.find(params[:id]) respond_to do |format| format.html format.svg { render svg: @page } end end endThiago Youssef
Please check [8-1-stable]) for previous changes.