{"id":1361,"date":"2026-10-11T23:06:34","date_gmt":"2026-10-11T23:06:34","guid":{"rendered":"https:\/\/virtualserversvps.com\/blog\/?p=1361"},"modified":"2026-10-11T23:06:34","modified_gmt":"2026-10-11T23:06:34","slug":"nginx-proxy-cache-stale-while-revalidate-stale-if-error-vps","status":"publish","type":"post","link":"https:\/\/virtualserversvps.com\/blog\/nginx-proxy-cache-stale-while-revalidate-stale-if-error-vps\/","title":{"rendered":"Serving Stale Content: Nginx proxy_cache stale-while-revalidate and stale-if-error"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Most Nginx caching advice stops at <code>proxy_cache_path<\/code> and a <code>proxy_cache_valid<\/code> line. That configuration will serve stale content happily when your origin is slow, but it has no strategy for the moment the origin is genuinely <em>down<\/em>. The directives <code>stale-while-revalidate<\/code> and <code>stale-if-error<\/code> turn a cache into a resilience layer: they let the edge keep serving a slightly older object while a refresh happens in the background, or when the backend is throwing 5xx. This tutorial wires them up correctly and, just as importantly, measures whether they are working.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How the Two Extensions Differ<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Both directives extend the lifetime of a cached object past its normal expiry, but they respond to different conditions. <code>stale-while-revalidate=30<\/code> says: for 30 seconds after the object expires, serve the stale copy immediately <em>and<\/em> trigger one background request to refresh it \u2014 client latency stays flat instead of spiking on every expiry. <code>stale-if-error=86400<\/code> says: if the origin returns an error or times out, keep serving the stale copy for up to a day instead of propagating the failure. Together they convert a fragile cache into something that degrades gracefully.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Configuring the Cache Zone<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>proxy_cache_path \/var\/cache\/nginx\/data levels=1:2 keys_zone=app_cache:20m\n                 max_size=2g inactive=12h use_temp_path=off;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then in the location block that proxies to your application, apply the policy and \u2014 critically \u2014 tell Nginx when a response is cacheable at all:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>location \/ {\n    proxy_pass http:\/\/app_upstream;\n    proxy_cache app_cache;\n    proxy_cache_key \"$scheme$host$request_uri\";\n    proxy_cache_valid 200 5m;\n    proxy_cache_valid 404 1m;\n    proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504 updating;\n    add_header X-Cache-Status $upstream_cache_status always;\n    add_header Cache-Control \"public, stale-while-revalidate=30, stale-if-error=86400\";\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>updating<\/code> token in <code>proxy_cache_use_stale<\/code> is what actually implements stale-while-revalidate in Nginx: it allows one worker to refresh the object while others serve the stale copy instead of all piling onto the origin. Without it, an expiry under load creates a thundering herd.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Verifying Cache Behaviour<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>X-Cache-Status<\/code> header is your window into the cache. Watch it transition through its states under load:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>for i in $(seq 1 20); do\n  curl -s -o \/dev\/null -D - http:\/\/127.0.0.1\/ | grep -i x-cache-status\ndone\n# Expect: MISS then HIT; under origin failure: STALE<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">To test the error path, stop the application briefly and confirm the status becomes <code>STALE<\/code> rather than a 502. Tune <code>proxy_cache_background_update on<\/code> so the revalidation happens off the request path, and <code>proxy_cache_lock on<\/code> so only one request goes upstream when a key is cold. If your caching layer sits behind a CDN, remember the CDN has its own copy of these policy decisions; the edge and origin should agree on TTLs to avoid double staleness.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Common Pitfalls<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Do not apply this to authenticated or personalised responses \u2014 a stale copy of a login page is a security problem, not a feature. Set <code>proxy_no_cache<\/code> and <code>proxy_cache_bypass<\/code> for cookies and <code>Authorization<\/code> headers. And size the zone honestly: a cache that evicts objects before they expire never reaches the STALE state and silently behaves as a pass-through. Monitor the cache hit ratio and <code>max_size<\/code> usage; if evictions are frequent, increase the zone rather than the TTL. For a broader picture of how this fits with the rest of a server&#8217;s tuning, the <a href=\"https:\/\/virtualserversvps.com\/\">VPS guides<\/a> cover related performance topics.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Stale-serving is not about showing users old content \u2014 it is about choosing to show slightly old content rather than an error page. Configured deliberately and verified with the status header, it removes the single most common cause of user-visible outages on a self-managed <a href=\"https:\/\/virtualserversvps.com\/\">web VPS<\/a>.<\/p>\n\n","protected":false},"excerpt":{"rendered":"<p>Most Nginx caching advice stops at proxy_cache_path and a proxy_cache_valid line. That configuration will serve stale content happily when your origin is slow, but it has no strategy for the&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":1,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1361","post","type-post","status-publish","format-standard","hentry","category-vps-guides-tutorials"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.1 (Yoast SEO v26.1) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Serving Stale Content: Nginx proxy_cache stale-while-revalidate and stale-if-error - Virtual Servers VPS Blog<\/title>\n<meta name=\"description\" content=\"Most Nginx caching advice stops at proxy_cache_path and a proxy_cache_valid line. That configuration will serve stale content happily when your origin is\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/virtualserversvps.com\/blog\/nginx-proxy-cache-stale-while-revalidate-stale-if-error-vps\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Serving Stale Content: Nginx proxy_cache stale-while-revalidate and stale-if-error\" \/>\n<meta property=\"og:description\" content=\"Serving Stale Content: Nginx proxy_cache stale-while-revalidate and stale-if-error\" \/>\n<meta property=\"og:url\" content=\"https:\/\/virtualserversvps.com\/blog\/nginx-proxy-cache-stale-while-revalidate-stale-if-error-vps\/\" \/>\n<meta property=\"og:site_name\" content=\"Virtual Servers VPS Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-10-11T23:06:34+00:00\" \/>\n<meta name=\"author\" content=\"Virtual-Servers-Vps-Editor\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Virtual-Servers-Vps-Editor\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"3 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/nginx-proxy-cache-stale-while-revalidate-stale-if-error-vps\/\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/nginx-proxy-cache-stale-while-revalidate-stale-if-error-vps\/\",\"name\":\"Serving Stale Content: Nginx proxy_cache stale-while-revalidate and stale-if-error - Virtual Servers VPS Blog\",\"isPartOf\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\"},\"datePublished\":\"2026-10-11T23:06:34+00:00\",\"author\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\"},\"description\":\"Most Nginx caching advice stops at proxy_cache_path and a proxy_cache_valid line. That configuration will serve stale content happily when your origin is\",\"breadcrumb\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/nginx-proxy-cache-stale-while-revalidate-stale-if-error-vps\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/virtualserversvps.com\/blog\/nginx-proxy-cache-stale-while-revalidate-stale-if-error-vps\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/nginx-proxy-cache-stale-while-revalidate-stale-if-error-vps\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/virtualserversvps.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Serving Stale Content: Nginx proxy_cache stale-while-revalidate and stale-if-error\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/\",\"name\":\"Virtual Servers VPS Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/virtualserversvps.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\",\"name\":\"Virtual-Servers-Vps-Editor\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g\",\"caption\":\"Virtual-Servers-Vps-Editor\"},\"sameAs\":[\"https:\/\/virtualserversvps.com\/blog\"],\"url\":\"https:\/\/virtualserversvps.com\/blog\/author\/virtualserversvps\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Serving Stale Content: Nginx proxy_cache stale-while-revalidate and stale-if-error - Virtual Servers VPS Blog","description":"Most Nginx caching advice stops at proxy_cache_path and a proxy_cache_valid line. That configuration will serve stale content happily when your origin is","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/virtualserversvps.com\/blog\/nginx-proxy-cache-stale-while-revalidate-stale-if-error-vps\/","og_locale":"en_US","og_type":"article","og_title":"Serving Stale Content: Nginx proxy_cache stale-while-revalidate and stale-if-error","og_description":"Serving Stale Content: Nginx proxy_cache stale-while-revalidate and stale-if-error","og_url":"https:\/\/virtualserversvps.com\/blog\/nginx-proxy-cache-stale-while-revalidate-stale-if-error-vps\/","og_site_name":"Virtual Servers VPS Blog","article_published_time":"2026-10-11T23:06:34+00:00","author":"Virtual-Servers-Vps-Editor","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Virtual-Servers-Vps-Editor","Est. reading time":"3 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/virtualserversvps.com\/blog\/nginx-proxy-cache-stale-while-revalidate-stale-if-error-vps\/","url":"https:\/\/virtualserversvps.com\/blog\/nginx-proxy-cache-stale-while-revalidate-stale-if-error-vps\/","name":"Serving Stale Content: Nginx proxy_cache stale-while-revalidate and stale-if-error - Virtual Servers VPS Blog","isPartOf":{"@id":"https:\/\/virtualserversvps.com\/blog\/#website"},"datePublished":"2026-10-11T23:06:34+00:00","author":{"@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0"},"description":"Most Nginx caching advice stops at proxy_cache_path and a proxy_cache_valid line. That configuration will serve stale content happily when your origin is","breadcrumb":{"@id":"https:\/\/virtualserversvps.com\/blog\/nginx-proxy-cache-stale-while-revalidate-stale-if-error-vps\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/virtualserversvps.com\/blog\/nginx-proxy-cache-stale-while-revalidate-stale-if-error-vps\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/virtualserversvps.com\/blog\/nginx-proxy-cache-stale-while-revalidate-stale-if-error-vps\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/virtualserversvps.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Serving Stale Content: Nginx proxy_cache stale-while-revalidate and stale-if-error"}]},{"@type":"WebSite","@id":"https:\/\/virtualserversvps.com\/blog\/#website","url":"https:\/\/virtualserversvps.com\/blog\/","name":"Virtual Servers VPS Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/virtualserversvps.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0","name":"Virtual-Servers-Vps-Editor","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g","caption":"Virtual-Servers-Vps-Editor"},"sameAs":["https:\/\/virtualserversvps.com\/blog"],"url":"https:\/\/virtualserversvps.com\/blog\/author\/virtualserversvps\/"}]}},"_links":{"self":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1361","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/comments?post=1361"}],"version-history":[{"count":1,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1361\/revisions"}],"predecessor-version":[{"id":1366,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1361\/revisions\/1366"}],"wp:attachment":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/media?parent=1361"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/categories?post=1361"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/tags?post=1361"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}