Configuration: HTTP cache

This page documents the cache directive for configuring Ferron’s in-memory HTTP response cache. The cache stores complete GET response representations, serves HEAD from cached GET metadata, follows standard HTTP caching semantics by default, and understands a subset of LiteSpeed Cache response headers for LSCache-aware applications.

The cache applies to final HTTP responses produced by static file serving, reverse proxying, and other response stages.

Info

cache

{
    cache {
        max_entries 2048
    }
}

example.com {
    cache {
        max_response_size 1048576
        litespeed_override_cache_control false
        vary Accept-Encoding Accept-Language
        ignore Set-Cookie
    }

    location /admin {
        cache false
    }
}

At HTTP host scope, cache can be written either as a block or as a boolean flag. Block form enables caching for that scope and configures nested directives. Boolean form is useful when you want to enable or disable inherited caching without changing any nested settings.

Global cache block

Use the global cache { ... } block to configure shared cache capacity.

Nested directiveArgumentsDescriptionDefault
max_entries<int>This directive specifies the maximum number of response entries stored in the shared in-memory HTTP cache. Setting this directive to 0 keeps the module loaded but prevents new entries from being stored.1024

Configuration example:

{
    cache {
        max_entries 4096
    }
}
Tip

Global cache { ... } blocks are only for shared cache sizing — they do not enable caching for HTTP hosts by themselves.

HTTP host cache block

Use the HTTP host cache { ... } block to enable caching and tune how responses are stored for that host or matching location.

Nested directiveArgumentsDescriptionDefault
max_response_size<int>This directive specifies the maximum response body size, in bytes, that can be buffered and stored in the cache. Responses larger than this limit are still served, but they are not stored.2097152
litespeed_override_cache_control[<bool>]This directive specifies whether X-LiteSpeed-Cache-Control overrides standard response caching headers such as Cache-Control and Expires when Ferron decides whether to store a response and what TTL to use. This mode is intentionally non-standard and is intended only for applications that expect LiteSpeed-style cache semantics.false
emit_litespeed_headers[<bool>]This directive specifies whether the X-LiteSpeed-Cache-Control response header should be emitted when serving a cached response.false
purge_method[<bool>]This directive specifies whether the PURGE HTTP method is accepted for cache invalidation. When enabled, requests with method PURGE to a given URL will remove all cached entries matching that URL. This directive requires either HTTP basic authentication or the purge_allowed_ips directive; unauthenticated requests from non-allowed IPs are rejected with a 403 Forbidden response.false
purge_allowed_ips<string> [<string> ...]This directive specifies one or more IP addresses or CIDR ranges that are allowed to send PURGE requests. When non-empty, only requests from these IPs are allowed (unless the request is already authenticated via HTTP basic authentication). This directive can be specified multiple times.none
vary<string> [<string> ...]This directive specifies additional request headers that are added to the cache key, alongside any standard Vary response headers returned by the origin. This directive can be specified multiple times.none
ignore<string> [<string> ...]This directive specifies response headers that are removed from the stored cache representation while leaving the live response unchanged. This directive can be specified multiple times.none

Configuration example:

example.com {
    cache {
        max_response_size 2097152
        litespeed_override_cache_control
        emit_litespeed_headers
        vary Accept-Encoding Accept-Language
        ignore Set-Cookie
    }
}
Important

litespeed_override_cache_control makes Ferron treat X-LiteSpeed-Cache-Control as overriding standard HTTP caching rules. It is intentionally non-compliant with RFC 9111 — enable it only when the upstream is written for LiteSpeed-style cache semantics. Request-side directives such as Cache-Control: no-cache and Pragma: no-cache still affect cache lookup behavior normally.

Boolean cache form

FormDescriptionDefault
cacheEnables caching for the current HTTP host or location scope.false
cache trueExplicitly enables caching for the current scope.false
cache falseDisables caching for the current scope, which is useful for overriding an inherited cache { ... } block.false

Behavior

Cache eligibility

  • Only GET and HEAD requests perform cache lookups.
  • HEAD requests reuse cached GET representations and return only headers.
  • Non-GET responses are not stored, but they may still trigger LSCache-compatible purge headers.
  • Responses with Vary: * are never stored.
  • Built-in error responses generated after the main HTTP pipeline are not currently stored.

PURGE method cache invalidation

When the purge_method subdirective is enabled, Ferron accepts the PURGE HTTP method for cache invalidation. A PURGE request to a specific URL removes all cached entries (both public and private) matching that URL, causing subsequent requests to fetch fresh content.

Security:

PURGE requests must be either:

  • Authenticated via HTTP basic authentication (the basic_auth directive), or
  • Originating from an IP address matching the purge_allowed_ips list.

If neither condition is met, Ferron returns a 403 Forbidden response. This ensures that cache purging is never accidentally left unsecured.

Example using trusted IP list:

example.com {
    cache {
        purge_method
        purge_allowed_ips "127.0.0.1" "10.0.0.0/8"
    }
}

Example using basic authentication:

example.com {
    cache {
        purge_method
    }
    basic_auth {
        users {
            user "$argon2id$..."
        }
    }
}

Example request:

PURGE /blog/post-123 HTTP/1.1
Host: example.com

Public and private cache behavior

  • Public responses containing Set-Cookie are not stored.
  • Private responses are partitioned by client context. Ferron currently uses the client IP address, the authenticated username when available, and detected private cookies.
  • If Ferron cannot determine a narrower private cookie set, it falls back to all request cookies for the private cache key.

LSCache-compatible response headers

When the cache module is enabled, Ferron understands the following response headers from upstream applications and origin handlers:

HeaderDescriptionNotes
X-LiteSpeed-Cache-ControlControls cache scope and TTL using LSCache-style directives such as public, private, max-age, s-maxage, no-cache, and no-store.By default, standard HTTP caching rules still take precedence. Enable litespeed_override_cache_control to prefer this header instead.
X-LiteSpeed-VaryAdds LSCache-style vary dimensions.cookie=<name> is supported. value=<name> is not supported yet and causes Ferron to skip cache storage for that response.
X-LiteSpeed-TagAssigns tags to cached responses so they can be purged later.On private responses, public: prefixes remain public tags.
X-LiteSpeed-PurgePurges cached responses by tag, URL, or wildcard.The stale marker currently falls back to an immediate hard purge.
LSC-CookieAdds cache-safe cookie replay metadata.Ferron converts this header to Set-Cookie before sending the response.
X-LiteSpeed-CacheExposes cache hit, miss, or bypass status on outgoing responses.Ferron sets this header itself (if enabled). Origin-provided values are ignored.
Note

X-LiteSpeed-Vary: value=... is not supported yet because Ferron does not currently have a request-time equivalent of LiteSpeed’s rewrite-rule vary environment values. The ignore directive affects only the stored representation — the live response sent to the client still includes those headers unless another module removes them.

Observability

Metrics

The cache module emits the following metrics:

MetricTypeAttributesDescription
ferron.cache.requestsCounterferron.cache.result, ferron.cache.scopeCache hits, misses, and bypasses
ferron.cache.entriesGaugeCurrent number of cached entries
ferron.cache.storesCounterferron.cache.scopeResponses stored in the cache
ferron.cache.evictionsCounterferron.cache.reason ("expired" or "size")Entries evicted from the cache
ferron.cache.purgesCounterferron.cache.scopeEntries purged through LSCache-compatible controls

Logs

  • DEBUG — logged when Ferron skips cache storage because X-LiteSpeed-Vary: value=... is not supported yet.
  • DEBUG — logged when Ferron skips cache storage because the response body exceeds cache.max_response_size.
  • DEBUG — logged when Ferron performs a purge through X-LiteSpeed-Purge.
  • DEBUG — logged when Ferron performs a purge through PURGE HTTP method.
  • DEBUG — logged when Ferron receives an LSCache stale purge marker and falls back to a hard purge.

Structured logs

Description (summary)LevelAttributes
Skipping cache store because response body exceeded maximum sizeDEBUG-
Skipping cache store because X-LiteSpeed-Vary is not supported yetDEBUG-
Cache purged via LSCache controlsDEBUGcache.purged.count (purged cache entries)
Cache purged via PURGE methodDEBUGcache.purged.count (purged cache entries)
LSCache stale purge marker ignoredDEBUG-

Best practices

The following best-practice checks are reported by ferron doctor for directives on this page.

  • litespeed_override_cache_control — This makes LiteSpeed cache headers override standard HTTP cache policy. Enable only for applications that require LiteSpeed-compatible semantics.
  • purge_method without access control — Cache purging enabled without purge_allowed_ips or basic_auth in the same scope allows unauthenticated cache invalidation.
  • purge_allowed_ips with wildcard — Allowing every source address for cache purging should be restricted to trusted operators or internal networks.