CINXE.COM
Advanced caching made easy | Netlify Developers
<!DOCTYPE html><html lang="en"> <head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="generator" content="Astro v4.15.6"><!-- SEO Meta Tags --><title>Advanced caching made easy | Netlify Developers</title><meta name="description" content="Easily generate the correct cache headers using the cdn-cache-control library"><meta property="og:title" content="Advanced caching made easy | Netlify Developers"><meta property="og:description" content="Easily generate the correct cache headers using the cdn-cache-control library"><meta property="og:image" content="https://developers.netlify.com/preview-image/guides/advanced-caching-made-easy"><meta property="og:url" content="https://developers.netlify.com/guides/advanced-caching-made-easy/"><meta property="twitter:card" content="summary_large_image"><!-- Favicons --><link rel="icon" href="/favicon/favicon.ico" sizes="any"><link rel="icon" href="/favicon/icon.svg" type="image/svg+xml"><link rel="apple-touch-icon" href="/favicon/apple-touch-icon.png"><link rel="manifest" href="/site.webmanifest" crossorigin="use-credentials"><meta name="apple-mobile-web-app-title" content="Netlify"><meta name="application-name" content="Netlify"><meta name="theme-color" content="#ffffff"><link rel="preload" href="/fonts/pacaembu/PacaembuVar-latin.woff2" as="font" type="font/woff2" crossorigin=""><link rel="preload" href="/fonts/mulish/MulishVar-latin.woff2" as="font" type="font/woff2" crossorigin=""><!-- Google Tag Manager --><script type="text/javascript"> (function (w, d, s, l, i) { w[l] = w[l] || []; w[l].push({ "gtm.start": new Date().getTime(), event: "gtm.js" }); var f = d.getElementsByTagName(s)[0], j = d.createElement(s), dl = l != "dataLayer" ? "&l=" + l : ""; j.async = true; j.defer = true; j.src = "https://www.googletagmanager.com/gtm.js?id=" + i + dl; f.parentNode.insertBefore(j, f); })(window, document, "script", "dataLayer", "GTM-NMKKF2M"); </script><!-- End Google Tag Manager --><link rel="alternate" type="application/rss+xml" title="Netlify Developers" href="/feed.xml"><link rel="stylesheet" href="/_astro/_slug_.Dnpr5qbb.css"> <link rel="stylesheet" href="/_astro/index.Bj6b_FMJ.css"> <style>.prose code{background-color:var(--neutral-dark-700);border-radius:var(--radius-xs);color:#5de4c7} @media only screen and (max-width: 599px){.guides-grid-item[data-astro-cid-llvbsxhg]{margin-bottom:6rem}}.guides-grid-item[data-astro-cid-llvbsxhg] h3[data-astro-cid-llvbsxhg]{font-size:var(--step-1);margin:.6em 0 .75em;padding:0;line-height:1.6;text-wrap:balance}.guides-grid-item[data-astro-cid-llvbsxhg] time[data-astro-cid-llvbsxhg]{display:inline-block;margin:0;color:var(--color-link-inverse)}.author[data-astro-cid-llvbsxhg]{color:var(--color-link-inverse)}.tags[data-astro-cid-llvbsxhg]{display:flex;flex-wrap:wrap;gap:.5rem;margin:1.25em 0}.excerpt[data-astro-cid-llvbsxhg]{margin-top:2em} svg[data-astro-cid-4ejhtf3k]{width:.5rem}.wrapper[data-astro-cid-lbpydqlr]{display:inline-flex;align-items:center;gap:.5rem;padding:.25rem .75rem;border-radius:1rem;background-color:var(--neutral-dark-600);color:var(--neutral-light-000);font-size:.75rem;font-weight:500;line-height:1rem;letter-spacing:.05rem;text-transform:uppercase}a[data-astro-cid-lbpydqlr].wrapper{text-decoration:none}a[data-astro-cid-lbpydqlr].wrapper:hover{text-decoration:underline;text-underline-offset:auto}a[data-astro-cid-lbpydqlr].wrapper:hover svg{transform:scale(1.2)} </style><script type="module" src="/_astro/hoisted.YKvFAI-m.js"></script><style>.callout[data-astro-cid-mrmim4ef].tip{--callout-accent-color: var(--color-brand-1-hover)}.callout[data-astro-cid-mrmim4ef].warning{--callout-accent-color: var(--color-brand-create-2)}.callout[data-astro-cid-mrmim4ef]{background-color:#0003;border-radius:6px;border-left:solid 8px var(--callout-accent-color);padding:2em;margin-top:2em;margin-bottom:3em}.callout[data-astro-cid-mrmim4ef] h2{font-size:var(--step-1);margin:0 0 1em;padding:0}.callout-content[data-astro-cid-mrmim4ef] ul,.callout-content[data-astro-cid-mrmim4ef] ol{margin:1em}.callout-content[data-astro-cid-mrmim4ef] p+p{margin-top:1em} </style><style>a[data-astro-cid-ur3u2r3y].btn-dtn{display:inline-block;margin:1em auto}a[data-astro-cid-ur3u2r3y].btn-dtn img[data-astro-cid-ur3u2r3y]{margin:0;padding:0} </style><style>.pro-tip[data-astro-cid-y4iyyay5]{background-color:var(--neutral-dark-700);border-left:8px solid #5de4c7;border-radius:var(--radius-m);padding:2em;margin-top:2em;margin-bottom:3em}.pro-tip[data-astro-cid-y4iyyay5] h2[data-astro-cid-y4iyyay5]{font-size:var(--step-1);margin:0 0 1em;padding:0;display:inline-flex;gap:.625rem}.icon[data-astro-cid-y4iyyay5]{--icon-size: calc(1em * var(--line-height-heading));--icon-color: #5de4c7;flex-shrink:0}.pro-tip[data-astro-cid-y4iyyay5] code{background-color:var(--neutral-dark-800)}.pro-tip[data-astro-cid-y4iyyay5] div.expressive-code{margin:1.5em 0} </style><style>.video-component-wrapper[data-astro-cid-7qzxku2k]{position:relative;padding-bottom:56.25%;height:0;overflow:hidden}.video-component-wrapper[data-astro-cid-7qzxku2k]>[data-astro-cid-7qzxku2k]:first-child{position:absolute;top:0;left:0;width:100%;height:100%;max-width:none} </style><style>lite-youtube>iframe{all:unset!important;width:100%!important;height:100%!important;position:absolute!important;inset:0!important;border:0!important} </style><style>lite-youtube{background-color:#000;position:relative;display:block;contain:content;background-position:center center;background-size:cover;cursor:pointer;max-width:720px}lite-youtube:before{content:attr(data-title);display:block;position:absolute;top:0;background-image:linear-gradient(180deg,#000000ab,#0000008a 14%,#00000026 54%,#0000000d 72%,#0000 94%);height:99px;width:100%;font-family:YouTube Noto,Roboto,Arial,Helvetica,sans-serif;color:#eee;text-shadow:0 0 2px rgba(0,0,0,.5);font-size:18px;padding:25px 20px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;box-sizing:border-box}lite-youtube:hover:before{color:#fff}lite-youtube:after{content:"";display:block;padding-bottom:56.25%}lite-youtube>iframe{width:100%;height:100%;position:absolute;top:0;left:0;border:0}lite-youtube>.lty-playbtn{display:block;width:100%;height:100%;background:no-repeat center/68px 48px;background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 48"><path d="M66.52 7.74c-.78-2.93-2.49-5.41-5.42-6.19C55.79.13 34 0 34 0S12.21.13 6.9 1.55c-2.93.78-4.63 3.26-5.42 6.19C.06 13.05 0 24 0 24s.06 10.95 1.48 16.26c.78 2.93 2.49 5.41 5.42 6.19C12.21 47.87 34 48 34 48s21.79-.13 27.1-1.55c2.93-.78 4.64-3.26 5.42-6.19C67.94 34.95 68 24 68 24s-.06-10.95-1.48-16.26z" fill="red"/><path d="M45 24 27 14v20" fill="white"/></svg>');position:absolute;cursor:pointer;z-index:1;filter:grayscale(100%);transition:filter .1s cubic-bezier(0,0,.2,1);border:0}lite-youtube:hover>.lty-playbtn,lite-youtube .lty-playbtn:focus{filter:none}lite-youtube.lyt-activated{cursor:unset}lite-youtube.lyt-activated:before,lite-youtube.lyt-activated>.lty-playbtn{opacity:0;pointer-events:none}.lyt-visually-hidden{clip:rect(0 0 0 0);clip-path:inset(50%);height:1px;overflow:hidden;position:absolute;white-space:nowrap;width:1px} </style><script src="/_astro/ImageUploader.astro_astro_type_script_index_0_lang.B7qzO5r-.js" type="module"></script><script src="/_astro/Tabs.astro_astro_type_script_index_0_lang.ronPbwkz.js" type="module"></script><script src="/_astro/YouTube.astro_astro_type_script_index_0_lang.Dkyb9mLy.js" type="module"></script></head> <body> <!-- Google Tag Manager (noscript) --> <noscript> <iframe src="https://www.googletagmanager.com/ns.html?id=GTM-NMKKF2M" height="0" width="0" style="display:none;visibility:hidden"></iframe> </noscript> <!-- End Google Tag Manager (noscript) --> <header class="site-header" data-astro-cid-wu5dj4rx> <nav class="guides" data-astro-cid-ymhdp2rl> <div class="wrapper l-center" data-astro-cid-ymhdp2rl> <span class="badge" data-astro-cid-ymhdp2rl> <a href="https://www.netlify.com/" class="netlify-logo" data-astro-cid-ymhdp2rl> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 209" width="512" height="209" fill="none"> <title>Netlify</title> <g clip-path="url(#a)" fill="#fff"> <path d="M117.436 207.036v-52.432l1.093-1.094h10.923l1.093 1.094v52.432l-1.093 1.094h-10.923l-1.093-1.094ZM117.436 53.523V1.093L118.529 0h10.923l1.093 1.093v52.43l-1.093 1.093h-10.923l-1.093-1.093ZM69.954 169.238h-1.545l-7.722-7.726v-1.545l18.033-18.029 8.178.004 1.097 1.089v8.178l-18.041 18.029ZM69.946 38.892h-1.544l-7.723 7.726v1.545l18.033 18.029 8.178-.004 1.097-1.09v-8.177l-18.04-18.03ZM1.093 97.51h74.278l1.094 1.094v10.922l-1.094 1.094H1.093L0 109.526V98.604l1.093-1.094ZM440.999 97.51h69.911l1.094 1.094v10.922l-1.094 1.094h-74.277l-1.094-1.094 4.366-10.922 1.094-1.094ZM212.056 108.727l-1.093 1.094h-33.884l-1.093 1.093c0 2.187 2.187 8.743 10.93 8.743 3.28 0 6.556-1.093 7.65-3.28l1.093-1.093h13.117l1.093 1.093c-1.093 6.557-6.556 16.397-22.953 16.397-18.58 0-27.327-13.117-27.327-28.417s8.743-28.416 26.233-28.416c17.491 0 26.234 13.117 26.234 28.416v4.374-.004Zm-16.397-10.93c0-1.093-1.093-8.743-9.837-8.743-8.743 0-9.836 7.65-9.836 8.743l1.093 1.094h17.487l1.093-1.094ZM242.66 115.284c0 2.186 1.093 3.28 3.28 3.28h9.836l1.094 1.093v10.93l-1.094 1.094h-9.836c-9.837 0-18.58-4.374-18.58-16.397V91.237l-1.094-1.094h-7.649l-1.094-1.093V78.12l1.094-1.094h7.649l1.094-1.093v-9.837l1.093-1.093h13.117l1.093 1.094v9.836l1.094 1.093h12.023l1.094 1.094v10.93l-1.094 1.093h-12.023l-1.094 1.094v24.047h-.003ZM283.1 131.681h-13.117l-1.094-1.094V56.264l1.094-1.094H283.1l1.093 1.094v74.323l-1.093 1.094ZM312.61 68.287h-13.117l-1.094-1.093v-10.93l1.094-1.094h13.117l1.093 1.094v10.93l-1.093 1.093Zm0 63.394h-13.117l-1.094-1.094V78.124l1.094-1.094h13.117l1.093 1.094v52.463l-1.093 1.094ZM363.98 56.264v10.93l-1.094 1.093h-9.836c-2.187 0-3.281 1.093-3.281 3.28v4.374l1.094 1.093h10.93l1.093 1.094v10.93l-1.093 1.093h-10.93l-1.094 1.093v39.347l-1.093 1.093h-13.117l-1.093-1.093V91.244l-1.094-1.093h-7.649l-1.094-1.093v-10.93l1.094-1.094h7.649l1.094-1.093v-4.374c0-12.023 8.743-16.397 18.58-16.397h9.836l1.094 1.094h.004ZM404.42 132.774c-4.374 10.93-8.743 17.487-24.047 17.487h-5.467l-1.093-1.094v-10.93l1.093-1.093h5.467c5.463 0 6.556-1.094 7.65-4.374v-1.093L370.536 89.05V78.12l1.094-1.094h9.836l1.094 1.094 13.117 37.164h1.093l13.117-37.164 1.093-1.094h9.837l1.093 1.094v10.93l-17.486 43.72-.004.004ZM135.454 131.681l-1.093-1.094.007-31.67c0-5.463-2.148-9.699-8.743-9.836-3.391-.088-7.271-.008-11.416.168l-.619.634.008 40.704-1.094 1.094H99.391l-1.093-1.094V77.539l1.093-1.094 29.51-.267c14.784 0 20.767 10.158 20.767 21.623v32.786l-1.093 1.094h-13.121Z"></path> </g> <defs><clipPath id="a"><path fill="#fff" d="M0 0h512v208.126H0z"></path></clipPath></defs> </svg> </a> <a href="/" class="developers-home" data-astro-cid-ymhdp2rl>Developers</a> </span> <ul class="menu-compact" data-astro-cid-ymhdp2rl> <li data-astro-cid-ymhdp2rl> <a class="btn-icon" href="/search" aria-label="Search" data-astro-cid-ymhdp2rl><svg width="18" height="18" viewBox="0 0 22 24" data-astro-cid-ymhdp2rl data-icon="search"> <symbol id="ai:local:search"><path fill="currentcolor" d="M9.413.473a9.081 9.081 0 0 1 6.454 15.47l5.526 5.524-2.122 2.121-5.872-5.872A9.081 9.081 0 1 1 9.413.472m0 3a6.081 6.081 0 1 0 0 12.162 6.081 6.081 0 0 0 0-12.162"/></symbol><use xlink:href="#ai:local:search"></use> </svg></a> </li> <li data-astro-cid-ymhdp2rl> <button class="btn-icon menu-toggle" aria-label="Open Menu" aria-expanded="false" aria-controls="menu" data-astro-cid-ymhdp2rl> <span data-astro-cid-ymhdp2rl></span> </button> </li> </ul> <ul id="menu" class="menu" data-astro-cid-ymhdp2rl> <li data-astro-cid-ymhdp2rl><a class="feed" href="/feed/" data-astro-cid-ymhdp2rl>Activity Feed</a></li> <li data-astro-cid-ymhdp2rl><a class="guides" href="/guides/" data-astro-cid-ymhdp2rl>Guides</a></li> <li data-astro-cid-ymhdp2rl><a class="sdk" href="/sdk/" data-astro-cid-ymhdp2rl>SDK</a></li> <li data-astro-cid-ymhdp2rl><a class="cli" href="/cli/" data-astro-cid-ymhdp2rl>CLI</a></li> <li data-astro-cid-ymhdp2rl><a class="docs" href="https://docs.netlify.app" data-astro-cid-ymhdp2rl>Documentation</a></li> <li class="menu-search" data-astro-cid-ymhdp2rl> <a class="btn-icon btn-search" href="/search" aria-label="Search" data-astro-cid-ymhdp2rl> <svg width="18" height="18" viewBox="0 0 22 24" data-astro-cid-ymhdp2rl data-icon="search"> <use xlink:href="#ai:local:search"></use> </svg> </a> </li> <li class="menu-log-in" data-astro-cid-ymhdp2rl><a class="btn btn-outline" href="https://app.netlify.com/login" data-astro-cid-ymhdp2rl>Log in</a></li> <li data-astro-cid-ymhdp2rl><a class="btn" href="https://app.netlify.com/signup" data-astro-cid-ymhdp2rl>Sign up</a></li> </ul> </div> </nav> </header> <main> <div data-kapa-crawl data-pagefind-body data-astro-cid-2u677oxp> <div class="post-header l-center" data-astro-cid-2u677oxp> <time datetime="2024-06-20T00:00:00.000Z" data-astro-cid-2u677oxp>June 20, 2024</time> <h1 data-pagefind-meta="title" data-astro-cid-2u677oxp>Advanced caching made easy</h1> <p class="author" data-astro-cid-2u677oxp> by <span data-astro-cid-2u677oxp>Matt Kane</span> </p> </div> <div class="toc-wrapper" data-astro-cid-3aox33w5> <div class="content-body-wrapper l-center" data-astro-cid-6lpija4z> <p>Netlify鈥檚 global network supports a powerful range of cache features, most of which can be controlled via response headers. Now you don鈥檛 need to remember the different header names, directives and best practices to serve dynamic content that is fast and fresh.</p> <h2 id="tldr"><a class="header-anchor" href="#tldr">#</a>TL;DR</h2> <p>The <code>cdn-cache-control</code> package has a simple API and sensible defaults that makes it easy to generate the headers needed for common caching tasks. It works anywhere that lets you set response headers, including Netlify Functions and Edge Functions, as well as natively within most web frameworks.</p> <h2 id="advanced-caching-on-netlify"><a class="header-anchor" href="#advanced-caching-on-netlify">#</a>Advanced caching on Netlify</h2> <p>Netlify supports <a href="https://docs.netlify.com/platform/caching/#supported-cache-control-headers">several advanced cache directives</a> that can be controlled via response headers. This is particularly useful for server-side rendering of web content, as it allows you to manually handle the invalidation of content, ensuring it stays fast and fresh. You can either set the cached responses to expire after a certain period, or purge it manually using the API. Using <code>stale-while-revalidate</code> allows you to update content in the background without blocking requests.</p> <h2 id="manually-setting-headers"><a class="header-anchor" href="#manually-setting-headers">#</a>Manually setting headers</h2> <p>The full <a href="https://docs.netlify.com/platform/caching/">caching docs</a> show how to have full control over your cache headers. You can use standard <code>Cache-Control</code> and <code>CDN-Cache-Control</code> headers, or target the cache more specifically with the <code>Netlify-CDN-Cache-Control</code> header. You can also use the <code>Netlify-Cache-Tag</code> header to invalidate cached content. <a href="https://docs.netlify.com/platform/caching/#on-demand-invalidation">On-demand invalidation</a> allows you to purge content from the cache individually or in bulk. Because this is based on standard HTTP headers, it works with any web framework or function. You can see detailed guides for <a href="https://developers.netlify.com/guides/how-to-do-isr-and-advanced-caching-with-remix/">Remix</a> and <a href="https://developers.netlify.com/guides/how-to-do-advanced-caching-and-isr-with-astro/">Astro</a> in the Netlify developer hub.</p> <h2 id="using-the-cdn-cache-control-package"><a class="header-anchor" href="#using-the-cdn-cache-control-package">#</a>Using the <code>cdn-cache-control</code> package</h2> <p>While manually setting headers gives you powerful, fine-grained control over the cache it can be hard to remember the correct headers to set, and the best practices for different types of content. I created the <a href="https://github.com/ascorbic/cdn-cache-control">cdn-cache-control</a> package to make it easy to generate the correct headers for common use cases. It extends the Fetch standard <a href="https://developer.mozilla.org/en-US/docs/Web/API/Headers"><code>Headers</code> class</a>, adding a simple, chainable API with sensible defaults for common use cases. It works by setting the <code>Cache-Control</code> and <code>Netlify-CDN-Cache-Control</code> headers to the appropriate values. It also has helpers for setting the <code>Netlify-Cache-Tag</code> header, which is used to invalidate cached content.</p> <h3 id="installing-the-package"><a class="header-anchor" href="#installing-the-package">#</a>Installing the package</h3> <p>The package can be installed from npm:</p> <div class="expressive-code"><link rel="stylesheet" href="/_astro/ec.eyjlz.css"/><script type="module" src="/_astro/ec.8zarh.js"></script><figure class="frame is-terminal"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="bash"><code><div class="ec-line"><div class="code"><span style="--0:#91B4D5">npm</span><span style="--0:#A6ACCD"> </span><span style="--0:#ADD7FF">install</span><span style="--0:#A6ACCD"> </span><span style="--0:#ADD7FF">cdn-cache-control</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="npm install cdn-cache-control"><div></div></button></div></figure></div> <h3 id="using-the-package"><a class="header-anchor" href="#using-the-package">#</a>Using the package</h3> <p>The module exports a single class, <code>CacheHeaders</code>, which is a subclass of the fetch <a href="https://developer.mozilla.org/en-US/docs/Web/API/Headers"><code>Headers</code></a> class. By default it sets the <code>Cache-Control</code> and <code>Netlify-CDN-Cache-Control</code> headers to sensible values for content that should be cached by the CDN and revalidated by the browser. It also provides a chainable API for setting cache headers.</p> <p>Like a regular <code>Headers</code> object it can optionally be created with a value to pre-populate the header values. This can be an existing <code>Headers</code> object, a plain object or array with existing header values. In that case it will default to using existing <code>s-maxage</code> directives if present.</p> <p>This example shows simple usage of the package:</p> <div class="expressive-code"><figure class="frame"><figcaption class="header"></figcaption><pre data-language="typescript"><code><div class="ec-line"><div class="code"><span style="--0:#5DE4C7">import </span><span style="--0:#A6ACCD">{</span><span style="--0:#5DE4C7"> </span><span style="--0:#ADD7FF">CacheHeaders</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">}</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">from</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">"</span><span style="--0:#5DE4C7">cdn-cache-control</span><span style="--0:#A6ACCD">"</span><span style="--0:#A6ACCD">;</span></div></div><div class="ec-line"><div class="code"><span style="--0:#5DE4C7">import </span><span style="--0:#A6ACCD">type</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">{</span><span style="--0:#5DE4C7"> </span><span style="--0:#ADD7FF">Config</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">}</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">from</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">"</span><span style="--0:#5DE4C7">@netlify/functions</span><span style="--0:#A6ACCD">"</span><span style="--0:#A6ACCD">;</span></div></div><div class="ec-line"><div class="code"> </div></div><div class="ec-line"><div class="code"><span style="--0:#5DE4C7">export default async </span><span style="--0:#91B4D5">function</span><span style="--0:#5DE4C7"> </span><span style="--0:#ADD7FF">handler</span><span style="--0:#A6ACCD">(</span><span style="--0:#E4F0FB">request</span><span style="--0:#91B4D5">:</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCDD2">Request</span><span style="--0:#A6ACCD">)</span><span style="--0:#91B4D5">:</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCDD2">Promise</span><span style="--0:#A6ACCD"><</span><span style="--0:#A6ACCDD2">Response</span><span style="--0:#A6ACCD">></span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#91B4D5">const</span><span style="--0:#5DE4C7"> </span><span style="--0:#E4F0FB">headers</span><span style="--0:#5DE4C7"> </span><span style="--0:#91B4D5">=</span><span style="--0:#5DE4C7"> new </span><span style="--0:#E4F0FBD0">CacheHeaders</span><span style="--0:#A6ACCD">();</span></div></div><div class="ec-line"><div class="code"> </div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#5DE4C7C0">return</span><span style="--0:#5DE4C7"> new </span><span style="--0:#E4F0FBD0">Response</span><span style="--0:#A6ACCD">(</span><span style="--0:#A6ACCD">"</span><span style="--0:#5DE4C7">Hello, world!</span><span style="--0:#A6ACCD">"</span><span style="--0:#A6ACCD">,</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">{</span><span style="--0:#E4F0FB"> headers</span><span style="--0:#ADD7FF"> </span><span style="--0:#A6ACCD">});</span></div></div><div class="ec-line"><div class="code"><span style="--0:#A6ACCD">}</span></div></div><div class="ec-line"><div class="code"> </div></div><div class="ec-line"><div class="code"><span style="--0:#5DE4C7">export</span><span style="--0:#A6ACCD"> </span><span style="--0:#91B4D5">const</span><span style="--0:#A6ACCD"> </span><span style="--0:#E4F0FB">config</span><span style="--0:#91B4D5">:</span><span style="--0:#A6ACCD"> </span><span style="--0:#A6ACCDD2">Config</span><span style="--0:#A6ACCD"> {</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#91B4D5">path</span><span style="--0:#A6ACCD">: </span><span style="--0:#A6ACCD">"</span><span style="--0:#5DE4C7">/hello</span><span style="--0:#A6ACCD">"</span></div></div><div class="ec-line"><div class="code"><span style="--0:#A6ACCD">}</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="import { CacheHeaders } from "cdn-cache-control";import type { Config } from "@netlify/functions";export default async function handler(request: Request): Promise<Response> { const headers = new CacheHeaders(); return new Response("Hello, world!", { headers });}export const config: Config { path: "/hello"}"><div></div></button></div></figure></div> <p>This sets the <code>Netlify-CDN-Cache-Control</code> header to <code>public,s-maxage=31536000,durable,must-revalidate</code>, which tells the CDN to cache the content for a year and use Netlify鈥檚 <a href="https://docs.netlify.com/platform/caching/#durable-directive">Durable Cache</a> for improved performance. It sets <code>Cache-Control</code> to <code>public,max-age=0,must-revalidate</code>, which tells the browser to always check with the CDN for a fresh version. You should combine this with an <code>ETag</code> or <code>Last-Modified</code> header to allow the CDN to serve a <code>304 Not Modified</code> response when the content hasn鈥檛 changed.</p> <h3 id="tagging-content-for-invalidation"><a class="header-anchor" href="#tagging-content-for-invalidation">#</a>Tagging content for invalidation</h3> <p>If you need to purge cached content you can <a href="https://docs.netlify.com/platform/caching/#purge-by-cache-tag">add cache tags</a> to your responses. This is particularly useful if your responses are based on an API that may change. You can use a webhook to purge the cache when content changes. For a complete example see the <a href="https://developers.netlify.com/guides/how-to-do-advanced-caching-and-isr-with-astro/">Astro guide</a>, but read on for a simple guide.</p> <p>In this example we add a cache tag to the response based on the id of the data:</p> <div class="expressive-code"><figure class="frame"><figcaption class="header"></figcaption><pre data-language="typescript"><code><div class="ec-line"><div class="code"><span style="--0:#5DE4C7">import </span><span style="--0:#A6ACCD">type</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">{</span><span style="--0:#5DE4C7"> </span><span style="--0:#ADD7FF">Config</span><span style="--0:#A6ACCD">,</span><span style="--0:#5DE4C7"> </span><span style="--0:#ADD7FF">Context</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">}</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">from</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">"</span><span style="--0:#5DE4C7">@netlify/functions</span><span style="--0:#A6ACCD">"</span><span style="--0:#A6ACCD">;</span></div></div><div class="ec-line"><div class="code"><span style="--0:#5DE4C7">import </span><span style="--0:#A6ACCD">{</span><span style="--0:#5DE4C7"> </span><span style="--0:#ADD7FF">CacheHeaders</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">}</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">from</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">"</span><span style="--0:#5DE4C7">cdn-cache-control</span><span style="--0:#A6ACCD">"</span><span style="--0:#A6ACCD">;</span></div></div><div class="ec-line"><div class="code"><span style="--0:#5DE4C7">import </span><span style="--0:#ADD7FF">etag</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">from</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">"</span><span style="--0:#5DE4C7">etag</span><span style="--0:#A6ACCD">"</span><span style="--0:#A6ACCD">;</span></div></div><div class="ec-line"><div class="code"><span style="--0:#5DE4C7">import </span><span style="--0:#A6ACCD">{</span><span style="--0:#5DE4C7"> </span><span style="--0:#ADD7FF">getPet</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">}</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">from</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">"</span><span style="--0:#5DE4C7">./pets-api.js</span><span style="--0:#A6ACCD">"</span><span style="--0:#A6ACCD">;</span></div></div><div class="ec-line"><div class="code"> </div></div><div class="ec-line"><div class="code"><span style="--0:#5DE4C7">export default async </span><span style="--0:#91B4D5">function</span><span style="--0:#5DE4C7"> </span><span style="--0:#ADD7FF">handler</span><span style="--0:#A6ACCD">(</span><span style="--0:#E4F0FB">request</span><span style="--0:#91B4D5">:</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCDD2">Request</span><span style="--0:#A6ACCD">,</span><span style="--0:#5DE4C7"> </span><span style="--0:#E4F0FB">context</span><span style="--0:#91B4D5">:</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCDD2">Context</span><span style="--0:#A6ACCD">)</span><span style="--0:#91B4D5">:</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCDD2">Promise</span><span style="--0:#A6ACCD"><</span><span style="--0:#A6ACCDD2">Response</span><span style="--0:#A6ACCD">></span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">{</span></div></div><div class="ec-line"><div class="code"> </div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#91B4D5">const</span><span style="--0:#5DE4C7"> </span><span style="--0:#E4F0FB">pet</span><span style="--0:#5DE4C7"> </span><span style="--0:#91B4D5">=</span><span style="--0:#5DE4C7"> </span><span style="--0:#5DE4C7C0">await</span><span style="--0:#5DE4C7"> </span><span style="--0:#E4F0FBD0">getPet</span><span style="--0:#A6ACCD">(</span><span style="--0:#E4F0FB">context</span><span style="--0:#A6ACCD">.</span><span style="--0:#E4F0FB">params</span><span style="--0:#A6ACCD">.</span><span style="--0:#E4F0FB">slug</span><span style="--0:#A6ACCD">);</span></div></div><div class="ec-line"><div class="code"> </div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#A6ACCD">if(</span><span style="--0:#91B4D5">!</span><span style="--0:#E4F0FB">pet</span><span style="--0:#A6ACCD">)</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#5DE4C7C0">return</span><span style="--0:#5DE4C7"> new </span><span style="--0:#E4F0FBD0">Response</span><span style="--0:#A6ACCD">(</span><span style="--0:#A6ACCD">"</span><span style="--0:#5DE4C7">Not found</span><span style="--0:#A6ACCD">"</span><span style="--0:#A6ACCD">,</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">{</span><span style="--0:#E4F0FB"> </span><span style="--0:#ADD7FF">status</span><span style="--0:#A6ACCD">:</span><span style="--0:#ADD7FF"> </span><span style="--0:#5DE4C7">404</span><span style="--0:#ADD7FF"> </span><span style="--0:#A6ACCD">});</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#A6ACCD">}</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#8E93AF;--0fs:italic">// Create the cache headers, setting the ETag to the hash of the pet object</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#91B4D5">const</span><span style="--0:#5DE4C7"> </span><span style="--0:#E4F0FB">headers</span><span style="--0:#5DE4C7"> </span><span style="--0:#91B4D5">=</span><span style="--0:#5DE4C7"> new </span><span style="--0:#E4F0FBD0">CacheHeaders</span><span style="--0:#A6ACCD">({</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#A6ACCD">"</span><span style="--0:#5DE4C7">etag</span><span style="--0:#A6ACCD">"</span><span style="--0:#A6ACCD">:</span><span style="--0:#ADD7FF"> </span><span style="--0:#E4F0FBD0">etag</span><span style="--0:#A6ACCD">(</span><span style="--0:#ADD7FF">JSON</span><span style="--0:#A6ACCD">.</span><span style="--0:#E4F0FBD0">stringify</span><span style="--0:#A6ACCD">(</span><span style="--0:#E4F0FB">pet</span><span style="--0:#A6ACCD">)),</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#8E93AF;--0fs:italic">// Use the chained 'tag' method to add a cache tag</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#A6ACCD">}).</span><span style="--0:#E4F0FBD0">tag</span><span style="--0:#A6ACCD">(</span><span style="--0:#A6ACCD">"</span><span style="--0:#5DE4C7">pets</span><span style="--0:#A6ACCD">"</span><span style="--0:#A6ACCD">,</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">"</span><span style="--0:#5DE4C7">pet-</span><span style="--0:#A6ACCD">"</span><span style="--0:#5DE4C7"> </span><span style="--0:#91B4D5">+</span><span style="--0:#5DE4C7"> </span><span style="--0:#E4F0FB">pet</span><span style="--0:#A6ACCD">.</span><span style="--0:#E4F0FB">id</span><span style="--0:#A6ACCD">);</span></div></div><div class="ec-line"><div class="code"> </div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#5DE4C7C0">return</span><span style="--0:#5DE4C7"> </span><span style="--0:#E4F0FB">Response</span><span style="--0:#A6ACCD">.</span><span style="--0:#E4F0FBD0">json</span><span style="--0:#A6ACCD">(</span><span style="--0:#E4F0FB">pet</span><span style="--0:#A6ACCD">,</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">{</span><span style="--0:#E4F0FB"> headers</span><span style="--0:#ADD7FF"> </span><span style="--0:#A6ACCD">});</span></div></div><div class="ec-line"><div class="code"><span style="--0:#A6ACCD">}</span></div></div><div class="ec-line"><div class="code"> </div></div><div class="ec-line"><div class="code"><span style="--0:#5DE4C7">export</span><span style="--0:#A6ACCD"> </span><span style="--0:#91B4D5">const</span><span style="--0:#A6ACCD"> </span><span style="--0:#E4F0FB">config</span><span style="--0:#91B4D5">:</span><span style="--0:#A6ACCD"> </span><span style="--0:#A6ACCDD2">Config</span><span style="--0:#A6ACCD"> {</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#91B4D5">path</span><span style="--0:#A6ACCD">: </span><span style="--0:#A6ACCD">"</span><span style="--0:#5DE4C7">/pets/:slug</span><span style="--0:#A6ACCD">"</span></div></div><div class="ec-line"><div class="code"><span style="--0:#A6ACCD">}</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="import type { Config, Context } from "@netlify/functions";import { CacheHeaders } from "cdn-cache-control";import etag from "etag";import { getPet } from "./pets-api.js";export default async function handler(request: Request, context: Context): Promise<Response> { const pet = await getPet(context.params.slug); if(!pet) { return new Response("Not found", { status: 404 }); } // Create the cache headers, setting the ETag to the hash of the pet object const headers = new CacheHeaders({ "etag": etag(JSON.stringify(pet)), // Use the chained 'tag' method to add a cache tag }).tag("pets", "pet-" + pet.id); return Response.json(pet, { headers });}export const config: Config { path: "/pets/:slug"}"><div></div></button></div></figure></div> <h3 id="expiring-content"><a class="header-anchor" href="#expiring-content">#</a>Expiring content</h3> <p>By default the <code>CacheHeaders</code> class will cache your content for up to a year, or until you next deploy. If you want to set a different expiry time you can use the <code>ttl</code> (time to live) method. You can pass a number of seconds to the <code>ttl</code> method to set a different value. There are helper constants for common values:</p> <div class="expressive-code"><figure class="frame"><figcaption class="header"></figcaption><pre data-language="javascript"><code><div class="ec-line"><div class="code"><span style="--0:#5DE4C7">import </span><span style="--0:#A6ACCD">{</span><span style="--0:#5DE4C7"> </span><span style="--0:#ADD7FF">CacheHeaders</span><span style="--0:#A6ACCD">,</span><span style="--0:#5DE4C7"> </span><span style="--0:#ADD7FF">ONE_WEEK</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">}</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">from</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">"</span><span style="--0:#5DE4C7">cdn-cache-control</span><span style="--0:#A6ACCD">"</span><span style="--0:#A6ACCD">;</span></div></div><div class="ec-line"><div class="code"> </div></div><div class="ec-line"><div class="code"><span style="--0:#8E93AF;--0fs:italic">// Cache content for up to a week</span></div></div><div class="ec-line"><div class="code"><span style="--0:#91B4D5">const</span><span style="--0:#A6ACCD"> </span><span style="--0:#E4F0FB">headers</span><span style="--0:#A6ACCD"> </span><span style="--0:#91B4D5">=</span><span style="--0:#A6ACCD"> </span><span style="--0:#5DE4C7">new</span><span style="--0:#A6ACCD"> </span><span style="--0:#E4F0FBD0">CacheHeaders</span><span style="--0:#A6ACCD">().</span><span style="--0:#E4F0FBD0">ttl</span><span style="--0:#A6ACCD">(</span><span style="--0:#E4F0FB">ONE_WEEK</span><span style="--0:#A6ACCD">);</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="import { CacheHeaders, ONE_WEEK } from "cdn-cache-control";// Cache content for up to a weekconst headers = new CacheHeaders().ttl(ONE_WEEK);"><div></div></button></div></figure></div> <h3 id="faster-loading-with-stale-while-revalidate"><a class="header-anchor" href="#faster-loading-with-stale-while-revalidate">#</a>Faster loading with <code>stale-while-revalidate</code></h3> <p>Enabling the <code>stale-while-revalidate</code> directive tells the CDN to return stale content after it has expired, but then regenerate the content in the background. This is good for pages that may be slow to render, but where it鈥檚 ok to return stale content for a while. You can enable <code>stale-while-revalidate</code> with the <code>swr</code> method:</p> <div class="expressive-code"><figure class="frame"><figcaption class="header"></figcaption><pre data-language="javascript"><code><div class="ec-line"><div class="code"><span style="--0:#5DE4C7">import </span><span style="--0:#A6ACCD">{</span><span style="--0:#5DE4C7"> </span><span style="--0:#ADD7FF">CacheHeaders</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">}</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">from</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">"</span><span style="--0:#5DE4C7">cdn-cache-control</span><span style="--0:#A6ACCD">"</span><span style="--0:#A6ACCD">;</span></div></div><div class="ec-line"><div class="code"> </div></div><div class="ec-line"><div class="code"><span style="--0:#91B4D5">const</span><span style="--0:#A6ACCD"> </span><span style="--0:#E4F0FB">headers</span><span style="--0:#A6ACCD"> </span><span style="--0:#91B4D5">=</span><span style="--0:#A6ACCD"> </span><span style="--0:#5DE4C7">new</span><span style="--0:#A6ACCD"> </span><span style="--0:#E4F0FBD0">CacheHeaders</span><span style="--0:#A6ACCD">().</span><span style="--0:#E4F0FBD0">swr</span><span style="--0:#A6ACCD">();</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="import { CacheHeaders } from "cdn-cache-control";const headers = new CacheHeaders().swr();"><div></div></button></div></figure></div> <p>By default it will return stale content for up to a week, but you can pass a number of seconds to the <code>swr</code> method to set a different value.</p> <div class="expressive-code"><figure class="frame"><figcaption class="header"></figcaption><pre data-language="javascript"><code><div class="ec-line"><div class="code"><span style="--0:#5DE4C7">import </span><span style="--0:#A6ACCD">{</span><span style="--0:#5DE4C7"> </span><span style="--0:#ADD7FF">CacheHeaders</span><span style="--0:#A6ACCD">,</span><span style="--0:#5DE4C7"> </span><span style="--0:#ADD7FF">ONE_DAY</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">}</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">from</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">"</span><span style="--0:#5DE4C7">cdn-cache-control</span><span style="--0:#A6ACCD">"</span><span style="--0:#A6ACCD">;</span></div></div><div class="ec-line"><div class="code"> </div></div><div class="ec-line"><div class="code"><span style="--0:#8E93AF;--0fs:italic">// Return stale content for up to an hour, and revalidate it in the background</span></div></div><div class="ec-line"><div class="code"><span style="--0:#91B4D5">const</span><span style="--0:#A6ACCD"> </span><span style="--0:#E4F0FB">headers</span><span style="--0:#A6ACCD"> </span><span style="--0:#91B4D5">=</span><span style="--0:#A6ACCD"> </span><span style="--0:#5DE4C7">new</span><span style="--0:#A6ACCD"> </span><span style="--0:#E4F0FBD0">CacheHeaders</span><span style="--0:#A6ACCD">().</span><span style="--0:#E4F0FBD0">swr</span><span style="--0:#A6ACCD">(</span><span style="--0:#E4F0FB">ONE_HOUR</span><span style="--0:#A6ACCD">);</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="import { CacheHeaders, ONE_DAY } from "cdn-cache-control";// Return stale content for up to an hour, and revalidate it in the backgroundconst headers = new CacheHeaders().swr(ONE_HOUR);"><div></div></button></div></figure></div> <p>By default, calling <code>swr</code> will expire content immediately and regenerate it after each request. If you鈥檙e happy to keep the content fresh for a while you can use the <code>ttl</code> method alongside <code>swr</code>.</p> <div class="expressive-code"><figure class="frame"><figcaption class="header"></figcaption><pre data-language="javascript"><code><div class="ec-line"><div class="code"><span style="--0:#5DE4C7">import </span><span style="--0:#A6ACCD">{</span><span style="--0:#5DE4C7"> </span><span style="--0:#ADD7FF">CacheHeaders</span><span style="--0:#A6ACCD">,</span><span style="--0:#5DE4C7"> </span><span style="--0:#ADD7FF">ONE_HOUR</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">}</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">from</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">"</span><span style="--0:#5DE4C7">cdn-cache-control</span><span style="--0:#A6ACCD">"</span><span style="--0:#A6ACCD">;</span></div></div><div class="ec-line"><div class="code"> </div></div><div class="ec-line"><div class="code"><span style="--0:#8E93AF;--0fs:italic">// Content is fresh for 30 seconds, then return stale content for up to an hour</span></div></div><div class="ec-line"><div class="code"><span style="--0:#91B4D5">const</span><span style="--0:#A6ACCD"> </span><span style="--0:#E4F0FB">headers</span><span style="--0:#A6ACCD"> </span><span style="--0:#91B4D5">=</span><span style="--0:#A6ACCD"> </span><span style="--0:#5DE4C7">new</span><span style="--0:#A6ACCD"> </span><span style="--0:#E4F0FBD0">CacheHeaders</span><span style="--0:#A6ACCD">().</span><span style="--0:#E4F0FBD0">ttl</span><span style="--0:#A6ACCD">(</span><span style="--0:#5DE4C7">30</span><span style="--0:#A6ACCD">).</span><span style="--0:#E4F0FBD0">swr</span><span style="--0:#A6ACCD">(</span><span style="--0:#E4F0FB">ONE_HOUR</span><span style="--0:#A6ACCD">);</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="import { CacheHeaders, ONE_HOUR } from "cdn-cache-control";// Content is fresh for 30 seconds, then return stale content for up to an hourconst headers = new CacheHeaders().ttl(30).swr(ONE_HOUR);"><div></div></button></div></figure></div> <h3 id="immutable-content"><a class="header-anchor" href="#immutable-content">#</a>Immutable content</h3> <p>Sometimes you are generating content that you know will <em>never</em> change. This might be an asset that includes a hash in the URL, or other unchanging unique content. For these you can use the <code>immutable</code> to cache the content for the year in both the CDN and the browser, adding the <code>immutable</code> directive to the headers.</p> <div class="expressive-code"><figure class="frame"><figcaption class="header"></figcaption><pre data-language="javascript"><code><div class="ec-line"><div class="code"><span style="--0:#5DE4C7">import </span><span style="--0:#A6ACCD">{</span><span style="--0:#5DE4C7"> </span><span style="--0:#ADD7FF">CacheHeaders</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">}</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">from</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">"</span><span style="--0:#5DE4C7">cdn-cache-control</span><span style="--0:#A6ACCD">"</span><span style="--0:#A6ACCD">;</span></div></div><div class="ec-line"><div class="code"> </div></div><div class="ec-line"><div class="code"><span style="--0:#8E93AF;--0fs:italic">// Cache content for up to a year, and tell the browser it will never change</span></div></div><div class="ec-line"><div class="code"><span style="--0:#91B4D5">const</span><span style="--0:#A6ACCD"> </span><span style="--0:#E4F0FB">headers</span><span style="--0:#A6ACCD"> </span><span style="--0:#91B4D5">=</span><span style="--0:#A6ACCD"> </span><span style="--0:#5DE4C7">new</span><span style="--0:#A6ACCD"> </span><span style="--0:#E4F0FBD0">CacheHeaders</span><span style="--0:#A6ACCD">().</span><span style="--0:#E4F0FBD0">immutable</span><span style="--0:#A6ACCD">();</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="import { CacheHeaders } from "cdn-cache-control";// Cache content for up to a year, and tell the browser it will never changeconst headers = new CacheHeaders().immutable();"><div></div></button></div></figure></div> <section class="callout warning" data-astro-cid-mrmim4ef> <h2 data-astro-cid-mrmim4ef>Careful</h2> <div class="callout-content" data-astro-cid-mrmim4ef> <p>Be careful when using <code>immutable</code> as it will cache the content for a year in the browser. If you need to change the content you will need to change the URL or the content itself. It is best to restrict its use to cases where the URL is guaranteed to be unique and unchanging, such as assets with a hash in the URL.</p> </div> </section> <h3 id="using-the-cacheheaders-object-with-a-framework"><a class="header-anchor" href="#using-the-cacheheaders-object-with-a-framework">#</a>Using the <code>CacheHeaders</code> object with a framework</h3> <p>The <code>CacheHeaders</code> object works perfectly with frameworks SSR functions. It can be used anywhere you can set response headers. It is a valid fetch <code>Headers</code> object, so can be used with the <code>Response</code> constructor. Some frameworks use a readonly <code>Headers</code> object, where you need to set the headers in the response object directly. In this case you can use the <code>copyTo</code> method to copy the headers to the response object:</p> <div class="expressive-code"><figure class="frame"><figcaption class="header"></figcaption><pre data-language="astro"><code><div class="ec-line"><div class="code"><span style="--0:#8E93AF;--0fs:italic">---</span></div></div><div class="ec-line"><div class="code"><span style="--0:#5DE4C7">import </span><span style="--0:#A6ACCD">{</span><span style="--0:#5DE4C7"> </span><span style="--0:#ADD7FF">CacheHeaders</span><span style="--0:#A6ACCD">,</span><span style="--0:#5DE4C7"> </span><span style="--0:#ADD7FF">ONE_HOUR</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">}</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">from</span><span style="--0:#5DE4C7"> </span><span style="--0:#A6ACCD">"</span><span style="--0:#5DE4C7">cdn-cache-control</span><span style="--0:#A6ACCD">"</span><span style="--0:#A6ACCD">;</span></div></div><div class="ec-line"><div class="code"> </div></div><div class="ec-line"><div class="code"><span style="--0:#5DE4C7">new</span><span style="--0:#A6ACCD"> </span><span style="--0:#E4F0FBD0">CacheHeaders</span><span style="--0:#A6ACCD">().</span><span style="--0:#E4F0FBD0">swr</span><span style="--0:#A6ACCD">(</span><span style="--0:#E4F0FB">ONE_HOUR</span><span style="--0:#A6ACCD">).</span><span style="--0:#E4F0FBD0">copyTo</span><span style="--0:#A6ACCD">(</span><span style="--0:#E4F0FB">Astro</span><span style="--0:#A6ACCD">.</span><span style="--0:#E4F0FB">response</span><span style="--0:#A6ACCD">.</span><span style="--0:#E4F0FB">headers</span><span style="--0:#A6ACCD">);</span></div></div><div class="ec-line"><div class="code"><span style="--0:#8E93AF;--0fs:italic">---</span></div></div></code></pre><div class="copy"><button title="Copy to clipboard" data-copied="Copied!" data-code="---import { CacheHeaders, ONE_HOUR } from "cdn-cache-control";new CacheHeaders().swr(ONE_HOUR).copyTo(Astro.response.headers);---"><div></div></button></div></figure></div> <h3 id="troubleshooting"><a class="header-anchor" href="#troubleshooting">#</a>Troubleshooting</h3> <p>If you鈥檙e having trouble, the <code>Cache-Status</code> header can be useful for debugging. It shows the status of the cache for the request, including whether the content was served from the cache or not, and whether it was stale or fresh. For more details see <a href="https://docs.netlify.com/platform/caching/#debug-with-cache-status">the cache debugging docs</a>.</p> <h3 id="learn-more"><a class="header-anchor" href="#learn-more">#</a>Learn more</h3> <p>For more details and full API docs, see <a href="https://github.com/ascorbic/cdn-cache-control"><code>cdn-cache-control</code> on GitHub</a>.</p> </div> <button class="toc-toggle" aria-label="Show Table of Contents" aria-expanded="false" aria-controls="toc" data-variant="primary" data-astro-cid-3aox33w5> <span data-astro-cid-3aox33w5></span> <span data-astro-cid-3aox33w5></span> <span data-astro-cid-3aox33w5></span> </button> <nav id="toc" class="toc" data-astro-cid-3aox33w5> <div class="toc-inside" data-astro-cid-3aox33w5> <div class="toc-box" data-astro-cid-3aox33w5> <div class="toc-body" data-astro-cid-3aox33w5> <h2 data-astro-cid-3aox33w5>In this guide</h2> <ul data-astro-cid-qw4gscv3><li data-astro-cid-2b2qxdbo> <a href="#tldr" data-astro-cid-2b2qxdbo> TL;DR </a> </li> <li data-astro-cid-2b2qxdbo> <a href="#advanced-caching-on-netlify" data-astro-cid-2b2qxdbo> Advanced caching on Netlify </a> </li> <li data-astro-cid-2b2qxdbo> <a href="#manually-setting-headers" data-astro-cid-2b2qxdbo> Manually setting headers </a> </li> <li data-astro-cid-2b2qxdbo> <a href="#using-the-cdn-cache-control-package" data-astro-cid-2b2qxdbo> Using the cdn-cache-control package </a> <ul data-astro-cid-2b2qxdbo> <li data-astro-cid-2b2qxdbo> <a href="#installing-the-package" data-astro-cid-2b2qxdbo> Installing the package </a> </li> <li data-astro-cid-2b2qxdbo> <a href="#using-the-package" data-astro-cid-2b2qxdbo> Using the package </a> </li> <li data-astro-cid-2b2qxdbo> <a href="#tagging-content-for-invalidation" data-astro-cid-2b2qxdbo> Tagging content for invalidation </a> </li> <li data-astro-cid-2b2qxdbo> <a href="#expiring-content" data-astro-cid-2b2qxdbo> Expiring content </a> </li> <li data-astro-cid-2b2qxdbo> <a href="#faster-loading-with-stale-while-revalidate" data-astro-cid-2b2qxdbo> Faster loading with stale-while-revalidate </a> </li> <li data-astro-cid-2b2qxdbo> <a href="#immutable-content" data-astro-cid-2b2qxdbo> Immutable content </a> </li> <li data-astro-cid-2b2qxdbo> <a href="#using-the-cacheheaders-object-with-a-framework" data-astro-cid-2b2qxdbo> Using the CacheHeaders object with a framework </a> </li> <li data-astro-cid-2b2qxdbo> <a href="#troubleshooting" data-astro-cid-2b2qxdbo> Troubleshooting </a> </li> <li data-astro-cid-2b2qxdbo> <a href="#learn-more" data-astro-cid-2b2qxdbo> Learn more </a> </li> </ul> </li> </ul> </div> </div> </div> </nav> </div> <div class="post-footer l-center" data-astro-cid-2u677oxp> <div class="more-guides" data-astro-cid-2u677oxp> <h2 data-astro-cid-2u677oxp>More guides</h2> <div class="grid" data-astro-cid-2u677oxp> <div class="guides-grid-item" data-astro-cid-llvbsxhg> <time datetime="2024-02-13T00:00:00.000Z" data-astro-cid-llvbsxhg>February 13, 2024</time> <h3 data-astro-cid-llvbsxhg><a href="/guides/how-to-do-isr-and-advanced-caching-with-remix" target="_self" data-astro-cid-llvbsxhg>How to do ISR and advanced caching with Remix</a></h3> <p class="author" data-astro-cid-llvbsxhg> by Matt Kane </p> <div class="excerpt" data-astro-cid-llvbsxhg> <div class="prose"><p>Incremental Static Regeneration (ISR) is a powerful pattern for rendering pages on the web. Remix has useful tools to do fine-grained ISR and other advanced caching patterns when deployed to Netlify. This guide will show you how to do it.</p> </div> </div> <span class="tags" data-astro-cid-llvbsxhg> <a href="/guides/tag/remix" class="wrapper" data-astro-cid-lbpydqlr><svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" data-astro-cid-4ejhtf3k> <circle cx="50" cy="50" r="50" fill="var(--neutral-light-000)" data-astro-cid-4ejhtf3k></circle> </svg> <span data-astro-cid-lbpydqlr>Remix</span></a><a href="/guides/tag/caching" class="wrapper" data-astro-cid-lbpydqlr><svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" data-astro-cid-4ejhtf3k> <circle cx="50" cy="50" r="50" fill="var(--neutral-light-000)" data-astro-cid-4ejhtf3k></circle> </svg> <span data-astro-cid-lbpydqlr>Caching</span></a><a href="/guides/tag/primitives" class="wrapper" data-astro-cid-lbpydqlr><svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" data-astro-cid-4ejhtf3k> <circle cx="50" cy="50" r="50" fill="var(--neutral-light-000)" data-astro-cid-4ejhtf3k></circle> </svg> <span data-astro-cid-lbpydqlr>Primitives</span></a> </span> </div> <div class="guides-grid-item" data-astro-cid-llvbsxhg> <time datetime="2024-04-25T00:00:00.000Z" data-astro-cid-llvbsxhg>April 25, 2024</time> <h3 data-astro-cid-llvbsxhg><a href="/guides/how-to-do-advanced-caching-and-isr-with-astro" target="_self" data-astro-cid-llvbsxhg>How to do ISR and advanced caching with Astro</a></h3> <p class="author" data-astro-cid-llvbsxhg> by Matt Kane </p> <div class="excerpt" data-astro-cid-llvbsxhg> <div class="prose"><p>Incremental Static Regeneration (ISR) is a powerful pattern for rendering pages on the web. Astro has useful tools to do fine-grained ISR and other advanced caching patterns when deployed to Netlify. This guide will show you how to do it.</p> </div> </div> <span class="tags" data-astro-cid-llvbsxhg> <a href="/guides/tag/astro" class="wrapper" data-astro-cid-lbpydqlr><svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" data-astro-cid-4ejhtf3k> <circle cx="50" cy="50" r="50" fill="var(--neutral-light-000)" data-astro-cid-4ejhtf3k></circle> </svg> <span data-astro-cid-lbpydqlr>Astro</span></a><a href="/guides/tag/caching" class="wrapper" data-astro-cid-lbpydqlr><svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" data-astro-cid-4ejhtf3k> <circle cx="50" cy="50" r="50" fill="var(--neutral-light-000)" data-astro-cid-4ejhtf3k></circle> </svg> <span data-astro-cid-lbpydqlr>Caching</span></a><a href="/guides/tag/primitives" class="wrapper" data-astro-cid-lbpydqlr><svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" data-astro-cid-4ejhtf3k> <circle cx="50" cy="50" r="50" fill="var(--neutral-light-000)" data-astro-cid-4ejhtf3k></circle> </svg> <span data-astro-cid-lbpydqlr>Primitives</span></a> </span> </div> </div> <p class="tag-list" data-astro-cid-2u677oxp> <span data-astro-cid-2u677oxp>Explore other guides also tagged with</span> <a href="/guides/tag/caching" class="wrapper" data-astro-cid-lbpydqlr><svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" data-astro-cid-4ejhtf3k> <circle cx="50" cy="50" r="50" fill="var(--neutral-light-000)" data-astro-cid-4ejhtf3k></circle> </svg> <span data-astro-cid-lbpydqlr>Caching</span></a><a href="/guides/tag/primitives" class="wrapper" data-astro-cid-lbpydqlr><svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" data-astro-cid-4ejhtf3k> <circle cx="50" cy="50" r="50" fill="var(--neutral-light-000)" data-astro-cid-4ejhtf3k></circle> </svg> <span data-astro-cid-lbpydqlr>Primitives</span></a> or <a href="/guides/tags" data-astro-cid-2u677oxp>explore all tags</a>. </p> </div> <div class="card l-stack" data-theme="dark" id="guide-feedback-form" data-astro-cid-2u677oxp> <div class="feedback" data-astro-cid-77rygnu7> <div class="media" data-astro-cid-77rygnu7> <div class="l-stack" data-astro-cid-77rygnu7> <h2 class="media-title" data-astro-cid-77rygnu7>Did you find this guide useful?</h2> <p class="media-copy" data-astro-cid-77rygnu7>Your feedback helps us improve our guides.</p> </div> <div class="media-figure" data-astro-cid-77rygnu7> <button aria-label="upvote" class="feedback-vote feedback-vote-upvote" data-astro-cid-77rygnu7> <svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" aria-hidden="true" data-astro-cid-77rygnu7> <g fill="none" fill-rule="evenodd" data-astro-cid-77rygnu7> <circle cx="32" cy="32" r="32" data-astro-cid-77rygnu7></circle> <path d="M40 29h-7.645l1.473-3.889c.377-.996.042-2.135-.803-2.73-.963-.679-2.263-.427-2.936.569L26 29v9a4 4 0 0 0 4 4h6.517c1.51 0 2.893-.852 3.573-2.203L42 36v-5a2 2 0 0 0-2-2zm-18 0v12" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" data-astro-cid-77rygnu7></path> </g> </svg> </button> <button aria-label="downvote" class="feedback-vote feedback-vote-downvote" data-astro-cid-77rygnu7> <svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" aria-hidden="true" data-astro-cid-77rygnu7> <g fill="none" fill-rule="evenodd" data-astro-cid-77rygnu7> <circle cx="32" cy="32" r="32" data-astro-cid-77rygnu7></circle> <path d="M23 35h7.645l-1.473 3.889c-.377.996-.042 2.135.803 2.73.963.679 2.263.427 2.936-.569l4.09-6.05v-9a4 4 0 0 0-4-4h-6.518c-1.51 0-2.893.852-3.573 2.202L21 28v5a2 2 0 0 0 2 2zm18 0V23" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" data-astro-cid-77rygnu7></path> </g> </svg> </button> </div> </div> <form class='feedback-form' data-astro-cid-77rygnu7 method='POST' name='guide-feedback'> <input type="hidden" name="form-name" value="guide-feedback" data-astro-cid-77rygnu7> <input type="hidden" name="path" value="/guides/advanced-caching-made-easy/" data-astro-cid-77rygnu7> <input type="hidden" name="vote" data-astro-cid-77rygnu7> <div class="visually-hidden" data-astro-cid-77rygnu7> <label data-astro-cid-77rygnu7>Don鈥檛 fill this out if you鈥檙e human</label> <input name="verification" data-astro-cid-77rygnu7> </div> <p data-astro-cid-77rygnu7> <label for="feedback" data-astro-cid-77rygnu7> An up or down vote is helpful, but if you've got more specific feedback, we'd be grateful for that too. </label> <textarea id="feedback" name="feedback" data-astro-cid-77rygnu7></textarea> </p> <p class="form-submit" data-astro-cid-77rygnu7> <button class="feedback-submit-btn btn" data-variant="secondary" type="submit" disabled data-astro-cid-77rygnu7>Send</button> </p> </form> </div> <p data-astro-cid-2u677oxp> What else would you like to learn? You can <a href="/guides#suggest-guide-form" data-astro-cid-2u677oxp>suggest a guide idea here</a>. </p> </div> </div> </div> </main> <footer class="l-breakout" data-astro-cid-w3csb3e3> <div style="--cluster-align: center; --cluster-justify: space-between" class="l-cluster" data-astro-cid-w3csb3e3> <a href="https://www.netlify.com/" id="cta-footer-netlifyLogo" data-astro-cid-w3csb3e3> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 105" width="256" height="105" fill="none"> <g clip-path="url(#a)"> <path fill="#05BDBA" d="M58.47 103.765v-26.35l.547-.547h6.587l.546.546v26.351l-.546.546h-6.587l-.547-.546ZM58.47 26.897V.547L59.017 0h6.587l.546.546v26.351l-.546.546h-6.587l-.547-.546ZM35.797 85.24h-.904l-4.531-4.532v-.904l8.49-8.5 4.796.01.64.63v4.796l-8.49 8.5ZM30.362 24.747v-.913l4.53-4.523h.905l8.491 8.49v4.788l-.64.648h-4.796l-8.49-8.49ZM.546 48.307H37.88l.546.546v6.597l-.547.546H.547L0 55.45v-6.597l.546-.546ZM255.445 48.316l.546.546v6.588l-.546.546h-37.879l-.546-.546 2.739-6.588.546-.546h35.14Z"></path> <path fill="#014847" d="M74.667 65.886h-6.588l-.546-.546V49.92c0-2.748-1.075-4.873-4.386-4.94-1.707-.043-3.653 0-5.735.085l-.316.315v19.951l-.546.546h-6.587l-.547-.546v-26.35l.547-.547h14.822c5.76 0 10.428 4.668 10.428 10.428v16.47l-.546.545v.009ZM106.573 54.349l-.546.546H88.994l-.546.546c0 1.1 1.1 4.395 5.496 4.395 1.646 0 3.293-.546 3.848-1.647l.546-.546h6.588l.546.546c-.546 3.294-3.294 8.243-11.537 8.243-9.335 0-13.73-6.588-13.73-14.285S84.6 37.862 93.389 37.862s13.184 6.588 13.184 14.285v2.202Zm-8.243-5.496c0-.546-.546-4.394-4.941-4.394s-4.941 3.848-4.941 4.394l.546.547h8.79l.546-.547ZM121.95 57.643c0 1.1.546 1.647 1.647 1.647h4.941l.546.546v5.495l-.546.546h-4.941c-4.941 0-9.336-2.201-9.336-8.243V45.551l-.546-.546h-3.848l-.546-.546v-5.496l.546-.546h3.848l.546-.546v-4.94l.546-.547h6.588l.546.546v4.94l.546.547h6.042l.546.546v5.496l-.546.546h-6.042l-.546.546v12.083l.009.009ZM142.276 65.886h-6.587l-.547-.546V27.98l.547-.545h6.587l.546.546v37.35l-.546.546v.009ZM157.107 34.022h-6.588l-.546-.546v-5.495l.546-.546h6.588l.546.546v5.495l-.546.546Zm0 31.864h-6.588l-.546-.546V38.972l.546-.546h6.588l.546.546V65.34l-.546.546ZM182.929 27.98v5.496l-.546.546h-4.941c-1.101 0-1.647.547-1.647 1.647v2.202l.546.546h5.496l.546.546v5.496l-.546.546h-5.496l-.546.546v19.772l-.546.546h-6.588l-.546-.546V45.55l-.546-.546h-3.848l-.547-.546v-5.496l.547-.546h3.848l.546-.546v-2.202c0-6.041 4.395-8.243 9.336-8.243h4.94l.547.546-.009.009ZM203.247 66.432c-2.202 5.495-4.395 8.79-12.083 8.79h-2.748l-.546-.547V69.18l.546-.546h2.748c2.747 0 3.294-.546 3.848-2.194v-.546l-8.789-21.427v-5.495l.546-.546h4.941l.546.546 6.588 18.68h.546l6.588-18.68.546-.546h4.941l.546.546v5.495l-8.79 21.974.026-.009Z"></path> </g> <defs><clipPath id="a"><path fill="#fff" d="M0 0h256v104.311H0z"></path></clipPath></defs> </svg> </a> <div class="social | l-cluster" data-astro-cid-w3csb3e3> <a href="https://www.youtube.com/@NetlifyApp" data-astro-cid-w3csb3e3> <svg width="24" height="24" viewBox="0 0 22 16" data-astro-cid-w3csb3e3 data-icon="youtube"> <title>YouTube</title> <symbol id="ai:local:youtube"><path fill="currentcolor" d="M10.994.524s-6.508 0-8.142.435c-.874.25-1.594.99-1.839 1.9C.59 4.536.59 8.007.59 8.007s0 3.484.424 5.134c.245.91.952 1.636 1.84 1.887 1.646.448 8.14.448 8.14.448s6.521 0 8.155-.435a2.62 2.62 0 0 0 1.826-1.887c.437-1.663.437-5.134.437-5.134s.013-3.484-.437-5.16A2.6 2.6 0 0 0 19.148.984c-1.634-.46-8.154-.46-8.154-.46M8.923 4.8l5.415 3.207L8.923 11.2z"/></symbol><use xlink:href="#ai:local:youtube"></use> </svg> </a> <a href="https://x.com/Netlify" data-astro-cid-w3csb3e3> <svg width="24" height="24" viewBox="0 0 512 512" data-astro-cid-w3csb3e3 data-icon="twitter"> <title>Twitter</title> <symbol id="ai:local:twitter"><style>@keyframes appear{0%{opacity:0;transform:scale3d(.3,.3,.3)}20%{opacity:1;transform:scale3d(1.1,1.1,1.1)}40%{transform:scale3d(.9,.9,.9)}60%{transform:scale3d(1.03,1.03,1.03)}80%{transform:scale3d(.97,.97,.97)}to{transform:scale3d(1.001,1.001,1.001)}}</style><path fill="currentcolor" d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8l164.9-188.5L26.8 48h145.6l100.5 132.9zm-24.8 373.8h39.1L151.1 88h-42z" class="icon-twitter-x"/></symbol><use xlink:href="#ai:local:twitter"></use> </svg> </a> <a href="https://linkedin.com/company/Netlify" data-astro-cid-w3csb3e3> <svg width="24" height="24" viewBox="0 0 448 512" data-astro-cid-w3csb3e3 data-icon="linkedin"> <title>LinkedIn</title> <symbol id="ai:local:linkedin"><path fill="currentcolor" d="M416 32H31.9C14.3 32 0 46.5 0 64.3v383.4C0 465.5 14.3 480 31.9 480H416c17.6 0 32-14.5 32-32.3V64.3c0-17.8-14.4-32.3-32-32.3M135.4 416H69V202.2h66.5V416zm-33.2-243c-21.3 0-38.5-17.3-38.5-38.5S80.9 96 102.2 96c21.2 0 38.5 17.3 38.5 38.5 0 21.3-17.2 38.5-38.5 38.5m282.1 243h-66.4V312c0-24.8-.5-56.7-34.5-56.7-34.6 0-39.9 27-39.9 54.9V416h-66.4V202.2h63.7v29.2h.9c8.9-16.8 30.6-34.5 62.9-34.5 67.2 0 79.7 44.3 79.7 101.9z"/></symbol><use xlink:href="#ai:local:linkedin"></use> </svg> </a> <a href="https://answers.netlify.com" data-astro-cid-w3csb3e3> <svg width="24" height="24" viewBox="0 0 24 24" data-astro-cid-w3csb3e3 data-icon="discourse"> <title>Forums</title> <symbol id="ai:local:discourse"><path fill="currentcolor" d="M12.103 0C18.666 0 24 5.485 24 11.997c0 6.51-5.33 11.99-11.9 11.99L0 24V11.79C0 5.28 5.532 0 12.103 0m.116 4.563a7.4 7.4 0 0 0-6.337 3.57 7.25 7.25 0 0 0-.148 7.22L4.4 19.61l4.794-1.074a7.42 7.42 0 0 0 8.136-1.39 7.26 7.26 0 0 0 1.737-7.997 7.375 7.375 0 0 0-6.84-4.585z"/></symbol><use xlink:href="#ai:local:discourse"></use> </svg> </a> </div> </div> <div class="links-container" data-astro-cid-w3csb3e3> <section data-astro-cid-w3csb3e3> <h3 class="headline text-0" data-astro-cid-w3csb3e3>Developers</h3> <ul role="list" data-astro-cid-w3csb3e3> <li data-astro-cid-w3csb3e3> <a href="/" class="text--1" data-astro-cid-w3csb3e3>Netlify Developers</a> </li> <li data-astro-cid-w3csb3e3> <a href="/sdk/" class="text--1" data-astro-cid-w3csb3e3>Netlify SDK</a> </li> <li data-astro-cid-w3csb3e3> <a href="/cli/" class="text--1" data-astro-cid-w3csb3e3>Netlify CLI</a> </li> <li data-astro-cid-w3csb3e3> <a href="/feed/" class="text--1" data-astro-cid-w3csb3e3>Activity Feed</a> </li> <li data-astro-cid-w3csb3e3> <a href="https://docs.netlify.com" class="text--1" data-astro-cid-w3csb3e3>Documentation</a> </li> </ul> </section> <section data-astro-cid-w3csb3e3> <h3 class="headline text-0" data-astro-cid-w3csb3e3>Company</h3> <ul role="list" data-astro-cid-w3csb3e3> <li data-astro-cid-w3csb3e3> <a href="https://netlify.com/blog/" class="text--1" data-astro-cid-w3csb3e3>Blog</a> </li> <li data-astro-cid-w3csb3e3> <a href="https://netlify.com/about/" class="text--1" data-astro-cid-w3csb3e3>About</a> </li> <li data-astro-cid-w3csb3e3> <a href="https://netlify.com/careers/" class="text--1" data-astro-cid-w3csb3e3>Careers</a> </li> <li data-astro-cid-w3csb3e3> <a href="https://netlify.com/conference/" class="text--1" data-astro-cid-w3csb3e3>Compose Conference</a> </li> <li data-astro-cid-w3csb3e3> <a href="https://netlify.com/press/" class="text--1" data-astro-cid-w3csb3e3>Press</a> </li> <li data-astro-cid-w3csb3e3> <a href="https://netlify.com/jamstack-fund/" class="text--1" data-astro-cid-w3csb3e3>Jamstack Fund</a> </li> <li data-astro-cid-w3csb3e3> <a href="https://swag.netlify.com/" class="text--1" data-astro-cid-w3csb3e3>Netlify Store</a> </li> <li data-astro-cid-w3csb3e3> <a href="https://netlify.com/sustainability/" class="text--1" data-astro-cid-w3csb3e3>Sustainability</a> </li> </ul> </section> <section data-astro-cid-w3csb3e3> <h3 class="headline text-0" data-astro-cid-w3csb3e3>Contact Us</h3> <ul role="list" data-astro-cid-w3csb3e3> <li data-astro-cid-w3csb3e3> <a id="cta-footer-sales" href="https://netlify.com/enterprise/contact/" class="text--1" data-astro-cid-w3csb3e3>Sales</a> </li> <li data-astro-cid-w3csb3e3> <a id="cta-footer-support" href="https://netlify.com/support/" class="text--1" data-astro-cid-w3csb3e3>Support</a> </li> <li data-astro-cid-w3csb3e3> <a id="cta-footer-status" href="https://netlifystatus.com/" class="text--1" data-astro-cid-w3csb3e3>Status</a> </li> <li data-astro-cid-w3csb3e3> <a id="cta-footer-answers" href="https://answers.netlify.com/" class="text--1" data-astro-cid-w3csb3e3>Forums</a> </li> <li data-astro-cid-w3csb3e3> <a id="cta-footer-agencyDirectory" href="https://netlify.com/agency-directory/" class="text--1" data-astro-cid-w3csb3e3>Hire an Agency</a> </li> </ul> </section> </div> <section class="fine-print-container" data-astro-cid-w3csb3e3> <ul class="legal" data-astro-cid-w3csb3e3> <li data-astro-cid-w3csb3e3> <a id="cta-legal-footer-trustCenter" href="https://netlify.com/trust-center/" class="text--1" data-astro-cid-w3csb3e3>Trust Center</a> </li> <li data-astro-cid-w3csb3e3> <a id="cta-legal-footer-privacyPolicy" href="https://netlify.com/privacy/" class="text--1" data-astro-cid-w3csb3e3>Privacy</a> </li> <li data-astro-cid-w3csb3e3> <a id="cta-legal-footer-security" href="https://netlify.com/security/" class="text--1" data-astro-cid-w3csb3e3>Security</a> </li> <li data-astro-cid-w3csb3e3> <a id="cta-legal-footer-gdpr" href="https://netlify.com/gdpr-ccpa/" class="text--1" data-astro-cid-w3csb3e3>GDPR/CCPA</a> </li> <li data-astro-cid-w3csb3e3> <a id="cta-legal-footer-abuse" href="mailto:fraud@netlify.com?subject=Abuse%20report&body=Please%20include%20the%20site%20URL%20and%20reason%20for%20your%20report%2C%20and%20we%20will%20reply%20promptly." class="text--1" data-astro-cid-w3csb3e3> Abuse </a> </li> </ul> <p class="copyright text--1" data-astro-cid-w3csb3e3>漏 2024 Netlify</p> </section> </footer> <!-- Start of HubSpot Embed Code --> <script type="text/javascript" id="hs-script-loader" async defer src="//js.hs-scripts.com/7477936.js"></script> <!-- End of HubSpot Embed Code --> <script async id="netlify-rum-container" src="/.netlify/scripts/rum" data-netlify-rum-site-id="41d9d252-cc2d-4600-bf60-32c3315b509d" data-netlify-deploy-branch="main" data-netlify-deploy-context="production" data-netlify-cwv-token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzaXRlX2lkIjoiNDFkOWQyNTItY2MyZC00NjAwLWJmNjAtMzJjMzMxNWI1MDlkIiwiYWNjb3VudF9pZCI6IjU4ZGE4ODkzZDY4NjVkMzVjOTJhNzJiOCIsImRlcGxveV9pZCI6IjY3NDc3MjEzNDlhOTA2MDAwODgxODkzNyIsImlzc3VlciI6Im5mc2VydmVyIn0.PH9o5twd7bDe9YSGl8ppwpF1M2wpLBhxRLBWpVR1WlM"></script></body></html> <style> /* Embedded tweet */ .twitter-tweet { margin-bottom: 2rem !important; margin-top: 0 !important; } .twitter-tweet-rendered { margin: 0 auto 2rem !important; } </style>