{"id":207,"date":"2020-03-20T21:23:33","date_gmt":"2020-03-20T20:23:33","guid":{"rendered":"https:\/\/blog.remyblom.nl\/?p=207"},"modified":"2020-11-17T10:20:57","modified_gmt":"2020-11-17T09:20:57","slug":"articles-archive-mark-nottingham-caching-tutorial","status":"publish","type":"post","link":"https:\/\/blog.remyblom.nl\/?p=207","title":{"rendered":"Article Archive: Mark Nottingham &#8211; Caching Tutorial"},"content":{"rendered":"\n<p>Every once and a while you run into an article on the internet that&#8217;s just too good to just make a bookmark (or some similar technique that allows you to store the URL to that previous piece of useful information). I have been on the internet long enough to know that no matter how good an article might be, just wait long enough and it will be gone, some day&#8230; So I decided to store these kinds of articles right here, on my own website, in what I call the <strong>Article Archive<\/strong>.<\/p>\n\n\n\n<p>This is the first of suchs articles; in which Mark Nottingham dives deep into the realm of caching. <\/p>\n\n\n\n<p>Original: <a href=\"https:\/\/www.mnot.net\/cache_docs\/\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"https:\/\/www.mnot.net\/cache_docs\/ (opens in a new tab)\">https:\/\/www.mnot.net\/cache_docs\/<\/a><\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Caching Tutorial<\/h1>\n\n\n\n<p>for Web Authors and Webmasters<\/p>\n\n\n\n<p>This is an informational document. Although technical in\nnature, it attempts to make the concepts involved understandable and\napplicable in real-world situations. Because of this, some aspects of the\nmaterial are simplified or omitted, for the sake of clarity. If you are\ninterested in the minutia of the subject, please explore the <a href=\"https:\/\/www.mnot.net\/cache_docs\/#REF\">References and Further Information<\/a> at the end.<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li><a href=\"https:\/\/www.mnot.net\/cache_docs\/#DEFINITION\">What\u2019s a Web Cache? Why do people use\n    them?<\/a><\/li><li><a href=\"https:\/\/www.mnot.net\/cache_docs\/#KINDS\">Kinds of Web Caches<\/a> \n    <ol><li><a href=\"https:\/\/www.mnot.net\/cache_docs\/#BROWSER\">Browser Caches<\/a><\/li><li><a href=\"https:\/\/www.mnot.net\/cache_docs\/#PROXY\">Proxy Caches<\/a><\/li><\/ol>\n  <\/li><li><a href=\"https:\/\/www.mnot.net\/cache_docs\/#WHY\">Aren\u2019t Web Caches bad for me? Why should I\n    help them?<\/a><\/li><li><a href=\"https:\/\/www.mnot.net\/cache_docs\/#WORK\">How Web Caches Work<\/a><\/li><li><a href=\"https:\/\/www.mnot.net\/cache_docs\/#CONTROL\">How (and how not) to Control Caches<\/a> \n    <ol><li><a href=\"https:\/\/www.mnot.net\/cache_docs\/#META\">HTML Meta Tags vs. HTTP\n      Headers<\/a><\/li><li><a href=\"https:\/\/www.mnot.net\/cache_docs\/#PRAGMA\">Pragma HTTP Headers (and why they\n        don\u2019t work)<\/a><\/li><li><a href=\"https:\/\/www.mnot.net\/cache_docs\/#EXPIRES\">Controlling Freshness with the\n        Expires HTTP Header<\/a><\/li><li><a href=\"https:\/\/www.mnot.net\/cache_docs\/#CACHE-CONTROL\">Cache-Control HTTP\n        Headers<\/a><\/li><li><a href=\"https:\/\/www.mnot.net\/cache_docs\/#VALIDATE\">Validators and\n      Validation<\/a><\/li><\/ol>\n  <\/li><li><a href=\"https:\/\/www.mnot.net\/cache_docs\/#TIPS\">Tips for Building a Cache-Aware\n  Site<\/a><\/li><li><a href=\"https:\/\/www.mnot.net\/cache_docs\/#SCRIPT\">Writing Cache-Aware Scripts<\/a><\/li><li><a href=\"https:\/\/www.mnot.net\/cache_docs\/#FAQ\">Frequently Asked Questions<\/a><\/li><li><a href=\"https:\/\/www.mnot.net\/cache_docs\/#IMP-SERVER\">Implementation Notes \u2014 Web\n  Servers<\/a><\/li><li><a href=\"https:\/\/www.mnot.net\/cache_docs\/#IMP-SCRIPT\">Implementation Notes \u2014 Server-Side\n    Scripting<\/a><\/li><li><a href=\"https:\/\/www.mnot.net\/cache_docs\/#REF\">References and Further Information<\/a><\/li><li><a href=\"https:\/\/www.mnot.net\/cache_docs\/#ABOUT\">About This Document<\/a><\/li><\/ol>\n\n\n\n<h2 class=\"wp-block-heading\"><a>What\u2019s a Web Cache? Why do people\nuse them?<\/a><\/h2>\n\n\n\n<p>A <em>Web cache<\/em> sits between one or more Web servers (also known as\n<em>origin servers<\/em>) and a client or many clients, and watches requests\ncome by, saving copies of the responses \u2014 like HTML pages, images and files\n(collectively known as <em>representations<\/em>) \u2014 for itself. Then, if there\nis another request for the same URL, it can use the response that it has,\ninstead of asking the origin server for it again.<\/p>\n\n\n\n<p>There are two main reasons that Web caches are used:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>To <strong>reduce latency<\/strong> \u2014 Because the request is satisfied\n  from the cache (which is closer to the client) instead of the origin server,\n  it takes less time for it to get the representation and display it. This\n  makes the Web seem more responsive.<\/li><li>To <strong>reduce network traffic<\/strong> \u2014 Because representations are\n  reused, it reduces the amount of bandwidth used by a client. This saves\n  money if the client is paying for traffic, and keeps their bandwidth\n  requirements lower and more manageable.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><a>Kinds of Web Caches<\/a><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><a>Browser Caches<\/a><\/h3>\n\n\n\n<p>If you examine the preferences dialog of any modern Web browser (like\nInternet Explorer, Safari or Mozilla), you\u2019ll probably notice a \u201ccache\u201d\nsetting. This lets you set aside a section of your computer\u2019s hard disk to\nstore representations that you\u2019ve seen, just for you. The browser cache works\naccording to fairly simple rules. It will check to make sure that the\nrepresentations are fresh, usually once a session (that is, the once in the\ncurrent invocation of the browser).<\/p>\n\n\n\n<p>This cache is especially useful when users hit the \u201cback\u201d button or click a\nlink to see a page they\u2019ve just looked at. Also, if you use the same\nnavigation images throughout your site, they\u2019ll be served from browsers\u2019\ncaches almost instantaneously.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><a>Proxy Caches<\/a><\/h3>\n\n\n\n<p>Web proxy caches work on the same principle, but a much larger scale.\nProxies serve hundreds or thousands of users in the same way; large\ncorporations and ISPs often set them up on their firewalls, or as standalone\ndevices (also known as <em>intermediaries<\/em>).<\/p>\n\n\n\n<p>Because proxy caches aren\u2019t part of the client or the origin server, but\ninstead are out on the network, requests have to be routed to them somehow.\nOne way to do this is to use your browser\u2019s proxy setting to manually tell it\nwhat proxy to use; another is using interception. <em>Interception\nproxies<\/em> have Web requests redirected to them by the underlying\nnetwork itself, so that clients don\u2019t need to be configured for them, or even\nknow about them.<\/p>\n\n\n\n<p>Proxy caches are a type of <em>shared cache<\/em>; rather than just having\none person using them, they usually have a large number of users, and because\nof this they are very good at reducing latency and network traffic. That\u2019s\nbecause popular representations are reused a number of times.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><a>Gateway Caches<\/a><\/h3>\n\n\n\n<p>Also known as \u201creverse proxy caches\u201d or \u201csurrogate caches,\u201d gateway caches\nare also intermediaries, but instead of being deployed by network\nadministrators to save bandwidth, they\u2019re typically deployed by Webmasters\nthemselves, to make their sites more scalable, reliable and better\nperforming.<\/p>\n\n\n\n<p>Requests can be routed to gateway caches by a number of methods, but\ntypically some form of load balancer is used to make one or more of them look\nlike the origin server to clients.<\/p>\n\n\n\n<p><em>Content delivery networks<\/em> (CDNs) distribute gateway caches\nthroughout the Internet (or a part of it) and sell caching to interested Web\nsites. <a href=\"http:\/\/www.speedera.com\/\">Speedera<\/a> and <a href=\"http:\/\/www.akamai.com\/\">Akamai<\/a> are examples of\nCDNs.<\/p>\n\n\n\n<p>This tutorial focuses mostly on browser and proxy caches, although some of\nthe information is suitable for those interested in gateway caches as\nwell.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a>Aren\u2019t Web Caches bad for me? Why should I help\nthem?<\/a><\/h2>\n\n\n\n<p>Web caching is one of the most misunderstood technologies on the Internet.\nWebmasters in particular fear losing control of their site, because a proxy\ncache can \u201chide\u201d their users from them, making it difficult to see who\u2019s using\nthe site.<\/p>\n\n\n\n<p>Unfortunately for them, even if Web caches didn\u2019t exist, there are too many\nvariables on the Internet to assure that they\u2019ll be able to get an accurate\npicture of how users see their site. If this is a big concern for you, this\ntutorial will teach you how to get the statistics you need without making your\nsite cache-unfriendly.<\/p>\n\n\n\n<p>Another concern is that caches can serve content that is out of date, or\n<em>stale<\/em>. However, this tutorial can show you how to configure your\nserver to control how your content is cached.<\/p>\n\n\n\n<p><abbr title=\"Content Delivery Networks\">CDNs<\/abbr>\nare an interesting development, because unlike many \nproxy caches, their gateway caches are aligned with the interests of the \nWeb site being cached, so that these problems aren\u2019t seen. However, even\nwhen you use a CDN, you still have to consider that there will be proxy\nand browser caches downstream.<\/p>\n\n\n\n<p>On the other hand, if you plan your site well, caches can help your Web\nsite load faster, and save load on your server and Internet link. The\ndifference can be dramatic; a site that is difficult to cache may take\nseveral seconds to load, while one that takes advantage of caching can seem\ninstantaneous in comparison. Users will appreciate a fast-loading site, and\nwill visit more often.<\/p>\n\n\n\n<p>Think of it this way; many large Internet companies are spending millions\nof dollars setting up farms of servers around the world to replicate their\ncontent, in order to make it as fast to access as possible for their users.\nCaches do the same for you, and they\u2019re even closer to the end user. Best of\nall, you don\u2019t have to pay for them.<\/p>\n\n\n\n<p>The fact is that proxy and browser caches will be used whether you like it\nor not. If you don\u2019t configure your site to be cached correctly, it will be\ncached using whatever defaults the cache\u2019s administrator decides upon.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a>How Web Caches Work<\/a><\/h2>\n\n\n\n<p>All caches have a set of rules that they use to determine when to serve a\nrepresentation from the cache, if it\u2019s available. Some of these rules are set\nin the protocols (HTTP 1.0 and 1.1), and some are set by the administrator of\nthe cache (either the user of the browser cache, or the proxy\nadministrator).<\/p>\n\n\n\n<p>Generally speaking, these are the most common rules that are followed\n(don\u2019t worry if you don\u2019t understand the details, it will be explained\nbelow):<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>If the response\u2019s headers tell the cache not to keep it,\n  it won\u2019t.<\/li><li>If the request is authenticated or secure (i.e., HTTPS), it won\u2019t be\n  cached by shared caches.<\/li><li>A cached representation is considered <em>fresh<\/em> (that is, able to\n  be sent to a client without checking with the origin server) if:\n    <ul><li>It has an expiry time or other age-controlling header set, and is\n      still within the fresh period, or<\/li><li>If the cache has seen the representation recently, and it was\n      modified relatively long ago.<\/li><\/ul>\n    Fresh representations are served directly from the cache, without checking\n    with the origin server.<\/li><li>If a representation is stale, the origin server will be asked to\n  <em>validate<\/em> it, or tell the cache whether the copy that it has is\n  still good.<\/li><li>Under certain circumstances \u2014 for example, when it\u2019s disconnected from a network \u2014\n\ta cache can serve stale responses without checking with the origin server.<\/li><\/ol>\n\n\n\n<p>If no validator (an <code>ETag<\/code> or <code>Last-Modified<\/code> header) is\npresent on a response, <em>and<\/em> it doesn&#8217;t have any explicit freshness information, \nit will usually \u2014 but not always \u2014 be considered uncacheable.<\/p>\n\n\n\n<p>Together, <em>freshness<\/em> and <em>validation<\/em> are the most important\nways that a cache works with content. A fresh representation will be available\ninstantly from the cache, while a validated representation will avoid sending\nthe entire representation over again if it hasn\u2019t changed.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a>How (and how not) to Control\nCaches<\/a><\/h2>\n\n\n\n<p>There are several tools that Web designers and Webmasters can use to\nfine-tune how caches will treat their sites. It may require getting your hands\na little dirty with your server\u2019s configuration, but the results are worth it.\nFor details on how to use these tools with your server, see the <a href=\"https:\/\/www.mnot.net\/cache_docs\/#IMP-SERVER\">Implementation<\/a> sections below.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><a>HTML Meta Tags and HTTP Headers<\/a><\/h3>\n\n\n\n<p>HTML authors can put tags in a document\u2019s &lt;HEAD&gt; section that\ndescribe its attributes. These <em>meta tags<\/em> are often used in the\nbelief that they can mark a document as uncacheable, or expire it at a\ncertain time.<\/p>\n\n\n\n<p>Meta tags are easy to use, but aren\u2019t very effective. That\u2019s\nbecause they\u2019re only honored by a few browser caches, not proxy caches\n(which almost never read the HTML in the document). While it may be tempting\nto put a Pragma: no-cache meta tag into a Web page, it won\u2019t necessarily\ncause it to be kept fresh.<\/p>\n\n\n\n<p>If your site is hosted at an ISP or hosting farm and they\ndon\u2019t give you the ability to set arbitrary HTTP headers (like <code>Expires<\/code> and\n<code>Cache-Control<\/code>), complain loudly; these are tools necessary for doing your\njob.<\/p>\n\n\n\n<p>On the other hand, true <em>HTTP headers<\/em> give you a lot of control\nover how both browser caches and proxies handle your representations. They\ncan\u2019t be seen in the HTML, and are usually automatically generated by the Web\nserver. However, you can control them to some degree, depending on the server\nyou use. In the following sections, you\u2019ll see what HTTP headers are\ninteresting, and how to apply them to your site.<\/p>\n\n\n\n<p>HTTP headers are sent by the server before the HTML, and only seen by the\nbrowser and any intermediate caches. Typical HTTP 1.1 response headers might\nlook like this:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">HTTP\/1.1 200 OK\nDate: Fri, 30 Oct 1998 13:19:41 GMT\nServer: Apache\/1.3.3 (Unix)\nCache-Control: max-age=3600, must-revalidate\nExpires: Fri, 30 Oct 1998 14:19:41 GMT\nLast-Modified: Mon, 29 Jun 1998 02:28:12 GMT\nETag: \"3e86-410-3596fbbc\"\nContent-Length: 1040\nContent-Type: text\/html<\/pre>\n\n\n\n<p>The HTML would follow these headers, separated by a blank\nline. See the <a href=\"https:\/\/www.mnot.net\/cache_docs\/#IMP-SERVER\">Implementation<\/a> sections for information about how to set HTTP\nheaders.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><a>Pragma HTTP Headers (and why they don\u2019t\nwork)<\/a><\/h3>\n\n\n\n<p>Many people believe that assigning a <code>Pragma: no-cache<\/code> HTTP header to a\nrepresentation will make it uncacheable. This is not necessarily true; the\nHTTP specification does not set any guidelines for Pragma response headers;\ninstead, Pragma request headers (the headers that a browser sends to a server)\nare discussed. Although a few caches may honor this header, the majority\nwon\u2019t, and it won\u2019t have any effect. Use the headers below instead.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><a>Controlling Freshness with the Expires\nHTTP Header<\/a><\/h3>\n\n\n\n<p>The <code>Expires<\/code> HTTP header is a basic means of controlling caches; it tells\nall caches how long the associated representation is fresh for. After that\ntime, caches will always check back with the origin server to see if a\ndocument is changed. <code>Expires<\/code> headers are supported by practically every\ncache.<\/p>\n\n\n\n<p>Most Web servers allow you to set <code>Expires<\/code> response headers in a number of\nways. Commonly, they will allow setting an absolute time to expire, a time\nbased on the last time that the client retrieved the representation (last <em>access\ntime<\/em>), or a time based on the last time the document changed on your\nserver (last <em>modification time<\/em>).<\/p>\n\n\n\n<p><code>Expires<\/code> headers are especially good for making static images (like\nnavigation bars and buttons) cacheable. Because they don\u2019t change much, you\ncan set extremely long expiry time on them, making your site appear much more\nresponsive to your users. They\u2019re also useful for controlling caching of a\npage that is regularly changed. For instance, if you update a news page once a\nday at 6am, you can set the representation to expire at that time, so caches\nwill know when to get a fresh copy, without users having to hit \u2018reload\u2019.<\/p>\n\n\n\n<p>The <strong>only<\/strong> value valid in an <code>Expires<\/code> header is a HTTP date;\nanything else will most likely be interpreted as \u2018in the past\u2019, so that the\nrepresentation is uncacheable. Also, remember that the time in a HTTP date is\nGreenwich Mean Time (GMT), not local time.<\/p>\n\n\n\n<p>For example:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Expires: Fri, 30 Oct 1998 14:19:41 GMT<\/pre>\n\n\n\n<p>It\u2019s important to make sure that your Web\nserver\u2019s clock is accurate if you use the <code>Expires<\/code> header. \nOne way to do this is using the <a href=\"http:\/\/www.ntp.org\/\">Network Time\nProtocol<\/a> (NTP); talk to your local system administrator to find out\nmore.<\/p>\n\n\n\n<p>Although the <code>Expires<\/code> header is useful, it has some limitations. First,\nbecause there\u2019s a date involved, the clocks on the Web server and the cache\nmust be synchronised; if they have a different idea of the time, the intended\nresults won\u2019t be achieved, and caches might wrongly consider stale content as\nfresh.<\/p>\n\n\n\n<p>Another problem with <code>Expires<\/code> is that it\u2019s easy to forget that you\u2019ve set\nsome content to expire at a particular time. If you don\u2019t update an <code>Expires<\/code>\ntime before it passes, each and every request will go back to your Web server, \nincreasing load and latency.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><a>Cache-Control HTTP\nHeaders<\/a><\/h3>\n\n\n\n<p>HTTP 1.1 introduced a new class of headers, <code>Cache-Control<\/code> response\nheaders, to give Web publishers more control over their content, and\nto address the limitations of <code>Expires<\/code>.<\/p>\n\n\n\n<p>Useful <code>Cache-Control<\/code> response headers include:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong><code>max-age=<\/code><\/strong>[seconds] \u2014 specifies the maximum amount of\n  time that a representation will be considered fresh. Similar to <code>Expires<\/code>,\n  this directive is relative to the time of the request, rather than absolute.\n  <\/li><\/ul>\n\n\n<p>[seconds]<\/p>\n\n\n\n<p> is the number of seconds from the time of the request you wish the\n  representation to be fresh for.\n  <strong><code>s-maxage=<\/code><\/strong>[seconds] \u2014 similar to <code>max-age<\/code>, except that it\n  only applies to shared (e.g., proxy) caches.\n  <strong><code>public<\/code><\/strong> \u2014 marks authenticated responses as cacheable;\n  normally, if HTTP authentication is required, responses are automatically private.\n  <strong><code>private<\/code><\/strong> \u2014 allows caches that are specific to one user (e.g., in a \n\tbrowser) to store the response; shared caches (e.g., in a proxy) may not. \n  <strong><code>no-cache<\/code><\/strong> \u2014 forces caches to submit the request to the\n  origin server for validation before releasing a cached copy, every time.\n  This is useful to assure that authentication is respected (in combination\n  with public), or to maintain rigid freshness, without sacrificing all of the\n  benefits of caching.\n  <strong><code>no-store<\/code><\/strong> \u2014 instructs caches not to keep a copy of the\n  representation under any conditions.\n  <strong><code>must-revalidate<\/code><\/strong> \u2014 tells caches that they must obey any\n  freshness information you give them about a representation. HTTP allows\n  caches to serve stale representations under special conditions; by\n  specifying this header, you\u2019re telling the cache that you want it to\n  strictly follow your rules.\n  <strong><code>proxy-revalidate<\/code><\/strong> \u2014 similar to <code>must-revalidate<\/code>, except\n  that it only applies to proxy caches.\n\n\n<\/p>\n\n\n\n<p>For example:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Cache-Control: max-age=3600, must-revalidate<\/pre>\n\n\n\n<p>When both <code>Cache-Control<\/code> and <code>Expires<\/code> are present,\n<code>Cache-Control<\/code> takes precedence. If you plan to use the\n<code>Cache-Control<\/code> headers, you should have a look at the excellent\ndocumentation in HTTP 1.1; see <a href=\"https:\/\/www.mnot.net\/cache_docs\/#REF\">References and Further\nInformation<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><a>Validators and Validation<\/a><\/h3>\n\n\n\n<p>In <a href=\"https:\/\/www.mnot.net\/cache_docs\/#WORK\">How Web Caches Work<\/a>, we said that validation is used\nby servers and caches to communicate when a representation has changed. By\nusing it, caches avoid having to download the entire representation when they\nalready have a copy locally, but they\u2019re not sure if it\u2019s still fresh.<\/p>\n\n\n\n<p>Validators are very important; if one isn\u2019t present, and there isn\u2019t any\nfreshness information (<code>Expires<\/code> or <code>Cache-Control<\/code>) available, caches will\nnot store a representation at all.<\/p>\n\n\n\n<p>The most common validator is the time that the document last changed, as\ncommunicated in <code>Last-Modified<\/code> header. When a cache has a\nrepresentation stored that includes a <code>Last-Modified<\/code> header, it can use it to\nask the server if the representation has changed since the last time it was\nseen, with an <code>If-Modified-Since<\/code> request.<\/p>\n\n\n\n<p>HTTP 1.1 introduced a new kind of validator called the <em>ETag<\/em>. ETags\nare unique identifiers that are generated by the server and changed every time\nthe representation does. Because the server controls how the ETag is\ngenerated, caches can be sure that if the ETag matches when they make a\n<code>If-None-Match<\/code> request, the representation really is the same.<\/p>\n\n\n\n<p>Almost all caches use Last-Modified times as validators; ETag validation is also becoming prevalent.<\/p>\n\n\n\n<p>Most modern Web servers will generate both <code>ETag<\/code> and <code>Last-Modified<\/code>\nheaders to use as validators for static content (i.e., files) automatically; you won\u2019t have to\ndo anything. However, they don\u2019t know enough about dynamic content (like CGI,\nASP or database sites) to generate them; see <a href=\"https:\/\/www.mnot.net\/cache_docs\/#SCRIPT\">Writing\nCache-Aware Scripts<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a>Tips for Building a Cache-Aware Site<\/a><\/h2>\n\n\n\n<p>Besides using freshness information and validation, there are a number of\nother things you can do to make your site more cache-friendly.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>Use URLs consistently<\/strong> \u2014 this is the golden\n    rule of caching. If you serve the same content on different pages, to\n    different users, or from different sites, it should use the same URL.\n    This is the easiest and most effective way to make your site\n    cache-friendly. For example, if you use \u201c\/index.html\u201d in your HTML as a\n    reference once, always use it that way.<\/li><li><strong>Use a common library of images<\/strong> and other elements and\n    refer back to them from different places.<\/li><li><strong>Make caches store images and pages that don\u2019t change\n  often<\/strong> by using a <code>Cache-Control: max-age<\/code> header with a large\n  value.<\/li><li><strong>Make caches recognise regularly updated pages<\/strong> by\n    specifying an appropriate max-age or expiration time.<\/li><li><strong>If a resource (especially a downloadable file) changes, change\n    its name.<\/strong> That way, you can make it expire far in the future,\n    and still guarantee that the correct version is served; the page that\n    links to it is the only one that will need a short expiry time.<\/li><li><strong>Don\u2019t change files unnecessarily.<\/strong> If you do,\n    everything will have a falsely young <code>Last-Modified<\/code> date. For instance,\n    when updating your site, don\u2019t copy over the entire site; just move the\n    files that you\u2019ve changed.<\/li><li><strong>Use cookies only where necessary<\/strong> \u2014 cookies are\n    difficult to cache, and aren\u2019t needed in most situations. If you must use\n    a cookie, limit its use to dynamic pages.<\/li><li><strong>Check your pages with <a href=\"https:\/\/redbot.org\/\">REDbot<\/a><\/strong>\n    \u2014 it can help you apply many of the concepts in this tutorial.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><a>Writing Cache-Aware Scripts<\/a><\/h2>\n\n\n\n<p>By default, most scripts won\u2019t return a validator (a <code>Last-Modified<\/code>\nor <code>ETag<\/code> response header) or freshness information (<code>Expires<\/code> or <code>Cache-Control<\/code>).\nWhile some scripts really are dynamic (meaning that they return a different\nresponse for every request), many (like search engines and database-driven\nsites) can benefit from being cache-friendly.<\/p>\n\n\n\n<p>Generally speaking, if a script produces output that is reproducible with\nthe same request at a later time (whether it be minutes or days later), it\nshould be cacheable. If the content of the script changes only depending on\nwhat\u2019s in the URL, it is cacheable; if the output depends on a cookie,\nauthentication information or other external criteria, it probably isn\u2019t.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>The best way to make a script cache-friendly (as well as perform\n    better) is to dump its content to a plain file whenever it changes. The\n    Web server can then treat it like any other Web page, generating and\n    using validators, which makes your life easier. Remember to only write\n    files that have changed, so the <code>Last-Modified<\/code> times are preserved.<\/li><li>Another way to make a script cacheable in a limited fashion is to set\n    an age-related header for as far in the future as practical. Although\n    this can be done with <code>Expires<\/code>, it\u2019s probably easiest to do so with\n    <code>Cache-Control: max-age<\/code>, which will make the request fresh for an amount\n    of time after the request.<\/li><li>If you can\u2019t do that, you\u2019ll need to make the script generate a\n    validator, and then respond to <code>If-Modified-Since<\/code> and\/or <code>If-None-Match<\/code>\n    requests. This can be done by parsing the HTTP headers, and then\n    responding with <code>304 Not Modified<\/code> when appropriate. Unfortunately, this is\n    not a trival task.<\/li><\/ul>\n\n\n\n<p>Some other tips;<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>Don\u2019t use POST<\/strong> unless it\u2019s appropriate. Responses to\n  the POST method aren\u2019t kept by most caches; if you send information in the\n  path or query (via GET), caches can store that information for the\n  future.<\/li><li><strong>Don\u2019t embed user-specific information in the URL<\/strong> unless\n  the content generated is completely unique to that user.<\/li><li><strong>Don\u2019t count on all requests from a user coming from the same\n  host<\/strong>, because caches often work together.<\/li><li><strong>Generate <code>Content-Length<\/code> response headers.<\/strong> It\u2019s easy to\n  do, and it will allow the response of your script to be used in a\n  <em>persistent connection<\/em>. This allows clients to request\n  multiple representations on one TCP\/IP connection, instead of setting up a\n  connection for every request. It makes your site seem much faster.<\/li><\/ul>\n\n\n\n<p>See the <a href=\"https:\/\/www.mnot.net\/cache_docs\/#IMP-SCRIPT\">Implementation Notes<\/a> for more specific\ninformation.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a>Frequently Asked Questions<\/a><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">What are the most important things to make cacheable?<\/h3>\n\n\n\n<p>A good strategy is to identify the most popular, largest representations\n(especially images) and work with them first.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How can I make my pages as fast as possible with caches?<\/h3>\n\n\n\n<p>The most cacheable representation is one with a long freshness time set.\nValidation does help reduce the time that it takes to see a representation,\nbut the cache still has to contact the origin server to see if it\u2019s fresh. If\nthe cache already knows it\u2019s fresh, it will be served directly.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">I understand that caching is good, but I need to keep statistics on how\nmany people visit my page!<\/h3>\n\n\n\n<p>If you must know every time a page is accessed, select ONE small item on\na page (or the page itself), and make it uncacheable, by giving it a suitable\nheaders. For example, you could refer to a 1&#215;1 transparent uncacheable image\nfrom each page. The <code>Referer<\/code> header will contain information about what page\ncalled it.<\/p>\n\n\n\n<p>Be aware that even this will not give truly accurate statistics about your\nusers, and is unfriendly to the Internet and your users; it generates\nunnecessary traffic, and forces people to wait for that uncached item to be\ndownloaded. For more information about this, see On Interpreting Access\nStatistics in the <a href=\"https:\/\/www.mnot.net\/cache_docs\/#REF\">references<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How can I see a representation\u2019s HTTP headers?<\/h3>\n\n\n\n<p>Many Web browsers let you see the <code>Expires<\/code> and <code>Last-Modified<\/code> headers are in\na \u201cpage info\u201d or similar interface. If available, this will give you a menu of\nthe page and any representations (like images) associated with it, along with\ntheir details.<\/p>\n\n\n\n<p>To see the full headers of a representation, you can manually connect to\nthe Web server using a Telnet client.<\/p>\n\n\n\n<p>To do so, you may need to type the port (be default, 80) into a separate\nfield, or you may need to connect to <code>www.example.com:80<\/code> or <code>www.example.com 80<\/code>\n(note the space). Consult your Telnet client\u2019s documentation.<\/p>\n\n\n\n<p>Once you\u2019ve opened a connection to the site, type a request for the\nrepresentation. For instance, if you want to see the headers for\n<code>http:\/\/www.example.com\/foo.html<\/code>, connect to <code>www.example.com<\/code>, port <code>80<\/code>, and\ntype:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">GET \/foo.html HTTP\/1.1 [return]\nHost: www.example.com [return][return]<\/pre>\n\n\n\n<p>Press the Return key every time you see <code>[return]<\/code>; make sure to press it\ntwice at the end. This will print the headers, and then the full\nrepresentation. To see the headers only, substitute HEAD for GET.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">My pages are password-protected; how do proxy caches deal with them?<\/h3>\n\n\n\n<p>By default, pages protected with HTTP authentication are considered private;\nthey will not be kept by shared caches. However, you can make authenticated\npages public with a Cache-Control: public header; HTTP 1.1-compliant caches will then\nallow them to be cached.<\/p>\n\n\n\n<p>If you\u2019d like such pages to be cacheable, but still authenticated for every\nuser, combine the <code>Cache-Control: public<\/code> and <code>no-cache<\/code> headers. This tells the\ncache that it must submit the new client\u2019s authentication information to the\norigin server before releasing the representation from the cache. This would look like:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Cache-Control: public, no-cache<\/pre>\n\n\n\n<p>Whether or not this is done, it\u2019s best to minimize use of authentication;\nfor example, if your images are not sensitive, put them in a separate\ndirectory and configure your server not to force authentication for it. That\nway, those images will be naturally cacheable.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Should I worry about security if people access my site through a\ncache?<\/h3>\n\n\n\n<p><code>https:\/\/<\/code> pages are not cached (or decrypted) by proxy caches, so you don\u2019t have\nto worry about that. However, because caches store <code>http:\/\/<\/code> responses and URLs\nfetched through them, you should be conscious about unsecured sites; an\nunscrupulous administrator could conceivably gather information about their\nusers, especially in the URL.<\/p>\n\n\n\n<p>In fact, any administrator on the network between your server and your\nclients could gather this type of information. One particular problem is when\nCGI scripts put usernames and passwords in the URL itself; this makes it\ntrivial for others to find and use their login.<\/p>\n\n\n\n<p>If you\u2019re aware of the issues surrounding Web security in general, you\nshouldn\u2019t have any surprises from proxy caches.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">I\u2019m looking for an integrated Web publishing solution. Which ones are\ncache-aware?<\/h3>\n\n\n\n<p>It varies. Generally speaking, the more complex a solution is, the more\ndifficult it is to cache. The worst are ones which dynamically generate all\ncontent and don\u2019t provide validators; they may not be cacheable at all. Speak\nwith your vendor\u2019s technical staff for more information, and see the\nImplementation notes below.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">My images expire a month from now, but I need to change them in the\ncaches now!<\/h3>\n\n\n\n<p>The Expires header can\u2019t be circumvented; unless the cache (either browser\nor proxy) runs out of room and has to delete the representations, the cached\ncopy will be used until then.<\/p>\n\n\n\n<p>The most effective solution is to change any links to them; that way,\ncompletely new representations will be loaded fresh from the origin server.\nRemember that any page that refers to these representations will be cached as\nwell. Because of this, it\u2019s best to make static images and similar\nrepresentations very cacheable, while keeping the HTML pages that refer to\nthem on a tight leash.<\/p>\n\n\n\n<p>If you want to reload a representation from a specific cache, you can\neither force a reload (in Firefox, holding down shift while pressing \u2018reload\u2019\nwill do this by issuing a <code>Pragma: no-cache<\/code> request header) while using the\ncache. Or, you can have the cache administrator delete the representation\nthrough their interface.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">I run a Web Hosting service. How can I let my users publish\ncache-friendly pages?<\/h3>\n\n\n\n<p>If you\u2019re using Apache, consider allowing them to use .htaccess files and\nproviding appropriate documentation.<\/p>\n\n\n\n<p>Otherwise, you can establish predetermined areas for various caching\nattributes in each virtual server. For instance, you could specify a\ndirectory \/cache-1m that will be cached for one month after access, and a\n\/no-cache area that will be served with headers instructing caches not to\nstore representations from it.<\/p>\n\n\n\n<p>Whatever you are able to do, it is best to work with your largest\ncustomers first on caching. Most of the savings (in bandwidth and in load on\nyour servers) will be realized from high-volume sites.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">I\u2019ve marked my pages as cacheable, but my browser keeps requesting them\non every request. How do I force the cache to keep representations of them?<\/h3>\n\n\n\n<p>Caches aren\u2019t required to keep a representation and reuse it; they\u2019re only\nrequired to <strong>not<\/strong> keep or use them under some conditions. All\ncaches make decisions about which representations to keep based upon their\nsize, type (e.g., image vs. html), or by how much space they have left to keep\nlocal copies. Yours may not be considered worth keeping around, compared to \nmore popular or larger representations.<\/p>\n\n\n\n<p>Some caches do allow their administrators to prioritize what kinds of \nrepresentations are kept, and some allow representations to be \u201cpinned\u201d in\ncache, so that they\u2019re always available.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a>Implementation Notes \u2014 Web\nServers<\/a><\/h2>\n\n\n\n<p>Generally speaking, it\u2019s best to use the latest version of whatever Web\nserver you\u2019ve chosen to deploy. Not only will they likely contain more\ncache-friendly features, new versions also usually have important security\nand performance improvements.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Apache HTTP Server<\/h3>\n\n\n\n<p><a href=\"http:\/\/www.apache.org\/\">Apache<\/a> uses\noptional modules to include headers, including both Expires and\nCache-Control. Both modules are available in the 1.2 or greater\ndistribution.<\/p>\n\n\n\n<p>The modules need to be built into Apache; although they are included in\nthe distribution, they are not turned on by default. To find out if the\nmodules are enabled in your server, find the httpd binary and run <code>httpd\n-l<\/code>; this should print a list of the available modules (note that this only \nlists compiled-in modules; on later versions of Apache, use <code>httpd -M<\/code> \nto include dynamically loaded modules as well). The modules we\u2019re\nlooking for are expires_module and headers_module.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>If they aren\u2019t available, and you have administrative access, you can\n    recompile Apache to include them. This can be done either by uncommenting\n    the appropriate lines in the Configuration file, or using the\n    <code>-enable-module=expires<\/code> and <code>-enable-module=headers<\/code>\n    arguments to configure (1.3 or greater). Consult the INSTALL file found\n    with the Apache distribution.<\/li><\/ul>\n\n\n\n<p>Once you have an Apache with the appropriate modules, you can use\nmod_expires to specify when representations should expire, either in .htaccess\nfiles or in the server\u2019s access.conf file. You can specify expiry from either\naccess or modification time, and apply it to a file type or as a default. See\nthe <a href=\"http:\/\/www.apache.org\/docs\/mod\/mod_expires.html\">module\ndocumentation<\/a> for more information, and speak with your local Apache guru\nif you have trouble.<\/p>\n\n\n\n<p>To apply <code>Cache-Control<\/code> headers, you\u2019ll need to use the mod_headers module,\nwhich allows you to specify arbitrary HTTP headers for a resource. See <a href=\"http:\/\/www.apache.org\/docs\/mod\/mod_headers.html\">the \nmod_headers documentation<\/a>.<\/p>\n\n\n\n<p>Here\u2019s an example .htaccess file that demonstrates the use of some\nheaders.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>.htaccess files allow web publishers to use commands normally only\n    found in configuration files. They affect the content of the directory\n    they\u2019re in and their subdirectories. Talk to your server administrator to\n    find out if they\u2019re enabled.<\/li><\/ul>\n\n\n\n<pre class=\"wp-block-preformatted\">### activate mod_expires\nExpiresActive On\n### Expire .gif's 1 month from when they're accessed\nExpiresByType image\/gif A2592000\n### Expire everything else 1 day from when it's last modified\n### (this uses the Alternative syntax)\nExpiresDefault \"modification plus 1 day\"\n### Apply a Cache-Control header to index.html\n&lt;Files index.html&gt;\nHeader append Cache-Control \"public, must-revalidate\"\n&lt;\/Files&gt;<\/pre>\n\n\n\n<ul class=\"wp-block-list\"><li>Note that mod_expires automatically calculates and inserts a\n    <code>Cache-Control:max-age<\/code> header as appropriate.<\/li><\/ul>\n\n\n\n<p>Apache 2\u2019s configuration is very similar to that of 1.3; see the 2.2 <a href=\"http:\/\/httpd.apache.org\/docs\/2.2\/mod\/mod_expires.html\">mod_expires<\/a> and\n<a href=\"http:\/\/httpd.apache.org\/docs\/2.2\/mod\/mod_headers.html\">mod_headers<\/a>\ndocumentation for more information.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Microsoft IIS<\/h3>\n\n\n\n<p><a href=\"http:\/\/www.microsoft.com\/\">Microsoft<\/a>\u2019s\nInternet Information Server makes it very easy to set headers in a somewhat\nflexible way. Note that this is only possible in version 4 of the server,\nwhich will run only on NT Server.<\/p>\n\n\n\n<p>To specify headers for an area of a site, select it in the\n<code>Administration Tools<\/code> interface, and bring up its properties. After\nselecting the <code>HTTP Headers<\/code> tab, you should see two interesting\nareas; <code>Enable Content Expiration<\/code> and <code>Custom HTTP headers<\/code>.\nThe first should be self-explanatory, and the second can be used to apply\nCache-Control headers.<\/p>\n\n\n\n<p>See the ASP section below for information about setting headers in Active\nServer Pages. It is also possible to set headers from ISAPI modules; refer to\nMSDN for details.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Netscape\/iPlanet Enterprise Server<\/h3>\n\n\n\n<p>As of version 3.6, Enterprise Server does not provide any obvious way to\nset Expires headers. However, it has supported HTTP 1.1 features since version\n3.0. This means that HTTP 1.1 caches (proxy and browser) will be able to take\nadvantage of Cache-Control settings you make.<\/p>\n\n\n\n<p>To use Cache-Control headers, choose <code>Content Management | Cache Control\nDirectives<\/code> in the administration server. Then, using the Resource Picker,\nchoose the directory where you want to set the headers. After setting the\nheaders, click \u2018OK\u2019. For more information, see the <a href=\"http:\/\/www.redhat.com\/docs\/manuals\/ent-server\/\">NES manual<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a>Implementation Notes \u2014 Server-Side\nScripting<\/a><\/h2>\n\n\n\n<p>One thing to keep in mind is that it may be easier to set\nHTTP headers with your Web server rather than in the scripting language. Try\nboth. <\/p>\n\n\n\n<p>Because the emphasis in server-side scripting is on dynamic content, it\ndoesn\u2019t make for very cacheable pages, even when the content could be cached.\nIf your content changes often, but not on every page hit, consider setting a\nCache-Control: max-age header; most users access pages again in a relatively\nshort period of time. For instance, when users hit the \u2018back\u2019 button, if there\nisn\u2019t any validator or freshness information available, they\u2019ll have to wait\nuntil the page is re-downloaded from the server to see it.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">CGI<\/h3>\n\n\n\n<p>CGI scripts are one of the most popular ways to generate content. You can\neasily append HTTP response headers by adding them before you send the body;\nMost CGI implementations already require you to do this for the\n<code>Content-Type<\/code> header. For instance, in Perl;<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">#!\/usr\/bin\/perl\nprint \"Content-type: text\/html\\n\";\nprint \"Expires: Thu, 29 Oct 1998 17:04:19 GMT\\n\";\nprint \"\\n\";\n### the content body follows...<\/pre>\n\n\n\n<p>Since it\u2019s all text, you can easily generate <code>Expires<\/code> and other\ndate-related headers with in-built functions. It\u2019s even easier if you use\n<code>Cache-Control: max-age<\/code>;<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">print \"Cache-Control: max-age=600\\n\";<\/pre>\n\n\n\n<p>This will make the script cacheable for 10 minutes after the request, so\nthat if the user hits the \u2018back\u2019 button, they won\u2019t be resubmitting the\nrequest.<\/p>\n\n\n\n<p>The CGI specification also makes request headers that the client sends\navailable in the environment of the script; each header has \u2018HTTP_\u2019 prepended\nto its name. So, if a client makes an <code>If-Modified-Since<\/code> request, it will show\nup as <code>HTTP_IF_MODIFIED_SINCE<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Server Side Includes<\/h3>\n\n\n\n<p>SSI (often used with the extension .shtml) is one of the first ways that\nWeb publishers were able to get dynamic content into pages. By using special\ntags in the pages, a limited form of in-HTML scripting was available.<\/p>\n\n\n\n<p>Most implementations of SSI do not set validators, and as such are not\ncacheable. However, Apache\u2019s implementation does allow users to specify which\nSSI files can be cached, by setting the group execute permissions on the\nappropriate files, combined with the <code>XbitHack full<\/code> directive. For more\ninformation, see the <a href=\"http:\/\/www.apache.org\/docs\/mod\/mod_include.html\">mod_include\ndocumentation<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">PHP<\/h3>\n\n\n\n<p><a href=\"http:\/\/www.php.net\/\">PHP<\/a> is a\nserver-side scripting language that, when built into the server, can be used\nto embed scripts inside a page\u2019s HTML, much like SSI, but with a far larger\nnumber of options. PHP can be used as a CGI script on any Web server (Unix or\nWindows), or as an Apache module.<\/p>\n\n\n\n<p>By default, representations processed by PHP are not assigned validators,\nand are therefore uncacheable. However, developers can set HTTP headers by\nusing the <code>Header()<\/code> function.<\/p>\n\n\n\n<p>For example, this will create a Cache-Control header, as well as an\nExpires header three days in the future:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;?php\n Header(\"Cache-Control: must-revalidate\");\n\n $offset = 60 * 60 * 24 * 3;\n $ExpStr = \"Expires: \" . gmdate(\"D, d M Y H:i:s\", time() + $offset) . \" GMT\";\n Header($ExpStr);\n?&gt;<\/pre>\n\n\n\n<p>Remember that the <code>Header()<\/code> function MUST come before any other output.<\/p>\n\n\n\n<p>As you can see, you\u2019ll have to create the HTTP date for an <code>Expires<\/code> header\nby hand; PHP doesn\u2019t provide a function to do it for you (although recent\nversions have made it easier; see the <a href=\"http:\/\/php.net\/date\">PHP&#8217;s date documentation<\/a>). Of course, it\u2019s\neasy to set a <code>Cache-Control: max-age header<\/code>, which is just as good for most\nsituations.<\/p>\n\n\n\n<p>For more information, see the <a href=\"http:\/\/www.php.net\/manual\/function.header.php3\">manual entry for\nheader<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Cold Fusion<\/h3>\n\n\n\n<p><a href=\"http:\/\/www.macromedia.com\/software\/coldfusion\/\">Cold Fusion<\/a>, by <a href=\"http:\/\/www.macromedia.com\/\">Macromedia<\/a> is a commercial server-side\nscripting engine, with support for several Web servers on Windows, Linux and\nseveral flavors of Unix.<\/p>\n\n\n\n<p>Cold Fusion makes setting arbitrary HTTP headers relatively easy, with the\n<code><a href=\"http:\/\/livedocs.macromedia.com\/coldfusion\/7\/htmldocs\/00000270.htm\">CFHEADER<\/a><\/code> \ntag. Unfortunately, their example for setting an <code>Expires<\/code> header, as below, is a bit misleading.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;CFHEADER NAME=\"Expires\" VALUE=\"#Now()#\"&gt;<\/pre>\n\n\n\n<p>It doesn\u2019t work like you might think, because the time (in this case, when the request is made)\ndoesn\u2019t get converted to a HTTP-valid date; instead, it just gets printed as\na representation of Cold Fusion\u2019s Date\/Time object. Most clients will either\nignore such a value, or convert it to a default, like January 1, 1970.<\/p>\n\n\n\n<p>However, Cold Fusion does provide a date formatting function that will do the job; \n<code><a href=\"http:\/\/livedocs.macromedia.com\/coldfusion\/7\/htmldocs\/00000483.htm\">GetHttpTimeString<\/a><\/code>. In combination with <code>\n<a href=\"http:\/\/livedocs.macromedia.com\/coldfusion\/7\/htmldocs\/00000437.htm\">DateAdd<\/a><\/code>, it\u2019s easy to set Expires dates; \nhere, we set a header to declare that representations of the page expire in one month;<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;cfheader name=\"Expires\" \n  value=\"#GetHttpTimeString(DateAdd('m', 1, Now()))#\"&gt;<\/pre>\n\n\n\n<p>You can also use the <code>CFHEADER<\/code> tag to set <code>Cache-Control: max-age<\/code> and other headers.<\/p>\n\n\n\n<p>Remember that Web server headers are passed through in some deployments of Cold Fusion\n(such as CGI); check yours to determine whether you can use\nthis to your advantage, by setting headers on the server instead of in Cold\nFusion.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">ASP and ASP.NET<\/h3>\n\n\n\n<p>When setting HTTP headers from ASPs, make sure you either\nplace the Response method calls before any HTML generation, or use\n<code>Response.Buffer<\/code> to buffer the output. Also, note that some versions of IIS set\na <code>Cache-Control: private<\/code> header on ASPs by default, and must be declared public\nto be cacheable by shared caches.<\/p>\n\n\n\n<p>Active Server Pages, built into IIS and also available for other Web\nservers, also allows you to set HTTP headers. For instance, to set an expiry\ntime, you can use the properties of the <code>Response<\/code> object;<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;% Response.Expires=1440 %&gt;<\/pre>\n\n\n\n<p>specifying the number of minutes from the request to expire the\nrepresentation. <code>Cache-Control<\/code> headers can be added like this:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;% Response.CacheControl=\"public\" %&gt;<\/pre>\n\n\n\n<p>In ASP.NET, <code>Response.Expires<\/code> is deprecated; the proper way to set cache-related\nheaders is with <code>Response.Cache<\/code>;<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Response.Cache.SetExpires ( DateTime.Now.AddMinutes ( 60 ) ) ;\nResponse.Cache.SetCacheability ( HttpCacheability.Public ) ;<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><a>References and Further Information<\/a><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><a href=\"http:\/\/www.ietf.org\/rfc\/rfc2616.txt\">HTTP 1.1 Specification<\/a><\/h3>\n\n\n\n<p>The HTTP 1.1 spec has many extensions for making pages cacheable,\nand is the authoritative guide to implementing the protocol. See sections 13,\n14.9, 14.21, and 14.25.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><a href=\"http:\/\/www.web-caching.com\/\">Web-Caching.com<\/a><\/h3>\n\n\n\n<p>An excellent introduction to caching concepts, with links to other online\nresources.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><a href=\"http:\/\/www.goldmark.org\/netrants\/webstats\/\">On Interpreting\nAccess Statistics<\/a><\/h3>\n\n\n\n<p>Jeff Goldberg\u2019s informative rant on why you shouldn\u2019t rely on access\nstatistics and hit counters.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><a href=\"https:\/\/redbot.org\/\">REDbot<\/a><\/h3>\n\n\n\n<p>Examines HTTP resources to determine how they will interact with Web caches, and generally how well they use the protocol.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a>About This Document<\/a><\/h2>\n\n\n\n<p>This document is Copyright \u00a9 1998-2013 Mark Nottingham &lt;<a href=\"mailto:mnot@mnot.net\">mnot@mnot.net<\/a>&gt;.\n\n\n\nThis work is licensed under a <a href=\"http:\/\/creativecommons.org\/licenses\/by-nc-nd\/3.0\/\">Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License<\/a>.\n<\/p>\n\n\n\n<p>All trademarks within are property of their respective holders.<\/p>\n\n\n\n<p>Although the author believes the contents to be accurate at the time of\npublication, no liability is assumed for them, their application or any\nconsequences thereof. If any misrepresentations, errors or other need for\nclarification is found, please contact the author immediately.<\/p>\n\n\n\n<p>The latest revision of this document can always be obtained from <a href=\"https:\/\/www.mnot.net\/cache_docs\/\">https:\/\/www.mnot.net\/cache_docs\/<\/a><\/p>\n\n\n\n<p>Translations are available in:\n\t<a href=\"http:\/\/www.chedong.com\/tech\/cache_docs.html\">Chinese<\/a>,\n\t<a href=\"http:\/\/www.jakpsatweb.cz\/clanky\/caching-tutorial-czech-translation.html\">Czech<\/a>,\n\t<a href=\"http:\/\/www.2uo.de\/caching-tutorial\/\">German<\/a>, and\n\t<a href=\"https:\/\/www.mnot.net\/cache_docs\/index.fr.html\">French<\/a>.\n<\/p>\n\n\n\n<p>25 October, 2017<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"http:\/\/creativecommons.org\/licenses\/by-nc-nd\/3.0\/\"><img decoding=\"async\" src=\"https:\/\/www.mnot.net\/lib\/by-nc-nd.png\" alt=\"Creative Commons License\"\/><\/a><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>Every once and a while you run into an article on the internet that&#8217;s just too good to just make a bookmark (or some similar technique that allows you to store the URL to that previous piece of useful information). I have been on the internet long enough to know that no matter how good &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/blog.remyblom.nl\/?p=207\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Article Archive: Mark Nottingham &#8211; Caching Tutorial&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[17],"tags":[],"class_list":["post-207","post","type-post","status-publish","format-standard","hentry","category-article-archive"],"_links":{"self":[{"href":"https:\/\/blog.remyblom.nl\/index.php?rest_route=\/wp\/v2\/posts\/207","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.remyblom.nl\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.remyblom.nl\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.remyblom.nl\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.remyblom.nl\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=207"}],"version-history":[{"count":2,"href":"https:\/\/blog.remyblom.nl\/index.php?rest_route=\/wp\/v2\/posts\/207\/revisions"}],"predecessor-version":[{"id":219,"href":"https:\/\/blog.remyblom.nl\/index.php?rest_route=\/wp\/v2\/posts\/207\/revisions\/219"}],"wp:attachment":[{"href":"https:\/\/blog.remyblom.nl\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=207"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.remyblom.nl\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=207"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.remyblom.nl\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=207"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}