[{"data":1,"prerenderedAt":729},["ShallowReactive",2],{"navigation":3,"-guide-cache-control":57,"-guide-cache-control-surround":726},[4],{"title":5,"path":6,"stem":7,"children":8,"icon":16},"Guide","\u002Fguide","1.guide\u002F1.index",[9,12,17,22,27,32,37,42,47,52],{"title":10,"path":6,"stem":7,"icon":11},"Getting Started","lucide:rocket",{"title":13,"path":14,"stem":15,"icon":16},"API Reference","\u002Fguide\u002Fapi","1.guide\u002F10.api","lucide:book-open",{"title":18,"path":19,"stem":20,"icon":21},"Caching Functions","\u002Fguide\u002Ffunctions","1.guide\u002F2.functions","lucide:square-function",{"title":23,"path":24,"stem":25,"icon":26},"Invalidation & Expiration","\u002Fguide\u002Finvalidation","1.guide\u002F3.invalidation","lucide:trash-2",{"title":28,"path":29,"stem":30,"icon":31},"Storage","\u002Fguide\u002Fstorage","1.guide\u002F4.storage","lucide:database",{"title":33,"path":34,"stem":35,"icon":36},"Caching HTTP Handlers","\u002Fguide\u002Fhandler","1.guide\u002F5.handler","lucide:server",{"title":38,"path":39,"stem":40,"icon":41},"Query Parameters","\u002Fguide\u002Fquery-params","1.guide\u002F6.query-params","lucide:link-2",{"title":43,"path":44,"stem":45,"icon":46},"Cookies","\u002Fguide\u002Fcookies","1.guide\u002F7.cookies","lucide:cookie",{"title":48,"path":49,"stem":50,"icon":51},"Cache-Control & Eligibility","\u002Fguide\u002Fcache-control","1.guide\u002F8.cache-control","lucide:sliders-horizontal",{"title":53,"path":54,"stem":55,"icon":56},"Incremental Static Regeneration","\u002Fguide\u002Fisr","1.guide\u002F9.isr","lucide:refresh-cw",{"id":58,"title":48,"body":59,"description":156,"extension":721,"meta":722,"navigation":723,"path":49,"seo":724,"stem":50,"__hash__":725},"content\u002F1.guide\u002F8.cache-control.md",{"type":60,"value":61,"toc":712,"icon":51},"minimark",[62,74,79,87,134,137,141,150,368,391,417,425,442,458,515,521,541,548,561,627,630,673,677,708],[63,64,65,69,70,73],"p",{},[66,67,68],"code",{},"defineCachedHandler"," synthesizes a ",[66,71,72],{},"Cache-Control"," header for cacheable responses, but it always defers to what the handler sets and to a set of built-in eligibility rules. This page covers how to keep responses out of the cache, decouple server-side storage from client caching, and add your own eligibility rules.",[75,76,78],"h2",{"id":77},"what-is-never-cached","What is never cached",[63,80,81,82,86],{},"Before storing a response, ocache applies these built-in checks. A response failing ",[83,84,85],"strong",{},"any"," of them is still returned to the caller, but is never stored or served from the cache:",[88,89,90,104,116,122],"ul",{},[91,92,93,103],"li",{},[83,94,95,98,99,102],{},[66,96,97],{},"4xx"," \u002F ",[66,100,101],{},"5xx"," statuses"," — only successful responses are cached.",[91,105,106,115],{},[83,107,108,111,112],{},[66,109,110],{},"Cache-Control: no-store"," or ",[66,113,114],{},"private"," — an explicit opt-out from the handler.",[91,117,118,121],{},[83,119,120],{},"Empty body"," — nothing to replay.",[91,123,124,133],{},[83,125,126,127,98,130],{},"Missing ",[66,128,129],{},"etag",[66,131,132],{},"last-modified"," — required for conditional requests (ocache adds these itself when the handler omits them, so this mainly guards handler-set placeholder values).",[63,135,136],{},"These checks always apply and cannot be turned off.",[75,138,140],{"id":139},"private-non-cacheable-responses","Private \u002F non-cacheable responses",[63,142,143,144,111,147,149],{},"To keep a specific response out of the cache from inside the handler, set ",[66,145,146],{},"no-store",[66,148,114],{},":",[151,152,157],"pre",{"className":153,"code":154,"language":155,"meta":156,"style":156},"language-ts shiki shiki-themes github-light github-dark github-dark","const handler = defineCachedHandler(\n  async (event) => {\n    const user = await getUser(event);\n    if (!user) {\n      return new Response(\"Please log in\", { status: 401 });\n    }\n    \u002F\u002F Personalized — never cache this one\n    return new Response(renderDashboard(user), {\n      headers: {\n        \"content-type\": \"text\u002Fhtml\",\n        \"cache-control\": \"private\",\n      },\n    });\n  },\n  { maxAge: 300 },\n);\n","ts","",[66,158,159,183,205,225,239,267,273,280,298,304,319,332,338,344,350,362],{"__ignoreMap":156},[160,161,164,168,172,175,179],"span",{"class":162,"line":163},"line",1,[160,165,167],{"class":166},"so5gQ","const",[160,169,171],{"class":170},"suiK_"," handler",[160,173,174],{"class":166}," =",[160,176,178],{"class":177},"shcOC"," defineCachedHandler",[160,180,182],{"class":181},"slsVL","(\n",[160,184,186,189,192,196,199,202],{"class":162,"line":185},2,[160,187,188],{"class":166},"  async",[160,190,191],{"class":181}," (",[160,193,195],{"class":194},"sQHwn","event",[160,197,198],{"class":181},") ",[160,200,201],{"class":166},"=>",[160,203,204],{"class":181}," {\n",[160,206,208,211,214,216,219,222],{"class":162,"line":207},3,[160,209,210],{"class":166},"    const",[160,212,213],{"class":170}," user",[160,215,174],{"class":166},[160,217,218],{"class":166}," await",[160,220,221],{"class":177}," getUser",[160,223,224],{"class":181},"(event);\n",[160,226,228,231,233,236],{"class":162,"line":227},4,[160,229,230],{"class":166},"    if",[160,232,191],{"class":181},[160,234,235],{"class":166},"!",[160,237,238],{"class":181},"user) {\n",[160,240,242,245,248,251,254,258,261,264],{"class":162,"line":241},5,[160,243,244],{"class":166},"      return",[160,246,247],{"class":166}," new",[160,249,250],{"class":177}," Response",[160,252,253],{"class":181},"(",[160,255,257],{"class":256},"sfrk1","\"Please log in\"",[160,259,260],{"class":181},", { status: ",[160,262,263],{"class":170},"401",[160,265,266],{"class":181}," });\n",[160,268,270],{"class":162,"line":269},6,[160,271,272],{"class":181},"    }\n",[160,274,276],{"class":162,"line":275},7,[160,277,279],{"class":278},"sCsY4","    \u002F\u002F Personalized — never cache this one\n",[160,281,283,286,288,290,292,295],{"class":162,"line":282},8,[160,284,285],{"class":166},"    return",[160,287,247],{"class":166},[160,289,250],{"class":177},[160,291,253],{"class":181},[160,293,294],{"class":177},"renderDashboard",[160,296,297],{"class":181},"(user), {\n",[160,299,301],{"class":162,"line":300},9,[160,302,303],{"class":181},"      headers: {\n",[160,305,307,310,313,316],{"class":162,"line":306},10,[160,308,309],{"class":256},"        \"content-type\"",[160,311,312],{"class":181},": ",[160,314,315],{"class":256},"\"text\u002Fhtml\"",[160,317,318],{"class":181},",\n",[160,320,322,325,327,330],{"class":162,"line":321},11,[160,323,324],{"class":256},"        \"cache-control\"",[160,326,312],{"class":181},[160,328,329],{"class":256},"\"private\"",[160,331,318],{"class":181},[160,333,335],{"class":162,"line":334},12,[160,336,337],{"class":181},"      },\n",[160,339,341],{"class":162,"line":340},13,[160,342,343],{"class":181},"    });\n",[160,345,347],{"class":162,"line":346},14,[160,348,349],{"class":181},"  },\n",[160,351,353,356,359],{"class":162,"line":352},15,[160,354,355],{"class":181},"  { maxAge: ",[160,357,358],{"class":170},"300",[160,360,361],{"class":181}," },\n",[160,363,365],{"class":162,"line":364},16,[160,366,367],{"class":181},");\n",[63,369,370,371,374,375,377,378,98,381,98,384,387,388,390],{},"Any ",[83,372,373],{},"other"," ",[66,376,72],{}," the handler sets is preserved verbatim — ocache only synthesizes ",[66,379,380],{},"max-age",[66,382,383],{},"s-maxage",[66,385,386],{},"stale-while-revalidate"," when the handler didn't set a ",[66,389,72],{}," of its own.",[392,393,394],"important",{},[63,395,396,98,398,400,401,404,405,111,411,416],{},[66,397,146],{},[66,399,114],{}," only controls what gets ",[83,402,403],{},"stored"," — it does not partition the cache key. Concurrent requests are still coalesced by cache key, so responses that differ per user must be keyed correctly (via ",[406,407,408],"a",{"href":34},[66,409,410],{},"varies",[406,412,413],{"href":44},[66,414,415],{},"allowCookies",") or a personalized body could be shared.",[75,418,420,421,424],{"id":419},"server-only-caching-sendcachecontrol","Server-only caching (",[66,422,423],{},"sendCacheControl",")",[63,426,427,428,431,432,436,437,98,439,441],{},"Sometimes you want to cache a response ",[83,429,430],{},"in your own storage"," (to save recomputing it) while telling browsers and CDNs ",[433,434,435],"em",{},"not"," to cache it — for example a page that is expensive to build but must always be revalidated downstream. Reaching for ",[66,438,146],{},[66,440,114],{}," doesn't work here: those also disqualify the response from storage.",[63,443,444,445,448,449,451,452,454,455,457],{},"Set ",[66,446,447],{},"sendCacheControl: false"," to decouple the two. The response is still stored and served from cache (SWR, ",[66,450,129],{},", and ",[66,453,132],{}," all still apply), but no ",[66,456,72],{}," header is emitted:",[151,459,461],{"className":153,"code":460,"language":155,"meta":156,"style":156},"const handler = defineCachedHandler(myHandler, {\n  maxAge: 60,\n  swr: true,\n  sendCacheControl: false, \u002F\u002F stored & served from cache, but nothing advertised downstream\n});\n",[66,462,463,476,486,496,510],{"__ignoreMap":156},[160,464,465,467,469,471,473],{"class":162,"line":163},[160,466,167],{"class":166},[160,468,171],{"class":170},[160,470,174],{"class":166},[160,472,178],{"class":177},[160,474,475],{"class":181},"(myHandler, {\n",[160,477,478,481,484],{"class":162,"line":185},[160,479,480],{"class":181},"  maxAge: ",[160,482,483],{"class":170},"60",[160,485,318],{"class":181},[160,487,488,491,494],{"class":162,"line":207},[160,489,490],{"class":181},"  swr: ",[160,492,493],{"class":170},"true",[160,495,318],{"class":181},[160,497,498,501,504,507],{"class":162,"line":227},[160,499,500],{"class":181},"  sendCacheControl: ",[160,502,503],{"class":170},"false",[160,505,506],{"class":181},", ",[160,508,509],{"class":278},"\u002F\u002F stored & served from cache, but nothing advertised downstream\n",[160,511,512],{"class":162,"line":241},[160,513,514],{"class":181},"});\n",[63,516,517,518,520],{},"This only governs ocache's own synthesis — a ",[66,519,72],{}," the handler sets explicitly is still preserved and sent.",[522,523,524],"tip",{},[63,525,526,527,530,531,534,535,537,538,540],{},"\nIf ",[66,528,529],{},"invalidateCache","\u002F",[66,532,533],{},"expireCache"," clears your storage but callers still get stale responses, a browser or CDN in front of the handler is likely serving from its own copy, cached independently via the synthesized ",[66,536,72],{}," — a purge to server storage never reaches it. ",[66,539,447],{}," avoids this by never letting the client\u002FCDN cache the response in the first place.",[75,542,544,545,424],{"id":543},"custom-eligibility-shouldcache","Custom eligibility (",[66,546,547],{},"shouldCache",[63,549,550,551,553,554,556,557,560],{},"Use ",[66,552,547],{}," to add your own rejection rule on top of the built-in checks. It receives the serialized response and returns ",[66,555,503],{}," to skip caching (the response is still returned to the caller). For example, to keep ",[66,558,559],{},"3xx"," redirects out of the cache, which the built-in checks would otherwise allow:",[151,562,564],{"className":153,"code":563,"language":155,"meta":156,"style":156},"const handler = defineCachedHandler(myHandler, {\n  maxAge: 60,\n  shouldCache: (res) => res.status \u003C 300 || res.status >= 400,\n});\n",[66,565,566,578,586,623],{"__ignoreMap":156},[160,567,568,570,572,574,576],{"class":162,"line":163},[160,569,167],{"class":166},[160,571,171],{"class":170},[160,573,174],{"class":166},[160,575,178],{"class":177},[160,577,475],{"class":181},[160,579,580,582,584],{"class":162,"line":185},[160,581,480],{"class":181},[160,583,483],{"class":170},[160,585,318],{"class":181},[160,587,588,591,594,597,599,601,604,607,610,613,615,618,621],{"class":162,"line":207},[160,589,590],{"class":177},"  shouldCache",[160,592,593],{"class":181},": (",[160,595,596],{"class":194},"res",[160,598,198],{"class":181},[160,600,201],{"class":166},[160,602,603],{"class":181}," res.status ",[160,605,606],{"class":166},"\u003C",[160,608,609],{"class":170}," 300",[160,611,612],{"class":166}," ||",[160,614,603],{"class":181},[160,616,617],{"class":166},">=",[160,619,620],{"class":170}," 400",[160,622,318],{"class":181},[160,624,625],{"class":162,"line":227},[160,626,514],{"class":181},[63,628,629],{},"Key behaviors:",[88,631,632,644,658,664],{},[91,633,634,374,637,639,640,643],{},[83,635,636],{},"ANDed with the built-ins.",[66,638,547],{}," can only ",[433,641,642],{},"narrow"," what gets cached — it can never force-cache a response the built-in checks reject.",[91,645,646,649,650,653,654,657],{},[83,647,648],{},"May be async."," Return a ",[66,651,652],{},"boolean"," or a ",[66,655,656],{},"Promise\u003Cboolean>",".",[91,659,660,663],{},[83,661,662],{},"Runs on reads too."," It gates both storing a fresh response and serving a stored one (including the stale-while-revalidate serve decision), so keep it fast and decide only from the passed entry.",[91,665,666,669,670,657],{},[83,667,668],{},"Fails closed."," A throwing hook is treated as non-cacheable and reported via ",[66,671,672],{},"onError",[75,674,676],{"id":675},"related","Related",[88,678,679,694],{},[91,680,681,686,687,690,691,693],{},[406,682,683],{"href":34},[66,684,685],{},"headersOnly"," skips response storage entirely and only answers conditional (",[66,688,689],{},"304",") requests — reach for it when you want the ",[66,692,689],{}," shortcut but no full-body caching.",[91,695,696,699,700,703,704,707],{},[406,697,698],{"href":54},"Incremental Static Regeneration (ISR)"," builds on ",[66,701,702],{},"swr"," + ",[66,705,706],{},"maxAge"," to serve stale content while revalidating.",[709,710,711],"style",{},"html pre.shiki code .so5gQ, html code.shiki .so5gQ{--shiki-light:#D73A49;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .suiK_, html code.shiki .suiK_{--shiki-light:#005CC5;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .shcOC, html code.shiki .shcOC{--shiki-light:#6F42C1;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .slsVL, html code.shiki .slsVL{--shiki-light:#24292E;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sQHwn, html code.shiki .sQHwn{--shiki-light:#E36209;--shiki-default:#FFAB70;--shiki-dark:#FFAB70}html pre.shiki code .sfrk1, html code.shiki .sfrk1{--shiki-light:#032F62;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .sCsY4, html code.shiki .sCsY4{--shiki-light:#6A737D;--shiki-default:#6A737D;--shiki-dark:#6A737D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":156,"searchDepth":185,"depth":185,"links":713},[714,715,716,718,720],{"id":77,"depth":185,"text":78},{"id":139,"depth":185,"text":140},{"id":419,"depth":185,"text":717},"Server-only caching (sendCacheControl)",{"id":543,"depth":185,"text":719},"Custom eligibility (shouldCache)",{"id":675,"depth":185,"text":676},"md",{"icon":51},{"icon":51},{"title":48,"description":156},"i0Gjr1AueL1QTjZm1E1W6T9YziF5jNx3nZ8wQQ4mYYY",[727,728],{"title":43,"path":44,"stem":45,"description":156,"icon":46,"children":-1},{"title":53,"path":54,"stem":55,"description":156,"icon":56,"children":-1},1783601179227]