└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # http-caching-guide 2 | 3 | Short Guide for Modern HTTP Caching 4 | 5 | ## Cache-Control 6 | 7 | Controls how responses should be cached. 8 | 9 | ### Directives 10 | 11 | #### no-store 12 | 13 | Do NOT cache the response. 14 | 15 | ```text 16 | Cache-Control: no-store 17 | ``` 18 | 19 | #### no-cache 20 | 21 | Revalidate cache before use (see [Cache Revalidation](#cache-revalidation) for more info). 22 | 23 | Browser will send a request to validate if the cached data can be used. 24 | This can reduce bandwidth but still requires a roundtrip to validate the cached response. 25 | 26 | ```text 27 | Cache-Control: no-cache 28 | ``` 29 | 30 | #### max-age 31 | 32 | Set the maximum amount of time (in seconds) that the resource will be cached. 33 | 34 | Browser will use cached response without sending any request (from disk cache). 35 | 36 | ```text 37 | Cache-Control: max-age=3600 38 | ``` 39 | 40 | #### public 41 | 42 | Response can be cached in shared cache (Reverse Proxy, CDN, etc.) 43 | 44 | > Note: most of the time, you don’t need to use `public` as it can be inferred by other caching directives, such as `max-age`. But check with CDN document first. 45 | 46 | ```text 47 | Cache-Control: max-age=3600 48 | ``` 49 | 50 | equals to 51 | 52 | ```text 53 | Cache-Control: public, max-age=3600 54 | ``` 55 | 56 | #### private 57 | 58 | Response is intended for one user CAN NOT be cached in shared cache. 59 | 60 | ```text 61 | Cache-Control: private, max-age=3600 62 | ``` 63 | 64 | #### immutable 65 | 66 | Prevents cache revalidation when clicking Refresh button (which normally causes revalidation on all resources). 67 | 68 | ```text 69 | Cache-Control: max-age=31536000, immutable 70 | ``` 71 | 72 | ### Cache-Control Summary 73 | 74 | #### Static files with unique file name (ex. with hash in file name) 75 | 76 | ```text 77 | Cache-Control: public, max-age=31536000, immutable 78 | ``` 79 | 80 | #### MPA (generate HTML) 81 | 82 | ```text 83 | Cache-Control: no-store 84 | ``` 85 | 86 | Why not `no-cache` ? 87 | 88 | Because MPA server usually send `Set-Cookie` to rolling cookie expiration time, 89 | and `304 Not Modified` should not send other header than cache information. 90 | [RFC 7372 - Section 4.1](https://tools.ietf.org/html/rfc7232#section-4.1) 91 | 92 | #### Web Pages (static HTML) 93 | 94 | ```text 95 | Cache-Control: no-cache 96 | ``` 97 | 98 | #### SPA index.html 99 | 100 | ```text 101 | Cache-Control: no-cache 102 | ``` 103 | 104 | ## Cache Revalidation 105 | 106 | > Last-Modified and ETag can be used for conditional requests, but not included in this article. 107 | 108 | When `no-cache` directive is used, 109 | at least one of these headers must be set to trigger cache revalidation. 110 | 111 | ### Last-Modified 112 | 113 | Time that the resource has been last modified. 114 | 115 | Normally used for static files. 116 | 117 | ```text 118 | Last-Modified: , :: GMT 119 | ``` 120 | 121 | ex. 122 | 123 | ```text 124 | Last-Modified: Tue, 18 Sep 2018 11:12:03 GMT 125 | ``` 126 | 127 | If browser has cached the response, it will send a request to validate cache with 128 | 129 | ```text 130 | If-Modified-Since: Tue, 18 Sep 2018 11:12:03 GMT 131 | ``` 132 | 133 | If the server has a newer file, then it sends the new file contents and new `Last-Modified` header 134 | 135 | ```text 136 | HTTP/1.1 200 OK 137 | Date: Tue, 18 Sep 2018 22:00:00 GMT 138 | Content-Type: text/html; charset=utf-8 139 | Content-Length: 7 140 | Last-Modified: Tue, 18 Sep 2018 20:00:00 GMT 141 | 142 | Content 143 | ``` 144 | 145 | If file has not been modified since, then it returns 304 status code without body. 146 | 147 | ```text 148 | HTTP/1.1 304 Not Modified 149 | Date: Tue, 18 Sep 2018 22:00:00 GMT 150 | 151 | ``` 152 | 153 | ### ETag 154 | 155 | Like `Last-Modified` but `ETag` has more accuracy. 156 | 157 | Used when server does not know resource's last modified date or when a resource could change more than once in a second. 158 | 159 | ETag is like a fingerprint of resource, and a server must generate a new ETag when the resource’s contents changed. 160 | 161 | ETag can be generated using hash of response body, UUID, or anything that results in a unique string when resource contents changed. 162 | 163 | Browser will send `If-None-Match` to server to validate cache. 164 | 165 | ETag has `Weak` and `Strong` validator. 166 | 167 | #### Weak 168 | 169 | A weak ETag is used to validate only **body** of response, when response body changed, ETag value will change. 170 | 171 | ```text 172 | ETag: W/"value" 173 | ``` 174 | 175 | Example 176 | 177 | ```text 178 | ETag: W/"abcde" 179 | ``` 180 | 181 | #### Strong 182 | 183 | A strong ETag is used to validate **both** response **body** and **headers**. 184 | 185 | Even if body is not changed but header is changed, a strong ETag must be changed to a new value. 186 | 187 | Use-cases: To cache byte-range response. 188 | 189 | ```text 190 | ETag: "value" 191 | ``` 192 | 193 | Example 194 | 195 | ```text 196 | ETag: "abc1" # for gzip 197 | ETag: "abc2" # for br 198 | ``` 199 | 200 | #### Comparison 201 | 202 | | ETag 1 | ETag 2 | Strong Comparison | Weak Comparison | 203 | |--------|--------|-------------------|-----------------| 204 | | W/"1" | W/"1" | no match | match | 205 | | W/"1" | W/"2" | no match | no match | 206 | | W/"1" | "1" | no match | match | 207 | | "1" | "1" | match | match | 208 | 209 | (from [RFC-7232](https://tools.ietf.org/html/rfc7232#section-2.3.2)) 210 | 211 | #### ETag Summary 212 | 213 | Use **Strong** ETag unless you know what you're doing 😁 214 | 215 | Ex. you don't care about 216 | 217 | - `Content-Encoding` 218 | - `Content-Language` 219 | - `Range` 220 | - `Set-Cookie` 221 | - `Vary` 222 | - etc. 223 | 224 | then you can use **Weak** ETag 225 | --------------------------------------------------------------------------------