It was 2:14 in the morning and the log line was boring in the way that ruins your night: a mobile client had hit a timeout on POST /api/jobs/search, decided it could not safely retry a POST, and showed the user an empty screen. Nothing was written, nothing was charged, nobody's data was touched. The endpoint was a read. We had simply told every piece of software between the phone and our database that it might not be, and they all believed us.

routes/api.php
<?php

use App\Http\Controllers\Api\SearchProductsController;
use Illuminate\Support\Facades\Route;

Route::query('/products/search', SearchProductsController::class);

That is the mess Laravel 13.35 finally lets me clean up properly. The release adds Route::query(), contributed by Joel Butcher in pull request #61797, which registers a route for the HTTP QUERY method. The IETF published that method as RFC 10008 in June 2026, a Proposed Standard, and its whole point is a request that carries a body while staying safe and idempotent. My position is simple: if you are building a new endpoint whose filters do not fit comfortably in a URL, reach for QUERY now. Not after the ecosystem has caught up. Now.

The framework side was mostly done already, which is why this feels like the moment. Since 13.19 you could send QUERY requests with Http::query() and exercise them in tests through the query() and queryJson() helpers. What was missing was the router speaking the verb natively; you had to spell it out with Route::match(['QUERY'], ...), which works but reads like a workaround. With 13.35 the verb is part of the router's list, so Route::any() and Route::redirect() pick it up too, and declaring the endpoint is one honest line in routes/api.php, shown below.

Let me give the other side its full weight, because it is a serious argument and not a grumpy one. Plenty of infrastructure has never heard of QUERY. A load balancer config written in 2019, a WAF with an allowlist of methods, an API gateway with a dropdown that stops at PATCH: any of them can quietly turn your elegant search into a 405. Browsers on another origin will send a preflight, because QUERY is not on the CORS safelist. And the caching story is not the free lunch GET gives you, since the cache key has to include the request body, which your CDN will almost certainly not do on its own. If you put the route in the web middleware group, Laravel's CSRF check treats it like a POST, so you need a token or an except entry. That is a real list of chores.

Here is why I still land on QUERY. Every item on that list is a place where your stack was already making a decision about your search traffic without you noticing. POST /search never got cached anyway, never got retried anyway, and the preflight was already happening the moment you set a JSON content type. Switching the verb does not create those costs; it makes them visible and, for the first time, fixable, because the method itself now tells the proxy the request is a read. Finding out your gateway drops unknown verbs on a Tuesday afternoon in staging is a gift compared with learning it from a pager.

There is also a quieter win I care about more than the caching. The RFC points out that URLs are more likely to end up in logs than request bodies are. Our job search filters include salary expectations and, for some customers, a candidate's current employer. I did not love seeing those in access logs as query strings, and I liked hiding them inside a POST even less, because it made a read look like a write to every auditor who skimmed our routes. QUERY lets the payload stay private-ish and the intent stay legible.

None of this means GET is in trouble. A page number and a single keyword belong in a URL you can paste into Slack, bookmark, and let any cache on earth handle. PHP has always been good at that, and $_GET is not going anywhere. QUERY is for the endpoint that has outgrown the address bar: nested price bands, arrays of IDs, typed booleans that a query string would flatten into the string "1". Keep a fallback route for clients that cannot send the verb yet, and treat the infrastructure audit as part of the feature, not an obstacle to it.

So I am curious where you will hit the wall first. If you put a QUERY route in front of your production stack this week, which layer do you expect to reject it: the load balancer, the WAF, the CDN, or some SDK your frontend team swears is fine? And if you already tried, what did you have to change to get the body cached correctly?