← Back to Issue

Add support for the HTTP QUERY method

From uloza+hey@proton.me · original ↗ · unsubscribe

QUERY is a safe and idempotent HTTP method that conveys the query in the request content, making it suitable for queries too large or structured for a URL query string


Add support for the HTTP QUERY method by magnogouveia · Pull Request #57973 · rails/rails · GitHub

Skip to content

Sign in

Appearance settings

Search/

Sign in

Sign up

Appearance settings

You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session. Dismiss alert

Uh oh!

There was an error while loading. Please reload this page.

rails / rails Public

Additional navigation options

Add support for the HTTP QUERY method - #57973

#57973

Merged

jeremy merged 10 commits into

rails:mainrails/rails:mainfrom

magnogouveia:add-http-query-methodmagnogouveia/rails:add-http-query-methodCopy head branch name to clipboard

Aug 14, 2026

ConversationCommits10 (10)ChecksFiles changed

Merged

##

Add support for the HTTP QUERY method#57973

jeremy merged 10 commits into

rails:mainrails/rails:mainfrom

magnogouveia:add-http-query-methodmagnogouveia/rails:add-http-query-methodCopy head branch name to clipboard

Conversation

@magnogouveia

###

@magnogouveiamagnogouveia commented Jul 3, 2026

Copy link

Copy Markdown

Contributor

Summary

Registers the HTTP QUERY method (RFC 10008, June 2026, Proposed Standard) as a known HTTP method and wires it through Action Pack:

# config/routes.rb query “search”, to: “search#index” match “filter”, to: “search#filter”, via: :query

# app code request.query? # => true request.request_method_symbol # => :query

# integration tests query “/search”, params: { filters: { status: “active” } }, as: :json

Motivation

QUERY is registered in the IANA HTTP Method Registry as safe and idempotent. It behaves like GET but carries the query in the request content, for queries too large or structured for a URL query string: complex filters, search endpoints, GraphQL, JSON-RPC.

Rails currently rejects the method before it reaches application code: request_method raises ActionController::UnknownHttpMethod (rendered as a 405), request.get? raises instead of returning false, and match ..., via: :query draws a route that can never match.

Proposed beforehand on the rails-core forum by Muhammet Dilmaç in Proposal: Support for the HTTP QUERY method (RFC 10008); this implements that proposal. Node.js core parses QUERY since 21.7.2, nginx supports it in recent mainline, and Spring has an open PR (spring-projects/spring-framework#34993).

Detail

  • ActionDispatch::Request: RFC10008 constant appended to HTTP_METHODS, plus a query? predicate (Rack::Request::Helpers does not define one).
  • Journey: QUERY added to VerbMatchers::VERBS.
  • Mapper: query verb helper alongside get/post/patch/put/delete.
  • Integration tests: query request helper. No ActionController::TestCase wrapper, mirroring options; process(:index, method: "QUERY") works there.
  • Forgery protection: QUERY requests are exempt, like GET and HEAD. HTML forms cannot issue QUERY requests, and cross-origin QUERY requests always require a CORS preflight (QUERY is not a CORS-safelisted method), so the forgery surface is smaller than GET’s. The exemption is a self-contained hunk with its own tests and is easy to drop if a token-required default is preferred.

Body parameter parsing needs no changes: parsing is keyed on Content-Type rather than the request method, so JSON and form-encoded QUERY bodies already parse into params.

No behavior changes for existing applications: QUERY requests previously raised before reaching any application code, so no framework default is needed.

Out of scope here: the Accept-Query response header, content-based cache keys for QUERY responses (RFC 10008 §2.7), resources integration, method override, and a Rack::Request#query? upstream addition.

Note on servers: Puma only allows the eight standard methods by default and answers QUERY with a 501 unless its supported_http_methods option is extended. That is a deployment concern outside Rails; this change makes Rails ready for the method without changing any server behavior.

Checklist

  • This Pull Request is related to one change. Unrelated changes should be opened in separate PRs.
  • Commit message has a detailed description of what changed and why. If this PR fixes a related issue include it in the commit message. Ex: [Fix #issue-number]
  • Tests are added or updated if you fix a bug or add a feature.
  • CHANGELOG files are updated for the changed libraries if there is a behavior change or additional feature. Minor bug fixes and documentation changes should not be included.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

👍 5 williantenfen, yaroslavrick, usutani, koic, and arg reacted with thumbs up emoji 🎉 3 grobie, n-rodriguez, and williantenfen reacted with hooray emoji 🚀 5 grobie, n-rodriguez, ashwin47, williantenfen, and eddyjaga reacted with rocket emoji

All reactions

@seanpdoyle

###

seanpdoyle commented Jul 6, 2026 •

edited

Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link

Copy Markdown

Contributor

Both rack/rack#2474 and puma/puma#3973 are related to this proposal.

While those particular PRs might not be the ones to add support for QUERY to Rack and Puma, others eventually will.

Are there additional considerations to incorporate into the framework to support a fragmented and incremental roll-out of support for architectural dependencies (or other Rack web servers that are not puma)?

🎉 1 magnogouveia reacted with hooray emoji

All reactions

  • 🎉 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@magnogouveia

###

magnogouveia commented Jul 6, 2026

Copy link

Copy Markdown

Contributor Author

Thanks for opening those, @seanpdoyle.

This PR doesn’t depend on either of them. Rails reads env["REQUEST_METHOD"] directly, so any server that puts QUERY in the Rack env works with this patch today (I verified end-to-end with Puma’s supported_http_methods opt-in). If rack/rack#2474 ships Rack::Request#query?, the predicate added here becomes redundant with the same semantics, and we can delegate to Rack once the required Rack version has it.

On incremental roll-out, each layer already degrades on its own:

  • If the server doesn’t support QUERY (Puma by default, CloudFront currently), the request is rejected before it reaches Rack (Puma responds 501). Apps that draw query routes still boot and run normally; those routes are just unreachable until the server allows the method.
  • During the transition, apps can draw match "search" => "search#index", via: [:query, :post] and have clients fall back to POST where some hop doesn’t forward QUERY yet. This works with the patch as-is, since QUERY goes through the regular verb-matching machinery.

The CHANGELOG entry documents the Puma opt-in for this reason. I can expand the routing guide with a short note on server support (Puma config, nginx mainline, CloudFront gap) and link the Rack/Puma PRs, either in this PR or as a follow-up.

Two things I’d leave as deliberate follow-ups rather than v1 scope: the Accept-Query response header (RFC 10008 §3) for capability discovery, and content-aware cache keys (§2.7), which no mainstream HTTP cache implements yet.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

nvasilevski

nvasilevski reviewed Jul 16, 2026

View reviewed changes

Comment thread actionpack/lib/action_dispatch/routing/mapper.rb Outdated

# query ‘bacon’, to: ‘food#bacon’

def query(*path_or_actions, as: DEFAULT, to: nil, controller: nil, action: nil, on: nil, defaults: nil, constraints: nil, anchor: nil, format: nil, path: nil, internal: nil, **mapping, &block)

if path_or_actions.grep(Hash).any? && (deprecated_options = path_or_actions.extract_options!)

as = assign_deprecated_option(deprecated_options, :as, :query) if deprecated_options.key?(:as)

###

@nvasilevskinvasilevski Jul 16, 2026

Copy link

Copy Markdown

Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality Hide comment

Do we have the luxury of not doing the deprecated options handling since this is a new method? Or does something enforce all methods to support this signature?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

###

@magnogouveiamagnogouveia Jul 17, 2026

Copy link

Copy Markdown

Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality Hide comment

Good call, nothing enforces the signature; the shim is per-method copy-paste from 3b4255e, there purely for backward compatibility with pre-existing hash-based routes. Since query is new in 8.2 (the release where hash support is slated for removal), there are no legacy callers to protect. Dropped the shim and updated the branch. (query "search" => "search#index" still works via **mapping, and positional hashes fall through to match’s own deprecation until the 8.2 removal.)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@magnogouveia

magnogouveia force-pushed the add-http-query-method branch from 9477c44 to dd98f8d Compare July 17, 2026 02:36

This was referenced Jul 20, 2026

Replace GET/POST with new HTTP QUERY RFC10008 w3c/lws-protocol#179

Merged

Define GET/POST fallback for Search / Type Index when QUERY is unavailable w3c/lws-protocol#205

Open

@magnogouveia

magnogouveia force-pushed the add-http-query-method branch 3 times, most recently from 35123c7 to ab5690b Compare July 23, 2026 15:43

@magnogouveia

magnogouveia force-pushed the add-http-query-method branch 2 times, most recently from beb14e9 to bd5a141 Compare August 5, 2026 16:45

@magnogouveia@jeremy

[Add support for the HTTP QUERY method](/rails/rails/pull/57973/commits/87d4dec543681470cf383dc350328d69980ee563 "Add support for the HTTP QUERY method RFC 10008 defines QUERY as a safe and idempotent HTTP method that conveys the query in the request content instead of the URL query string. It is registered in the IANA HTTP Method Registry (safe: yes, idempotent: yes) and is intended for queries too large or structured for a query string: complex filters, search APIs, GraphQL, and JSON-RPC style endpoints. This registers QUERY as a known HTTP method in ActionDispatch::Request and wires it through the framework: * Add the RFC10008 constant to ActionDispatch::Request::HTTP_METHODS, so QUERY requests no longer raise ActionController::UnknownHttpMethod. * Add ActionDispatch::Request#query?, mirroring the other verb predicates (Rack::Request::Helpers does not define one for QUERY). * Add a QUERY verb matcher to Journey (VerbMatchers::VERBS), so `via: :query` routes match without the Unknown fallback. * Add the `query` routing DSL method to ActionDispatch::Routing::Mapper::HttpHelpers: query \"search\", to: \"search#index\" match \"filter\", to: \"search#filter\", via: :query * Add the `query` request helper to integration tests: query \"/search\", params: { filters: { q: \"video\" } }, as: :json * Exempt QUERY requests from forgery protection, like GET and HEAD. HTML forms cannot issue QUERY requests, and cross-origin QUERY requests always require a CORS preflight (QUERY is not a CORS-safelisted method), so the forgery surface is strictly smaller than GET's. This exemption is kept in an isolated hunk so it is easy to change if the exemption is not wanted. Body parameter parsing needs no changes: parsing is keyed on Content-Type rather than on the request method, so JSON and form-encoded QUERY bodies already parse into params. There are no behavior changes for existing applications: QUERY requests previously raised ActionController::UnknownHttpMethod (405) before reaching any application code. Note that the application server must also accept the method. Puma currently allows only the eight standard methods by default; QUERY can be enabled with its `supported_http_methods` option. References: - RFC 10008: https://www.rfc-editor.org/rfc/rfc10008.html - IANA HTTP Method Registry: https://www.iana.org/assignments/http-methods/http-methods.xhtml - Rails core forum proposal: https://discuss.rubyonrails.org/t/proposal-support-for-the-http-query-method-rfc-10008/91255") …

[87d4dec](/rails/rails/pull/57973/commits/87d4dec543681470cf383dc350328d69980ee563)

RFC 10008 defines QUERY as a safe and idempotent HTTP method that conveys the query in the request content instead of the URL query string. It is registered in the IANA HTTP Method Registry (safe: yes, idempotent: yes) and is intended for queries too large or structured for a query string: complex filters, search APIs, GraphQL, and JSON-RPC style endpoints.

This registers QUERY as a known HTTP method in ActionDispatch::Request and wires it through the framework:

* Add the RFC10008 constant to ActionDispatch::Request::HTTP_METHODS, so QUERY requests no longer raise ActionController::UnknownHttpMethod.

* Add ActionDispatch::Request#query?, mirroring the other verb predicates (Rack::Request::Helpers does not define one for QUERY).

* Add a QUERY verb matcher to Journey (VerbMatchers::VERBS), so `via: :query` routes match without the Unknown fallback.

* Add the `query` routing DSL method to ActionDispatch::Routing::Mapper::HttpHelpers:

  query "search", to: "search#index"
  match "filter", to: "search#filter", via: :query

* Add the `query` request helper to integration tests:

  query "/search", params: { filters: { q: "video" } }, as: :json

* Exempt QUERY requests from forgery protection, like GET and HEAD. HTML forms cannot issue QUERY requests, and cross-origin QUERY requests always require a CORS preflight (QUERY is not a CORS-safelisted method), so the forgery surface is strictly smaller than GET’s. This exemption is kept in an isolated hunk so it is easy to change if the exemption is not wanted.

Body parameter parsing needs no changes: parsing is keyed on Content-Type rather than on the request method, so JSON and form-encoded QUERY bodies already parse into params.

There are no behavior changes for existing applications: QUERY requests previously raised ActionController::UnknownHttpMethod (405) before reaching any application code.

Note that the application server must also accept the method. Puma currently allows only the eight standard methods by default; QUERY can be enabled with its `supported_http_methods` option.

References:

@jeremy

jeremy force-pushed the add-http-query-method branch from bd5a141 to 87d4dec Compare August 13, 2026 18:03

jeremy added 4 commits August 13, 2026 11:04

@jeremy

[Count QUERY as a safe request method](/rails/rails/pull/57973/commits/0a607efbc8896408368b24eab137dd5a28e11283 "Count QUERY as a safe request method QUERY is safe and idempotent per RFC 10008 §2, so Request#safe_method? now includes it alongside RFC 9110 §9.2.1's GET, HEAD, OPTIONS, and TRACE.") …

[0a607ef](/rails/rails/pull/57973/commits/0a607efbc8896408368b24eab137dd5a28e11283)

QUERY is safe and idempotent per RFC 10008 §2, so Request#safe_method? now includes it alongside RFC 9110 §9.2.1’s GET, HEAD, OPTIONS, and TRACE.

@jeremy

[Complete QUERY support in the test harnesses](/rails/rails/pull/57973/commits/db15fb3bfd6b1172f99b289ac0a6f5ed4596b922 "Complete QUERY support in the test harnesses * Add \"query\" to the integration Runner delegation list so top-level query calls reset the HTML document and copy session variables, and so follow_redirect! can re-issue QUERY after a 307/308 (it dispatches via public_send on the request method). Cover 307-preserves / 303-switches-to-GET redirect behavior. * Add the query helper to ActionController::TestCase, mirroring the integration helper. TestRequest#assign_parameters already routes non-GET methods through the body-encoding branch, so params land in request_parameters with no query string. * Document that both helpers default the Content-Type to application/x-www-form-urlencoded, as RFC 10008 requires QUERY requests to declare one; pass as: :json for a JSON body. * Cover params-to-body encoding (urlencoded and as: :json) end to end.") …

[db15fb3](/rails/rails/pull/57973/commits/db15fb3bfd6b1172f99b289ac0a6f5ed4596b922)

* Add “query” to the integration Runner delegation list so top-level query calls reset the HTML document and copy session variables, and so follow_redirect! can re-issue QUERY after a 307/308 (it dispatches via public_send on the request method). Cover 307-preserves / 303-switches-to-GET redirect behavior. * Add the query helper to ActionController::TestCase, mirroring the integration helper. TestRequest#assign_parameters already routes non-GET methods through the body-encoding branch, so params land in request_parameters with no query string. * Document that both helpers default the Content-Type to application/x-www-form-urlencoded, as RFC 10008 requires QUERY requests to declare one; pass as: :json for a JSON body. * Cover params-to-body encoding (urlencoded and as: :json) end to end.

@jeremy

[Round out QUERY routing coverage](/rails/rails/pull/57973/commits/496e3d2733815f3b4e3f4f5a88037d5701cf7eab "Round out QUERY routing coverage * Mapper tests for the query helper (verb, defaults, format). * query works on resource collections/members through the existing scope machinery: query :search, on: :collection. * Pin that Journey's HEAD->GET fallback does not extend to QUERY: a via: :get route never matches a QUERY request. * Add RFC10008 to the TestHttpMethods matrix.") …

[496e3d2](/rails/rails/pull/57973/commits/496e3d2733815f3b4e3f4f5a88037d5701cf7eab)

* Mapper tests for the query helper (verb, defaults, format). * query works on resource collections/members through the existing scope machinery: query :search, on: :collection. * Pin that Journey’s HEAD->GET fallback does not extend to QUERY: a via: :get route never matches a QUERY request. * Add RFC10008 to the TestHttpMethods matrix.

@jeremy

[Apply GET-like safe-method semantics to QUERY](/rails/rails/pull/57973/commits/b37769ad0ececa342ed7735e08c0cc7e92805de2 "Apply GET-like safe-method semantics to QUERY * Cross-origin JavaScript response protection covers QUERY exactly as GET: mark_for_same_origin_verification! includes query?, so a non-XHR cross-origin QUERY returning JavaScript raises InvalidCrossOriginRequest. * SSL middleware: document that QUERY intentionally receives a 307 (not 301) when redirecting to HTTPS — 301 permits clients to rewrite the method to GET, which would drop the QUERY body (RFC 10008 redirect semantics). * Conditional requests: no code change needed — fresh_when/stale? are verb-agnostic. Pin that QUERY with a matching If-None-Match gets 304 Not Modified and a fresh QUERY gets 200 + ETag. Deliberate non-changes: ActionDispatch::Static keeps serving files for GET/HEAD only, and implicit-render behavior is untouched — QUERY is a programmatic method, not a browser navigation. Rack::ConditionalGet handles QUERY upstream on rack main (rack/rack#2474, unreleased); controller-level freshness covers it in the meantime.") …

[b37769a](/rails/rails/pull/57973/commits/b37769ad0ececa342ed7735e08c0cc7e92805de2)

* Cross-origin JavaScript response protection covers QUERY exactly as GET: mark_for_same_origin_verification! includes query?, so a non-XHR cross-origin QUERY returning JavaScript raises InvalidCrossOriginRequest. * SSL middleware: document that QUERY intentionally receives a 307 (not 301) when redirecting to HTTPS — 301 permits clients to rewrite the method to GET, which would drop the QUERY body (RFC 10008 redirect semantics). * Conditional requests: no code change needed — fresh_when/stale? are verb-agnostic. Pin that QUERY with a matching If-None-Match gets 304 Not Modified and a fresh QUERY gets 200 + ETag.

Deliberate non-changes: ActionDispatch::Static keeps serving files for GET/HEAD only, and implicit-render behavior is untouched — QUERY is a programmatic method, not a browser navigation. Rack::ConditionalGet handles QUERY upstream on rack main (rack/rack#2474, unreleased); controller-level freshness covers it in the meantime.

@github-actions github-actions Bot added activerecord actionview labels Aug 14, 2026

@jeremy jeremy mentioned this pull request Aug 14, 2026

Exclude QUERY from _method form-parameter overrides rack/rack#2502

Open

@jeremy

###

jeremy commented Aug 14, 2026

Copy link

Copy Markdown

Member

Nice work! I’ve rebased onto current main and pushed the remainder of QUERY support:

  • safe_method? coverage
  • query helpers for both test harnesses (with 307/308 follow_redirect! support)
  • collection-route coverage
  • GET-parity for conditional requests and same-origin JS protection
  • noted that via: :all now includes QUERY
  • Routing guide shows query :search, on: :collection inside resources, the form apps will use
  • Security guide has QUERY placed alongside GET in the W3C GET-vs-POST checklist section

One behavior guard: the CSRF exemption applies only to requests that natively arrived as QUERY, not via method override. Rack method-override removal: rack/rack#2502

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@jeremy jeremy added this to the 8.2.0 milestone Aug 14, 2026

jeremy added 5 commits August 13, 2026 23:37

@jeremy

[Restrict QUERY's CSRF exemption to native QUERY requests](/rails/rails/pull/57973/commits/c0556c12ed6dba3d0fd30c59b8e6dd1b9c199a57 "Restrict QUERY's CSRF exemption to native QUERY requests Rack merged QUERY into Rack::MethodOverride's override whitelist (rack/rack#2474, unreleased). Once a rack release ships it, an ordinary cross-origin form POST carrying _method=query would be dispatched as QUERY by the default middleware stack — and, without this change, inherit QUERY's CSRF exemption. That would silently void the structural rationale for the exemption: HTML forms cannot emit QUERY, and cross-origin QUERY is always preflighted. Neither is true of a form POST. Key the exemption on the method the request actually arrived with: request.method reports the pre-override method, so native QUERY remains exempt while a tunneled POST is verified like any other POST. The cross-origin JavaScript response protection uses the same predicate. This defends the invariant ahead of rack's release rather than reacting after it ships.") …

[c0556c1](/rails/rails/pull/57973/commits/c0556c12ed6dba3d0fd30c59b8e6dd1b9c199a57)

Rack merged QUERY into Rack::MethodOverride’s override whitelist (rack/rack#2474, unreleased). Once a rack release ships it, an ordinary cross-origin form POST carrying _method=query would be dispatched as QUERY by the default middleware stack — and, without this change, inherit QUERY’s CSRF exemption. That would silently void the structural rationale for the exemption: HTML forms cannot emit QUERY, and cross-origin QUERY is always preflighted. Neither is true of a form POST.

Key the exemption on the method the request actually arrived with: request.method reports the pre-override method, so native QUERY remains exempt while a tunneled POST is verified like any other POST. The cross-origin JavaScript response protection uses the same predicate.

This defends the invariant ahead of rack’s release rather than reacting after it ships.

@jeremy

[Pin QUERY's routing reach and pre-override method semantics](/rails/rails/pull/57973/commits/166af56c0ae4024d7d9a617d66dfaedbde127bb4 "Pin QUERY's routing reach and pre-override method semantics * Routes drawn via: :all (and custom constraints that don't check the request method) now receive QUERY requests, where previously such requests 405'd before routing ran. Note this in the CHANGELOG entry and pin it with a test — it's the one observable behavior change for existing applications. * Cover request.method/method_symbol reporting the pre-override method for a POST masquerading as QUERY, matching the existing masquerading tests and underpinning the native-QUERY CSRF exemption. * Guides: show query on resource collections in the routing guide, and place QUERY alongside GET in the security guide's GET-vs-POST checklist.") …

[166af56](/rails/rails/pull/57973/commits/166af56c0ae4024d7d9a617d66dfaedbde127bb4)

* Routes drawn via: :all (and custom constraints that don’t check the request method) now receive QUERY requests, where previously such requests 405’d before routing ran. Note this in the CHANGELOG entry and pin it with a test — it’s the one observable behavior change for existing applications. * Cover request.method/method_symbol reporting the pre-override method for a POST masquerading as QUERY, matching the existing masquerading tests and underpinning the native-QUERY CSRF exemption. * Guides: show query on resource collections in the routing guide, and place QUERY alongside GET in the security guide’s GET-vs-POST checklist.

@jeremy

[DatabaseSelector treats QUERY requests as reads](/rails/rails/pull/57973/commits/a40be5369b0b9ed768fc2b5c77096dcde732681c "DatabaseSelector treats QUERY requests as reads QUERY is safe and idempotent per RFC 10008, so the database selector middleware routes it to the replica like GET and HEAD. A QUERY arriving within the configured write window still reads from the primary, same as any other read.") …

[a40be53](/rails/rails/pull/57973/commits/a40be5369b0b9ed768fc2b5c77096dcde732681c)

QUERY is safe and idempotent per RFC 10008, so the database selector middleware routes it to the replica like GET and HEAD. A QUERY arriving within the configured write window still reads from the primary, same as any other read.

@jeremy

[current_page? matches QUERY requests with method: :query](/rails/rails/pull/57973/commits/9cb6c7cfe7370c3ff626e60ec3d0d362b6eb7e03 "current_page? matches QUERY requests with method: :query No code change needed — method: :query matches via request.method_symbol now that QUERY is a recognized method. The default method: :get deliberately does not match QUERY. Form helper QUERY generation (button_to/form_with method: :query via _method tunneling) is deliberately not included: a POST tunnel offers none of QUERY's intermediary-visible caching or retry semantics, and it would make QUERY endpoints reachable through an ordinary cross-origin form POST, undermining the structural CSRF protections that justify QUERY's forgery-protection exemption.") …

[9cb6c7c](/rails/rails/pull/57973/commits/9cb6c7cfe7370c3ff626e60ec3d0d362b6eb7e03)

No code change needed — method: :query matches via request.method_symbol now that QUERY is a recognized method. The default method: :get deliberately does not match QUERY.

Form helper QUERY generation (button_to/form_with method: :query via _method tunneling) is deliberately not included: a POST tunnel offers none of QUERY’s intermediary-visible caching or retry semantics, and it would make QUERY endpoints reachable through an ordinary cross-origin form POST, undermining the structural CSRF protections that justify QUERY’s forgery-protection exemption.

@jeremy

[Document HTTP QUERY across the guides](/rails/rails/pull/57973/commits/636ed8e2b3fe529b40a79d35dfdf06b851dcf90a "Document HTTP QUERY across the guides * testing: query request helper with a body-sending example, and query in the xhr: true helper list * action_controller_overview: body params framing, request predicate table (query?, safe_method?/unsafe_method?), and a 'Handling HTTP QUERY Requests' section covering routing, params, CSRF, and conditional-request behavior * caching_with_rails: conditional requests apply to QUERY via fresh_when/stale? (Rack::ConditionalGet remains GET/HEAD-only in released rack) * action_controller_advanced_topics: CSRF safe-method exemption * routing: query in the member/collection verb list") …

[636ed8e](/rails/rails/pull/57973/commits/636ed8e2b3fe529b40a79d35dfdf06b851dcf90a)

* testing: query request helper with a body-sending example, and query in the xhr: true helper list * action_controller_overview: body params framing, request predicate table (query?, safe_method?/unsafe_method?), and a ‘Handling HTTP QUERY Requests’ section covering routing, params, CSRF, and conditional-request behavior * caching_with_rails: conditional requests apply to QUERY via fresh_when/stale? (Rack::ConditionalGet remains GET/HEAD-only in released rack) * action_controller_advanced_topics: CSRF safe-method exemption * routing: query in the member/collection verb list

@jeremy

jeremy force-pushed the add-http-query-method branch from 4c03500 to 636ed8e Compare August 14, 2026 06:37

jeremy

jeremy approved these changes Aug 14, 2026

View reviewed changes

@jeremy

jeremy enabled auto-merge (squash) August 14, 2026 06:41

Hide details View details

@jeremy

jeremy merged commit e5d90d2 into rails:main Aug 14, 2026

5 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@koic koic mentioned this pull request Aug 14, 2026

Support the HTTP QUERY method in route and test cops rubocop/rubocop-rails#1653

Open

9 tasks

This was referenced Aug 14, 2026

feat(analyzer): detect HTTP QUERY routes in Rails owasp-noir/noir#2549

Closed

Support QUERY Method in Endpoints (RFC-10008) owasp-noir/noir#2232

Closed

feat(analyzer): detect HTTP QUERY routes in Rails owasp-noir/noir#2576

Merged

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

### Reviewers

@jeremyjeremy jeremy approved these changes

+1 more reviewer

@nvasilevskinvasilevski nvasilevski left review comments

Reviewers whose approvals may not affect merge requirements

Assignees

No one assigned

Labels

actionpack actionview activerecord docs

Projects

None yet

Milestone

8.2.0

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

4 participants

@magnogouveia@seanpdoyle @jeremy @nvasilevski

Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

© 2026 GitHub, Inc.

You can’t perform that action at this time.

Highlights & notes

    Notes