Docs
API7 GatewayManage traffic and APIsTraffic ManagementConfigure Proxy Cache

Configure Proxy Cache

Configure disk and memory caches, verify cache keys and status, and invalidate entries safely.

Applies to:API7 Gateway 3.10.x

Roles:Platform engineer

Goal

Create independent disk-backed and in-memory cache routes on one HTTPS upstream. Verify MISS and HIT results, show that the query string participates in the default cache key, and invalidate an exact entry with PURGE.

Before you begin

This procedure requires API7 Gateway 3.10.6 or later. In that release, the Control Plane began accepting PURGE in a route's method list; the data plane already supported the method.

Set the variables used in this guide:

export API7_ADMIN_URL="https://localhost:7443"
export API7_GATEWAY_GROUP_ID="default"   # only the group the quickstart creates; otherwise copy the gateway group's UUID from Gateway Groups in the Dashboard
read -rp "Gateway group name for ADC (if used): " API7_GATEWAY_GROUP_NAME && export API7_GATEWAY_GROUP_NAME
export GATEWAY_URL="http://127.0.0.1:9080"
read -rsp "Dashboard token: " API7_ADMIN_KEY && export API7_ADMIN_KEY && echo
  • Confirm that the control plane and selected gateway group are ready:

    curl -ksS --connect-timeout 3 --max-time 10 -o /dev/null -w '%{http_code}\n' "${API7_ADMIN_URL}/api/gateway_groups" -H "X-API-KEY: ${API7_ADMIN_KEY}"
    curl -ksS --connect-timeout 3 --max-time 10 "${API7_ADMIN_URL}/api/gateway_groups/${API7_GATEWAY_GROUP_ID}/instances" -H "X-API-KEY: ${API7_ADMIN_KEY}" | grep -o '"status": *"Healthy"' | wc -l

    Expected: 200, then 1 or more.

  • Confirm that the running gateway configuration has a disk zone named disk_cache_one and a memory zone named memory_cache. A disk zone has disk_path; a memory zone does not. If either is absent, merge it into apisix.proxy_cache.zones, preserve the other gateway settings, and roll out or reload the gateway through its deployment method. See Proxy Cache Configuration (opens in Plugin Hub docs).

  • The gateway can resolve and reach httpbin.org:443. This is a public demonstration service, so its availability and behavior are outside API7 Gateway's control.

  • curl, grep, and tr are available.

  • For ADC, confirm label-selector support and reserve the working files:

    adc --version
    adc sync --help | grep -- '--label-selector'
    for file in proxy-cache-adc.yaml proxy-cache-cleanup.yaml; do test ! -e "${file}" || { echo "Stop: ${file} already exists"; exit 1; }; done

    ADC resolves --gateway-group by display name; the compatibility Admin API uses the group's UUID. Enter the display name of the group identified by API7_GATEWAY_GROUP_ID.

This guide creates one service and two routes. Check every fixed Admin API identifier before using PUT:

curl -ksS --connect-timeout 3 --max-time 10 -w '\nservice: %{http_code}\n' "${API7_ADMIN_URL}/apisix/admin/services/proxy-cache-guide-service-id?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -H "X-API-KEY: ${API7_ADMIN_KEY}"
for id in proxy-cache-disk-id proxy-cache-memory-id; do
  curl -ksS --connect-timeout 3 --max-time 10 -w "\nroute ${id}: %{http_code}\n" "${API7_ADMIN_URL}/apisix/admin/routes/${id}?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -H "X-API-KEY: ${API7_ADMIN_KEY}"
done

Expected: every resource is absent. A 200 response containing a resource means the ID is in use; stop or change every occurrence. Correct authentication, group, and request errors until absence is unambiguous because PUT replaces a resource at the same ID. The Admin API URL IDs deliberately differ from the name fields below. ADC also treats a display name and backend ID as separate fields; do not infer one from the other. For ADC, continue only if its diff shows creates for exactly the labeled resources in this guide.

Steps

Complete steps 1 and 2 for the Admin API path, or skip them and complete step 3 for ADC. Do not apply both paths.

Step 1: Create the upstream service

curl -ksS --connect-timeout 3 --max-time 15 "${API7_ADMIN_URL}/apisix/admin/services/proxy-cache-guide-service-id?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -X PUT \
  -H "X-API-KEY: ${API7_ADMIN_KEY}" -H "Content-Type: application/json" \
  -d '{
    "name":"proxy-cache-guide-service","labels":{"guide":"proxy-cache"},
    "upstream":{"name":"default","type":"roundrobin","scheme":"https","pass_host":"node","nodes":[{"host":"httpbin.org","port":443,"weight":100}]}
  }'

What changed: the labeled service points to the HTTPS demonstration upstream. No public route uses it yet.

Step 2: Create the disk and memory cache routes

curl -ksS --connect-timeout 3 --max-time 15 "${API7_ADMIN_URL}/apisix/admin/routes/proxy-cache-disk-id?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -X PUT \
  -H "X-API-KEY: ${API7_ADMIN_KEY}" -H "Content-Type: application/json" \
  -d '{
    "name":"proxy-cache-disk","labels":{"guide":"proxy-cache"},"paths":["/cache/disk"],"methods":["GET","PURGE"],"service_id":"proxy-cache-guide-service-id",
    "plugins":{"proxy-rewrite":{"uri":"/response-headers"},"proxy-cache":{"cache_strategy":"disk","cache_zone":"disk_cache_one","cache_key":["$host","$request_uri"],"cache_method":["GET"],"cache_http_status":[200],"hide_cache_headers":false}}
  }'

curl -ksS --connect-timeout 3 --max-time 15 "${API7_ADMIN_URL}/apisix/admin/routes/proxy-cache-memory-id?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -X PUT \
  -H "X-API-KEY: ${API7_ADMIN_KEY}" -H "Content-Type: application/json" \
  -d '{
    "name":"proxy-cache-memory","labels":{"guide":"proxy-cache"},"paths":["/cache/memory"],"methods":["GET","PURGE"],"service_id":"proxy-cache-guide-service-id",
    "plugins":{"proxy-rewrite":{"uri":"/response-headers"},"proxy-cache":{"cache_strategy":"memory","cache_zone":"memory_cache","cache_key":["$host","$request_uri"],"cache_method":["GET"],"cache_http_status":[200],"cache_ttl":60,"hide_cache_headers":false}}
  }'

What changed: both routes cache only successful GET responses and expose cache status. cache_key is written explicitly to show its default components: host plus the original request URI, including its query string. Disk cache lifetime comes from the upstream Cache-Control or Expires header and otherwise from the gateway's static disk TTL. Memory cache uses the plugin's cache_ttl: 60 here because cache_control remains disabled.

Disk entries remain available across a gateway process restart only when the configured disk_path and its backing volume survive that restart. Memory entries do not survive a process restart.

Step 3: Apply the complete desired state with ADC

Skip this alternative if you completed steps 1 and 2. Save the service and both routes in one file so each sync describes the complete selected state.

proxy-cache-adc.yaml
services:
  - name: proxy-cache-guide-service
    labels: {guide: proxy-cache}
    upstream:
      name: default
      scheme: https
      pass_host: node
      nodes:
        - {host: httpbin.org, port: 443, weight: 100}
    routes:
      - name: proxy-cache-disk
        labels: {guide: proxy-cache}
        uris: [/cache/disk]
        methods: [GET, PURGE]
        plugins:
          proxy-rewrite: {uri: /response-headers}
          proxy-cache:
            cache_strategy: disk
            cache_zone: disk_cache_one
            cache_key: ['$host', '$request_uri']
            cache_method: [GET]
            cache_http_status: [200]
            hide_cache_headers: false
      - name: proxy-cache-memory
        labels: {guide: proxy-cache}
        uris: [/cache/memory]
        methods: [GET, PURGE]
        plugins:
          proxy-rewrite: {uri: /response-headers}
          proxy-cache:
            cache_strategy: memory
            cache_zone: memory_cache
            cache_key: ['$host', '$request_uri']
            cache_method: [GET]
            cache_http_status: [200]
            cache_ttl: 60
            hide_cache_headers: false

Lint, validate, inspect, and then apply the reviewed diff:

adc lint -f proxy-cache-adc.yaml
adc validate -f proxy-cache-adc.yaml --backend api7ee --server "${API7_ADMIN_URL}" --token "${API7_ADMIN_KEY}" --gateway-group "${API7_GATEWAY_GROUP_NAME}" --tls-skip-verify --label-selector guide=proxy-cache
adc diff -f proxy-cache-adc.yaml --backend api7ee --server "${API7_ADMIN_URL}" --token "${API7_ADMIN_KEY}" --gateway-group "${API7_GATEWAY_GROUP_NAME}" --tls-skip-verify --label-selector guide=proxy-cache
adc sync -f proxy-cache-adc.yaml --backend api7ee --server "${API7_ADMIN_URL}" --token "${API7_ADMIN_KEY}" --gateway-group "${API7_GATEWAY_GROUP_NAME}" --tls-skip-verify --label-selector guide=proxy-cache

Expected: lint and validation succeed; the diff creates only the labeled service and its two labeled routes; sync affects only that reviewed set.

Validate

Use a unique query value, then require MISS, HIT, a separate-key MISS, a successful purge, and another MISS on the disk route:

CACHE_TEST_ID="$(date +%s)"
DISK_URL="${GATEWAY_URL}/cache/disk?Cache-Control=max-age%3D60&kind=disk&key=${CACHE_TEST_ID}"
DISK_OTHER_URL="${GATEWAY_URL}/cache/disk?Cache-Control=max-age%3D60&kind=disk&key=${CACHE_TEST_ID}-other"

DISK_FIRST=$(curl -sS --connect-timeout 3 --max-time 15 -D - -o /dev/null "${DISK_URL}" | tr -d '\r')
printf '%s\n' "${DISK_FIRST}" | grep -qi '^Apisix-Cache-Status: MISS$'
printf '%s\n' "${DISK_FIRST}" | grep -qi '^Cache-Control: max-age=60$'

DISK_SECOND=$(curl -sS --connect-timeout 3 --max-time 15 -D - -o /dev/null "${DISK_URL}" | tr -d '\r')
printf '%s\n' "${DISK_SECOND}" | grep -qi '^Apisix-Cache-Status: HIT$'

curl -sS --connect-timeout 3 --max-time 15 -D - -o /dev/null "${DISK_OTHER_URL}" | tr -d '\r' | grep -qi '^Apisix-Cache-Status: MISS$'
test "$(curl -sS --connect-timeout 3 --max-time 15 -o /dev/null -w '%{http_code}' -X PURGE "${DISK_URL}")" = 200
curl -sS --connect-timeout 3 --max-time 15 -D - -o /dev/null "${DISK_URL}" | tr -d '\r' | grep -qi '^Apisix-Cache-Status: MISS$'

Expected: every assertion succeeds. The different query value is a different key because $request_uri includes the query string.

Repeat the deterministic lifecycle for the memory route:

MEMORY_URL="${GATEWAY_URL}/cache/memory?kind=memory&key=${CACHE_TEST_ID}"
curl -sS --connect-timeout 3 --max-time 15 -D - -o /dev/null "${MEMORY_URL}" | tr -d '\r' | grep -qi '^Apisix-Cache-Status: MISS$'
curl -sS --connect-timeout 3 --max-time 15 -D - -o /dev/null "${MEMORY_URL}" | tr -d '\r' | grep -qi '^Apisix-Cache-Status: HIT$'
test "$(curl -sS --connect-timeout 3 --max-time 15 -o /dev/null -w '%{http_code}' -X PURGE "${MEMORY_URL}")" = 200
curl -sS --connect-timeout 3 --max-time 15 -D - -o /dev/null "${MEMORY_URL}" | tr -d '\r' | grep -qi '^Apisix-Cache-Status: MISS$'

PURGE derives the cache key from the same host, path, query values, and any other configured key variables as the request that populated the entry. It does not take a route display name or route ID. ADC may assign a backend ID that differs from proxy-cache-disk or proxy-cache-memory; that ID is not part of this data-plane PURGE request.

In a gateway group with multiple instances, each instance has its own cache unless the deployment provides shared backing storage. Run this deterministic sequence against one instance or a sticky endpoint; otherwise an initial request on each instance can correctly be a MISS.

Roll back / Clean up

For the Admin API path, delete routes before their service:

for id in proxy-cache-memory-id proxy-cache-disk-id; do
  curl -ksS --connect-timeout 3 --max-time 15 -X DELETE "${API7_ADMIN_URL}/apisix/admin/routes/${id}?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -H "X-API-KEY: ${API7_ADMIN_KEY}"
done
curl -ksS --connect-timeout 3 --max-time 15 -X DELETE "${API7_ADMIN_URL}/apisix/admin/services/proxy-cache-guide-service-id?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -H "X-API-KEY: ${API7_ADMIN_KEY}"

For ADC, save an empty selected state and review the deletion diff before syncing it:

proxy-cache-cleanup.yaml
services: []
adc lint -f proxy-cache-cleanup.yaml
adc diff -f proxy-cache-cleanup.yaml --backend api7ee --server "${API7_ADMIN_URL}" --token "${API7_ADMIN_KEY}" --gateway-group "${API7_GATEWAY_GROUP_NAME}" --tls-skip-verify --label-selector guide=proxy-cache
adc sync -f proxy-cache-cleanup.yaml --backend api7ee --server "${API7_ADMIN_URL}" --token "${API7_ADMIN_KEY}" --gateway-group "${API7_GATEWAY_GROUP_NAME}" --tls-skip-verify --label-selector guide=proxy-cache
rm -f proxy-cache-adc.yaml proxy-cache-cleanup.yaml

Expected: the reviewed diff and sync delete only this guide's labeled service and routes. Removing a route stops access to its cache entries; storage reclaims remaining entries according to the configured cache lifecycle.

Troubleshoot

  • The route is rejected when it includes PURGE: verify that API7 Gateway and its Control Plane are 3.10.6 or later.
  • The plugin reports that a cache zone is missing or has the wrong strategy: inspect the running apisix.proxy_cache.zones; a disk zone needs disk_path, while the memory strategy needs a zone without it. Static configuration changes require a gateway reload or rollout.
  • The second request is still MISS: use the exact same host and URL, confirm that the response status is 200, and keep the Cache-Control=max-age%3D60 query on the disk example. In a multi-instance group, target one instance or use a sticky endpoint for this check.
  • PURGE returns 404: reproduce the exact cache key values and consumer identity used to populate the entry. On API7 Gateway 3.10.6, pre-upgrade memory entries use an older storage-key layout and are not reachable by the new PURGE path; they expire at their original TTL.
  • Cache-Control is absent: hide_cache_headers: true hides upstream Cache-Control and Expires; this guide leaves it false. Apisix-Cache-Status reports cache state independently.
  • ADC proposes unrelated changes: stop and verify the gateway-group display name, guide=proxy-cache selector, labels, and complete desired-state file.