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 -lExpected:
200, then1or more. -
Confirm that the running gateway configuration has a disk zone named
disk_cache_oneand a memory zone namedmemory_cache. A disk zone hasdisk_path; a memory zone does not. If either is absent, merge it intoapisix.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, andtrare 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; }; doneADC resolves
--gateway-groupby display name; the compatibility Admin API uses the group's UUID. Enter the display name of the group identified byAPI7_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}"
doneExpected: 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.
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: falseLint, 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-cacheExpected: 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:
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.yamlExpected: 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 needsdisk_path, while thememorystrategy 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 is200, and keep theCache-Control=max-age%3D60query 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-Controlis absent:hide_cache_headers: truehides upstreamCache-ControlandExpires; this guide leaves it false.Apisix-Cache-Statusreports cache state independently.- ADC proposes unrelated changes: stop and verify the gateway-group display name,
guide=proxy-cacheselector, labels, and complete desired-state file.