In Nginx reverse proxies, serving cached content while updating an expired item in the background involves two cooperative configuration directives: proxy_cache_background_update and proxy_cache_use_stale. Serializing requests that populate a brand-new cache element uses proxy_cache_lock. This guide explains their directive syntax, version availability, documented behaviors, and configuration context based strictly on official Nginx documentation.
Directive Overview and Supported Versions
The three directives belong to the Nginx HTTP proxy module (ngx_http_proxy_module) and can be configured inside the http, server, or location contexts:
- proxy_cache_background_update (introduced in Nginx 1.11.10): Syntax is
proxy_cache_background_update on | off;, defaulting tooff. It allows starting a background subrequest to update an expired cache item while returning a stale cached response to the client. - proxy_cache_use_stale: Syntax includes
proxy_cache_use_stale ... updating ... | off;, defaulting tooff. Theupdatingparameter permits using a stale cached response if the item is currently being updated, which minimizes accesses to proxied servers when updating cached data. - proxy_cache_lock: Syntax is
proxy_cache_lock on | off;(introduced in Nginx 1.1.12), defaulting tooff. When enabled, only one request at a time is allowed to populate a new cache element identified according toproxy_cache_keyby passing a request to a proxied server.
Background Updates and the proxy_cache_use_stale updating Requirement
Official Nginx documentation specifies an explicit prerequisite for background updates: when using proxy_cache_background_update on;, it is necessary to allow the usage of a stale cached response when it is being updated. This permission can be granted via configuration directives or response headers:
- Setting the
updatingparameter on theproxy_cache_use_staledirective. - Starting in Nginx 1.11.10, enabling stale responses directly in upstream response headers for a specified number of seconds after becoming stale. The
stale-while-revalidateextension of theCache-Controlheader field permits using a stale cached response if it is currently being updated. Using response headers has lower priority than setting directive parameters in configuration.
Documented Conditions for Expired Cache Items
When an item expires in the cache and a client requests it, the documented directives permit the following behavior:
proxy_cache_background_update on;allows starting a background subrequest to update an expired cache item while returning a stale cached response to the client.- Enabling stale usage during updates—such as through
proxy_cache_use_stale ... updatingorCache-Control: ... stale-while-revalidate=...—permits serving the stale cached response while the item is being updated, minimizing the number of accesses to proxied servers.
Locking Mechanics for New Cache Elements
While proxy_cache_background_update applies to updating an expired cache item, proxy_cache_lock is used to minimize accesses to proxied servers when populating a new cache element rather than establishing a general origin-wide lock.
When proxy_cache_lock on; is configured:
- Only one request at a time is allowed to populate a new cache element identified according to
proxy_cache_key. Although the default value ofproxy_cache_keyis stated as$scheme$proxy_host$request_uri, the documentation qualifies that by default the directive’s value is close to the string$scheme$proxy_host$uri$is_args$args. - Other requests for the same cache element will either wait for a response to appear in the cache or wait for the cache lock for this element to be released, up to the time set by
proxy_cache_lock_timeout. - Lock Timeout (proxy_cache_lock_timeout): Introduced in version 1.1.12 with a default of
5s. When this timeout expires, the request will be passed to the proxied server; however, the response will not be cached (before 1.7.8, the response could be cached). - Lock Age (proxy_cache_lock_age): Introduced in version 1.7.8 with a default of
5s. If the last request passed to the proxied server for populating a new cache element has not completed for the specified time, one more request may be passed to the proxied server.
Configuration Context and Directive Fragment Example
The following configuration fragment illustrates how these directives are declared together inside a location block. To function in a complete setup, caching requires an associated shared memory zone defined by proxy_cache (such as a zone configured via proxy_cache_path in the http context):
# Directive fragment inside a location block
location / {
proxy_pass http://backend_upstream;
proxy_cache my_cache;
# Background refresh for expired entries
proxy_cache_background_update on;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
# Serialization of requests populating a new cache element
proxy_cache_lock on;
proxy_cache_lock_timeout 5s;
proxy_cache_lock_age 5s;
}
In this directive fragment, requests populating a new cache element are serialized by proxy_cache_lock per cache key. When an existing cache item expires, proxy_cache_background_update and proxy_cache_use_stale ... updating allow Nginx to return a stale cached response while initiating a background subrequest to update the item.

Text version of the diagrams
- Two NGINX Cache Controls: Expired item — Serve stale, update behind; New element — Serialize cache population; Separate purpose — Different cache states
- Allowing Stale Updates: Directive — proxy_cache_use_stale updating; Header — stale-while-revalidate; Priority — Directive takes precedence
Research Method and Limitations
This technical answer was prepared strictly from the supplied public documentation excerpts for the Nginx HTTP proxy module (ngx_http_proxy_module). Material limitations include reliance on truncated documentation excerpts that omit complete context and parameters (such as the full proxy_cache_path specification), as well as the absence of accessible competing coverage or empirical performance benchmarks. Consequently, this explanation describes only documented configuration directives, default values, and stated functional behaviors without asserting unverified concurrency guarantees or benchmark figures.



