Mittelstand Radar: Buying signals from the German Mittelstand — secure your first report edition.Join the waitlist
An HTTP request using the QUERY verb carries a JSON body to a server, next to the .NET logo and icons for caching and retryability
Back to blog
HTTP.NETASP.NET Core

The New HTTP QUERY Method – and How to Use It Today with .NET 10

Sascha KieferDevelopment

Search over GET eventually blows up your URL; search over POST is neither safe, idempotent, nor cacheable. The new HTTP QUERY method (RFC 10008) closes exactly that gap: a request body like POST, but with the guarantees of GET. We look at the problem QUERY solves, what its semantics mean in detail, and how .NET 10 already supports it on both the client and server side.

Every REST API eventually hits the same wall: you build a search endpoint that needs to take filters, sorting, paging, and a few nested conditions – and suddenly none of it fits cleanly into a URL anymore.

You're left with exactly two bad options. Either you cram everything into the query string of a GET request and eventually run into length limits, encoding hell, and the fact that nested structures like ?filter[and][0][price][lt]=30 are only readable to machines. Or you turn the search into a POST – quietly giving up everything that makes a search a search: it's safe, idempotent, and cacheable.

A new HTTP method closes exactly this gap: QUERY. As of June 2026 it's officially a Proposed Standard, published as RFC 10008 – and the best part: with .NET 10, you can use it today.

The problem: search never really fit into HTTP

Let's look at why none of the existing methods is ideal.

GET with a query string. Semantically correct – safe, idempotent, cacheable. But URLs have practical limits: proxies, servers, and browsers often cap them at a few kilobytes. Complex filters with arrays and nested conditions can only be encoded painfully and illegibly.

GET with a body. Tempting, but a minefield. HTTP defines no semantics for a body on GET, and – quoting the specification – "various client, proxy and webserver implementations handle GET requests with a body differently." Some ignore the body, some reject the request. Unusable in practice.

POST as search. The pragmatic classic. You can send arbitrary bodies, everything works – but POST is by definition neither safe nor idempotent. For a search, that concretely means:

  • No automatic retries: A proxy or HTTP library must not simply re-send a POST after a dropped connection – it might have changed something.
  • No caching: POST responses aren't cached by default.
  • Wrong semantics: POST signals "I'm changing server-side state." For a pure search, that's simply a lie.

The solution: QUERY

QUERY combines the best of both worlds. The specification puts it plainly:

A QUERY requests that the request target process the enclosed content in a safe and idempotent manner and then respond with the result of that processing.

In other words: QUERY carries its request in the body – like POST, with arbitrary content and a Content-Type of your choice. At the same time, the method guarantees the same properties as GET:

PropertyGETQUERYPOST
Safe
Idempotent
Body with defined semantics
Response cacheableonly explicitly

A raw QUERY request looks roughly like this:

QUERY /products/search HTTP/1.1
Host: api.example.com
Content-Type: application/json
Accept: application/json

{
  "category": "books",
  "price": { "lt": 30 },
  "sort": ["-published", "title"],
  "page": { "size": 20, "cursor": "eyJpZCI6NDJ9" }
}

The server responds with 200 OK and the search result in the body. Because the method is safe and idempotent, intermediaries may freely retry the request on a failure and cache the response.

A few details that matter

  • Content-Type is mandatory. The server must reject the request if the Content-Type is missing or inconsistent with the content. The media type defines the query language – that can be JSON, but equally application/sql, application/graphql, or a custom filter format.
  • Content-Location in the response. If the server responds with a Content-Location header, it points to a resource corresponding to the result. Clients can later GET that URL – effectively materializing a search into a cacheable result resource.
  • Meaningful error codes. Missing Content-Type400. Unsupported media type → 415. Query syntactically valid but not processable → 422. Requested response format unsupported → 406.

Who's behind the standard?

QUERY comes out of the IETF HTTP Working Group (httpbis). RFC 10008 was written by Julian Reschke, James M. Snell (Cloudflare), and Mike Bishop (Akamai) – the involvement of CDN and edge providers is no accident, since cacheable search requests benefit exactly those players. During development the method went by the working title draft-ietf-httpbis-safe-method-w-body.

How widespread is it?

Honestly: the semantics are standardized, the stack isn't yet.

  • Browsers currently can't send QUERY from fetch/XHR.
  • Proxies and CDNs don't reliably cache QUERY responses at scale yet – even though authorship from Cloudflare and Akamai signals the direction.
  • Frameworks are only just starting to recognize the method.

And this is exactly where .NET comes in: .NET 10 (LTS, released November 2025) is among the first platforms with built-in QUERY support – on both the client and server side.

QUERY with .NET 10: the client side

On the client side, System.Net.Http gained a new property: HttpMethod.Query. No more magic string, just:

using System.Net.Http.Json;

var filter = new ProductFilter("books", MaxPrice: 30);

using var request = new HttpRequestMessage(HttpMethod.Query, "https://api.example.com/products/search")
{
    Content = JsonContent.Create(filter)
};

using var response = await httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();

var page = await response.Content.ReadFromJsonAsync<ProductPage>();

That's it. HttpClient sends the QUERY method along with the body over the wire – and because .NET knows the method is idempotent, the usual resilience mechanisms (e.g. automatic retries via Microsoft.Extensions.Http.Resilience) kick in just as you'd expect from GET.

QUERY with .NET 10: the server side

In ASP.NET Core 10, QUERY is available through two new building blocks in Microsoft.AspNetCore.Http.HttpMethods:

public static readonly string Query = "QUERY";
public static bool IsQuery(string method);

Kestrel already parses the method and surfaces it as "QUERY". With that, you can register an endpoint in a minimal API – via the generic MapMethods:

app.MapMethods("/products/search", new[] { HttpMethods.Query },
    async (HttpContext ctx, ICatalog catalog) =>
    {
        var filter = await ctx.Request.ReadFromJsonAsync<ProductFilter>();
        if (filter is null)
            return Results.BadRequest();

        var page = await catalog.SearchAsync(filter);
        return Results.Ok(page);
    });

One small caveat – and the workaround

The original ASP.NET Core proposal included convenience helpers like MapQuery(...) and an [HttpQuery] attribute for MVC controllers. After the API review, however, these were deliberately cut from the release – in .NET 10 there is no MapQuery and no [HttpQuery]. Only the primitives shipped.

No problem – the gap is a handful of lines wide:

public static class QueryEndpointExtensions
{
    public static RouteHandlerBuilder MapQuery(
        this IEndpointRouteBuilder endpoints,
        string pattern,
        Delegate handler)
        => endpoints.MapMethods(pattern, new[] { HttpMethods.Query }, handler);
}

With that, your endpoint reads as cleanly as you'd expect:

app.MapQuery("/products/search", async (ProductFilter filter, ICatalog catalog) =>
    Results.Ok(await catalog.SearchAsync(filter)));

And if you want to branch on the method in middleware or an existing handler:

if (HttpMethods.IsQuery(context.Request.Method))
{
    // QUERY-specific handling
}

What else you should know

ASP.NET Core 10 also recognizes QUERY in OpenAPI generation: since OpenAPI 3.1 doesn't (yet) know the method, QUERY endpoints are gracefully excluded when the document is generated, instead of throwing an error. If your API leans heavily on an OpenAPI spec, keep that in mind – the endpoints work, but they don't (yet) show up in the generated schema.

Should I use this already?

It depends on who's on the other end.

  • Server-to-server, both sides under your control (e.g. .NET ↔ .NET): Here you can put QUERY to good use today. Client and server speak the method, the semantics fit, and you get clean retries and correct semantics for free.
  • Public API with browser clients: Not yet. As long as fetch can't send QUERY, you'll need a POST fallback anyway – at which point the double effort rarely pays off.
  • Don't rely on caching by intermediaries yet. The semantics allow it, but proxies and CDNs don't implement it broadly yet. Treat it as a nice bonus for later, not as today's architectural foundation.

The pragmatic middle ground: design endpoints for QUERY internally already (the few lines of MapQuery helper cost nothing), while offering POST in parallel for public clients for now. That way you're prepared for when the rest of the stack catches up.

Conclusion

QUERY is one of those standardizations that solve a problem virtually every API team has wrestled with – the search that doesn't fit into a URL and for which POST is semantically wrong. The method gives you the body of POST with the guarantees of GET: safe, idempotent, cacheable.

With RFC 10008 the semantics are nailed down, and .NET 10 is among the first platforms to ship it – HttpMethod.Query on the client, HttpMethods.Query and HttpMethods.IsQuery on the server. Browsers and CDNs still need time, but for service-to-service communication in the .NET world you can use QUERY cleanly today. And with a tiny extension helper, it reads as if the framework had shipped MapQuery from day one.


Need support?

Are you building APIs that have to do more than simple CRUD endpoints – complex searches, clean HTTP semantics, resilient service communication? We help teams make their .NET interfaces future-proof: from choosing the right method to caching strategies to robust retry and resilience patterns. Just reach out via our contact page – we'll bring your APIs up to the current state of the art.