Skip to content

feat: add query-gateway plugin for safe caching of read-only body queries #13807

Description

@fatihbm

Description

Background

Many search, analytics, reporting, and query APIs accept a request body but are logically read-only. Today, clients and upstreams commonly use POST for those requests. While compatible, POST alone does not communicate that the operation is safe and idempotent, and it makes result caching an application-specific and potentially unsafe decision.

RFC 10008 defines the HTTP QUERY method for safe, idempotent requests that carry request content. It also permits caching QUERY responses, with a cache key that incorporates request content and relevant metadata.

The main need is therefore not only QUERY-to-POST compatibility. APISIX should provide a safe cache boundary for explicitly read-only, body-carrying queries even when both the client and the upstream use POST today. QUERY support is an optional compatibility and migration capability on top of that.

Proposal

Add a query-gateway plugin for safe, cache-aware handling of read-only HTTP queries with request bodies.

The plugin would support:

  1. Existing client POST requests, forwarded unchanged. Caching is opt-in and available only when the route explicitly marks those POST requests as read-only.
  2. Client QUERY requests, forwarded as POST by default for compatibility with existing upstreams.
  3. Native QUERY upstreams, enabled per route or upstream when support becomes available.
  4. A local-memory cache by default, with optional Redis and Redis Cluster backends.
  5. A short, configurable local-memory fallback when a remote cache backend is unavailable.

Example configuration:

plugins:
  query-gateway:
    preserve_original_method_header: true
    original_method_header: X-Original-Method

    query:
      # Default: client QUERY -> upstream POST
      upstream_method: post

      # For a native QUERY upstream:
      # upstream_method: query

    post:
      # Existing POST requests remain POST.
      # Cache is enabled only when both options are explicit.
      cache_enabled: true
      read_only: true

    cache:
      enabled: true
      backend: local
      ttl: 30
      fallback_ttl: 5

Cache safety model

Caching must be conservative. The cache key should include the route, target URI, client method, request content and content type, relevant content-negotiation metadata, approved cookie values, and applicable identity context.

The plugin should bypass cache for requests that include:

  • Authorization or Range;
  • Cache-Control: no-store or no-cache;
  • Pragma: no-cache;
  • cookies outside an explicit allow-list;
  • a missing Content-Type;
  • request or response bodies beyond configured limits.

It should not store responses that include:

  • Set-Cookie, WWW-Authenticate, Proxy-Authenticate, or Content-Range;
  • Cache-Control: private, no-store, no-cache, max-age=0, or s-maxage=0;
  • Vary values outside a conservative allow-list.

Use cases

  • Elasticsearch and OpenSearch search endpoints;
  • ClickHouse analytical queries;
  • reporting, filtering, and dashboard APIs;
  • GraphQL or JSON-based read-only query APIs;
  • gradual migration from POST-based queries to RFC 10008 QUERY without changing the external API contract;
  • sharing query results across APISIX instances with Redis or Redis Cluster.

Proposed validation

The feature should include unit tests and a separate integration profile that builds APISIX from source and exercises a real Elasticsearch upstream. The profile should verify:

  • QUERY and explicitly read-only POST cache MISS -> HIT flows;
  • cache bypass for credential-bearing requests;
  • negative Content-Type cases;
  • that cache hits do not reach the upstream;
  • correlation of client, APISIX, and upstream logs with X-Opaque-ID.

Scope and non-goals

This proposal does not attempt to:

  • add native QUERY support to every upstream or to the Nginx/OpenResty core;
  • cache arbitrary POST traffic or write operations;
  • cache user-specific or session-bound responses by default;
  • define a global distributed-cache invalidation protocol.

Feedback is especially welcome on the cache-safety rules, the read-only POST opt-in model, and Redis/Redis Cluster fallback behavior.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    Status
    📋 Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions