Files
RustProxy/docs/CACHE.md
T
2026-10-22 14:20:03 +02:00

10 KiB

HTTP cache — HOT, COLD and invalidation

RustProxy provides an optional shared HTTP response cache with a bounded in-memory HOT tier and optional persistent-disk COLD tier. Both the global cache and a Vhost's effective policy must permit caching. COLD storage is disabled by default, and newly created Vhosts do not automatically cache dynamic content.

Enable caching safely

  1. Open Settings → Cache, enable the global cache and choose memory limits. Enable COLD only if you want durable disk storage and have enough private disk capacity.
  2. On the appropriate Vhost, enable cache for public, repeatable responses. Restrict or disable it for login, personalized pages, uploads, APIs and responses carrying secrets.
  3. Ensure the origin supplies a valid public freshness policy, or configure a safe default TTL where supported. Cache-Control: private and no-store must prevent shared storage.
  4. Optionally enable HIT-only diagnostics. Send repeated identical requests using curl, then inspect Cache → Domains / Stored objects for MISS → HIT.
  5. Review Varnish-compatible cache policy, enabled by default, then check TTL, revalidation and invalidation after updating the origin's content or configuration.

Varnish-compatible cache policy (0.34.7)

Settings → Cache → Varnish-compatible cache policy (cache.varnish_compatible, on by default) changes request-side cache lookup, not authentication or the cache key. Disabling it restores the earlier strict policy without a service restart. Changing this policy invalidates cached representations to avoid mixing storage semantics across modes; allow time for HOT/COLD to refill. Effective settings also apply to eligible embedded Web Server resources and HOT/COLD entries. Existing JSON configs with no new field inherit true.

Condition Enabled: Varnish-style Disabled: legacy strict
Client Cache-Control: no-cache, no-store, max-age=0, min-fresh, max-stale Ignore client cache directives for lookup and freshness; a fresh object stays HIT and keeps Age and hit count Revalidate/bypass or constrain freshness as instructed by the client
Client Pragma: no-cache, only-if-cached Ignore both; do not force backend or a synthetic 504 purely because of these directives Revalidate or give 504 for a missing only-if-cached representation
Origin Cache-Control: no-cache Do not store, like Varnish's built-in VCL Store only with a validator and require revalidation per request
Origin Surrogate-Control: no-store Do not store Existing RustProxy behavior
Origin zero/nonpositive remaining TTL Do not store May retain a zero-TTL representation for revalidation; never a fresh HIT
Expired but eligible object Allow up to 10 seconds of background-refresh grace (or explicit larger stale-while-revalidate), unless must-revalidate / proxy-revalidate forbids stale delivery Only explicitly configured stale/grace conditions
Origin private, no-store, Set-Cookie, Vary: *; client Authorization Existing RustProxy restrictions always apply Same
Static-file cookie-ignore allowlist, custom bypass/force rules, TTL caps, BAN/PURGE, request collapse, compression/transform, HOT/COLD, tenant isolation Unchanged RustProxy mechanisms Same

For /ui/style.css with Cache-Control: public, max-age=600 and no origin ETag/Last-Modified, a hard reload with Cache-Control: no-cache should now remain HIT while its cached representation is fresh. The request headers are not stripped before forwarding when an origin fetch actually occurs; RustProxy only ignores them for its own lookup/freshness checks. Client If-None-Match/If-Modified-Since, range conditions, URL query, Host and Vary are preserved. Strict mode remains available for installations requiring clients to force validation.

This is a documented policy subset, not a Varnish binary/VCL clone. RustProxy deliberately keeps an explicit zero-by-default fallback TTL (no implicit Varnish 120s TTL), configured cookie exceptions, multi-tenant security, stale prohibitions for must-revalidate, and no Varnish-style hit-for-miss/hit-for-pass markers. Not every Varnish configuration behaves like the default built-in VCL. Cookies continue to be omitted from the cache key only for the already-configured static-file exception; do not enable that exception for personalized static endpoints. A no-cache origin response must not be made cacheable merely to gain HITs.

Use a three-request GET comparison with curl (replace URL with your static resource):

URL=https://example.com/ui/style.css
curl -sS --compressed -D - -o /dev/null "$URL" | grep -iE '^(HTTP/|age:|x-cache:|x-cache-hits:|x-cache-reason:)'
curl -sS --compressed -H 'Cache-Control: no-cache' -H 'Pragma: no-cache' -D - -o /dev/null "$URL" | grep -iE '^(HTTP/|age:|x-cache:|x-cache-hits:|x-cache-reason:)'
curl -sS --compressed -D - -o /dev/null "$URL" | grep -iE '^(HTTP/|age:|x-cache:|x-cache-hits:|x-cache-reason:)'

Enable diagnostic headers if needed. With compatibility on, the middle response should be HIT, and Age should not reset on a fresh object. With compatibility off, it can be MISS and reset if no origin validator is available. Explicit PURGE/BAN and stale/expired TTL remain authoritative.

Vhost editor options

In Vhosts → Cache, overrides for static extensions, bypass path prefixes and unsafe force targets each place the enable/override checkbox immediately above the associated multiline list. The fields are disabled while their individual override is off; their saved values and cache policy are unchanged. Never enable unsafe forced caching for personalized responses.

Stored object rules

  • Reuse is scoped by frontend scheme, Host, exact client URL, Vhost/content policy and eligible Vary dimensions. HTTP and HTTPS objects do not cross-share.
  • GET and suitable HEAD requests may reuse a full object; changing request methods do not silently invalidate all cached GET results.
  • Authorization, dynamic cookies, Set-Cookie, sticky-affinity cookies, Tinyauth and other private signals normally cause BYPASS. A limited static-file cookie exception exists for configured static extensions and does not override private, no-store, Set-Cookie or Vary: Cookie.
  • Origin status, response size, TTL, unsupported metadata, range/conditional semantics and request/client cache directives can prevent fill or require validation.
  • A Web Service selected as a Vhost source uses the same cache policy for eligible local files. File changes can be checked with ETag / Last-Modified; a 304 validation may refresh a stored body without downloading it again.

Freshness and diagnostics

State Interpretation
MISS No suitable fresh cached representation was found; the origin was consulted. The response may still be ineligible for storage.
HIT A matching stored object was reused.
BYPASS Cache was disabled or a policy intentionally skipped it (for example authorization, cookies or a path exclusion).
Revalidated An existing representation was checked against the origin; a 304 can retain the body.

Age is total HTTP age, not only the time spent in RustProxy. The optional X-Cache-Resident-Age reports the cache residency interval. Debug headers such as X-Cache, X-Cache-Hits and reason labels are optional and should be enabled deliberately. With varnish_compatible=true, browser hard reloads do not force revalidation while an object remains fresh. In strict mode hard reloads can force origin revalidation; compare with curl when investigating.

Example, replacing the listener and Host values with yours:

for n in 1 2 3; do
  curl -sS -D - -o /dev/null -H 'Host: app.example.com' \
    http://127.0.0.1:8080/public.css
  sleep 2
done

Look for cache reason labels including request_cookie, request_authorization, response_cache_control, route_disabled and cache_hit when diagnostic headers are enabled. A MISS alone does not prove the object was admitted to HOT/COLD.

Advanced behavior

  • Budget limits: HOT RAM capacity, object count/size, transient fill memory and optional COLD byte/object limits. Configure based on real workload and monitor disk/RSS.
  • HOT/COLD movement: entries may be demoted, recovered or expired; a periodic/explicit COLD cleanup is available. Cache storage is not a permanent archive.
  • Request collapsing: eligible concurrent MISS requests can share a bounded fill, limiting origin bursts.
  • Stale-if-error and refresh: conditional options can help on eligible backend failures, but are not an unconditional stale-serving guarantee. Web Server local-file validation has a distinct policy.
  • Compression/transformation: cached variants and validators must remain compatible with client encoding and content-modification settings.

Clear or invalidate content

Authenticated Cache page/API operations can purge selected entries, domains or larger scopes. The optional public HTTP PURGE/BAN interface is disabled by default. Enable it only by listing trusted client IPs/CIDRs under Settings → Cache → HTTP BAN / PURGE IP allowlist; operations are additionally subject to Vhost binding and normal access controls. PURGE addresses an exact URL; BAN applies the supported bounded matching rules. Never allow untrusted Internet clients to purge all tenants' objects.

In 0.34.0, changing operational Vhost settings is intended to preserve compatible HOT/COLD objects; content-affecting changes invalidate affected scopes and COLD carries an additional fingerprint against stale resurrection. This new behavior still needs release-level verification, so validate cache after configuration edits and during upgrades. Earlier persistent cache entries may need refilling following format/compatibility transitions.

Troubleshooting

  • Always BYPASS: check both cache enable switches, login/cookies, exclusion rules, methods, Tinyauth and sticky sessions.
  • MISS never becomes HIT: check response Cache-Control, status, object size, Vary, Set-Cookie, incomplete origin streams and whether the same scheme/Host/query is used.
  • Content unexpectedly old: inspect Age, TTL, validators, stale rules and previous invalidation; purge only affected content after confirming its identity.
  • Memory/disk pressure: review HOT/COLD budgets and cleanup, rather than increasing limits without measurements.

See Monitoring for cache telemetry and Security for privacy boundaries.