-
Serve static CSS and HTML files with
charset=utf-8in the Content-Type header.Static CSS and HTML files served by
::ActionDispatch::Staticnow include; charset=utf-8in their Content-Type response header, fixing browser encoding issues with CSS files containing non-ASCII characters.Mike Dalessio
-
Add
query:andbody:kwargs to integration test request helpers.params:was ambiguous for GET requests withas: :json— unclear whether params should go in the query string or request body. This caused failures in API-only apps which excludeRack::MethodOverride.Use
query:to explicitly send params in the URL query string (any HTTP method), andbody:to send an encoded request body. They can be combined.params:retains existing behavior: GET → query string, other methods → request body.get "/search", query: { q: "rails" }, as: :json post "/search", query: { page: 1 }, body: { filters: {} }, as: :jsonFixes #57131.
Denis Savchuk
-
Add ActionDispatch::Request#safe_method? and
#unsafe_method?.safe_method?returns true for HTTP methods that are defined as safe per RFC 9110 §9.2.1: GET, HEAD, OPTIONS, and TRACE.unsafe_method?is the inverse.request.safe_method? # => true for GET, HEAD, OPTIONS, TRACE request.unsafe_method? # => true for POST, PUT, PATCH, DELETEJoseph Hale
-
Add
content_typeoption to HTTP authentication methods.request_http_basic_authentication,request_http_digest_authentication, andrequest_http_token_authenticationnow accept acontent_typeparameter to control the Content-Type of the 401 response. The default behavior is unchanged.http_basic_authenticate_with( name: "admin", password: "secret", message: '{"error":"Access denied"}', content_type: "application/json" )Iliana Hadzhiatanasova
-
Add
RAILS_HOST_APP_PATHenvironment variable to support editor links in devcontainer/Docker environments.When Rails runs inside a container, file paths in error pages are container-internal paths that don't exist on the host machine. Setting
RAILS_HOST_APP_PATHto the host's application path enables proper translation of container paths to host paths for editor links.Example in
.devcontainer/devcontainer.json:{ "containerEnv": { "EDITOR": "code", "RAILS_HOST_APP_PATH": "${localWorkspaceFolder}" } }This allows the "open in editor" feature to work correctly when developing in containers.
Victor Cobos
-
Make
event_backtraceattribute inrescue_from_handled.action_controllernotifications the full backtrace, whenconfig.action_controller.rescue_from_event_backtraceis:array.This also affects
action_controller.rescue_from_handledevents.zzak
-
Avoid loading
::ActionController::Liveearly in initializer, and introduceaction_controller_liveload hook.Adrianna Chang
-
Make CSRF header-only protection compatible with local installs using HTTP
In local installations that don't use HTTPS and where the app is accessed within a local network, requests won't be performed from a secure context. In this case, the browser won't send the
Sec-Fetch-Siteheader. This means non-GET requests will be rejected because CSRF protection will fail when using the header-only approach.With this change, we allow these requests with missing
Sec-Fetch-Siteheaders if:- They happen over HTTP
- The app is not configured to force SSL
The
Origincheck always happens in any case.Rosa Gutierrez
-
Deprecate calling
protect_from_forgerywithout specifying a strategy.When
protect_from_forgeryis called without the:withoption, it currently defaults to:null_session. This is inconsistent withconfig.action_controller.default_protect_from_forgery, which uses:exception.A new configuration option
config.action_controller.default_protect_from_forgery_withhas been added to allow applications to configure the default strategy. It currently defaults to:null_sessionfor backwards compatibility, but will change to:exceptionin a future version of Rails.Applications can opt into the new behavior now by setting:
config.action_controller.default_protect_from_forgery_with = :exceptionTo silence the deprecation warning without changing behavior, explicitly pass the strategy:
protect_from_forgery with: :null_sessionSaid Kaldybaev
-
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.