{"id":22385328,"url":"https://github.com/acoshift/http-caching-guide","last_synced_at":"2026-02-24T08:33:26.723Z","repository":{"id":73779633,"uuid":"151031726","full_name":"acoshift/http-caching-guide","owner":"acoshift","description":" Short Guide for Modern HTTP Caching","archived":false,"fork":false,"pushed_at":"2018-11-04T16:04:47.000Z","size":16,"stargazers_count":26,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-01T02:20:24.201Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/acoshift.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-10-01T03:22:20.000Z","updated_at":"2021-09-11T02:40:38.000Z","dependencies_parsed_at":null,"dependency_job_id":"87baaf86-2c3c-400c-a149-d40c0313bd26","html_url":"https://github.com/acoshift/http-caching-guide","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acoshift%2Fhttp-caching-guide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acoshift%2Fhttp-caching-guide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acoshift%2Fhttp-caching-guide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acoshift%2Fhttp-caching-guide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/acoshift","download_url":"https://codeload.github.com/acoshift/http-caching-guide/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245728923,"owners_count":20662771,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-05T01:22:53.113Z","updated_at":"2025-10-29T20:50:31.324Z","avatar_url":"https://github.com/acoshift.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# http-caching-guide\n\nShort Guide for Modern HTTP Caching\n\n## Cache-Control\n\nControls how responses should be cached.\n\n### Directives\n\n#### no-store\n\nDo NOT cache the response.\n\n```text\nCache-Control: no-store\n```\n\n#### no-cache\n\nRevalidate cache before use (see [Cache Revalidation](#cache-revalidation) for more info).\n\nBrowser will send a request to validate if the cached data can be used.\nThis can reduce bandwidth but still requires a roundtrip to validate the cached response.\n\n```text\nCache-Control: no-cache\n```\n\n#### max-age\n\nSet the maximum amount of time (in seconds) that the resource will be cached.\n\nBrowser will use cached response without sending any request (from disk cache).\n\n```text\nCache-Control: max-age=3600\n```\n\n#### public\n\nResponse can be cached in shared cache (Reverse Proxy, CDN, etc.)\n\n\u003e 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.\n\n```text\nCache-Control: max-age=3600\n```\n\nequals to\n\n```text\nCache-Control: public, max-age=3600\n```\n\n#### private\n\nResponse is intended for one user CAN NOT be cached in shared cache.\n\n```text\nCache-Control: private, max-age=3600\n```\n\n#### immutable\n\nPrevents cache revalidation when clicking Refresh button (which normally causes revalidation on all resources).\n\n```text\nCache-Control: max-age=31536000, immutable\n```\n\n### Cache-Control Summary\n\n#### Static files with unique file name (ex. with hash in file name)\n\n```text\nCache-Control: public, max-age=31536000, immutable\n```\n\n#### MPA (generate HTML)\n\n```text\nCache-Control: no-store\n```\n\nWhy not `no-cache` ?\n\nBecause MPA server usually send `Set-Cookie` to rolling cookie expiration time,\nand `304 Not Modified` should not send other header than cache information.\n[RFC 7372 - Section 4.1](https://tools.ietf.org/html/rfc7232#section-4.1)\n\n#### Web Pages (static HTML)\n\n```text\nCache-Control: no-cache\n```\n\n#### SPA index.html\n\n```text\nCache-Control: no-cache\n```\n\n## Cache Revalidation\n\n\u003e Last-Modified and ETag can be used for conditional requests, but not included in this article.\n\nWhen `no-cache` directive is used,\nat least one of these headers must be set to trigger cache revalidation.\n\n### Last-Modified\n\nTime that the resource has been last modified.\n\nNormally used for static files.\n\n```text\nLast-Modified: \u003cday-name\u003e, \u003cday\u003e \u003cmonth\u003e \u003cyear\u003e \u003chour\u003e:\u003cminute\u003e:\u003csecond\u003e GMT\n```\n\nex.\n\n```text\nLast-Modified: Tue, 18 Sep 2018 11:12:03 GMT\n```\n\nIf browser has cached the response, it will send a request to validate cache with\n\n```text\nIf-Modified-Since: Tue, 18 Sep 2018 11:12:03 GMT\n```\n\nIf the server has a newer file, then it sends the new file contents and new `Last-Modified` header\n\n```text\nHTTP/1.1 200 OK\nDate: Tue, 18 Sep 2018 22:00:00 GMT\nContent-Type: text/html; charset=utf-8\nContent-Length: 7\nLast-Modified: Tue, 18 Sep 2018 20:00:00 GMT\n\nContent\n```\n\nIf file has not been modified since, then it returns 304 status code without body.\n\n```text\nHTTP/1.1 304 Not Modified\nDate: Tue, 18 Sep 2018 22:00:00 GMT\n\n```\n\n### ETag\n\nLike `Last-Modified` but `ETag` has more accuracy.\n\nUsed when server does not know resource's last modified date or when a resource could change more than once in a second.\n\nETag is like a fingerprint of resource, and a server must generate a new ETag when the resource’s contents changed.\n\nETag can be generated using hash of response body, UUID, or anything that results in a unique string when resource contents changed.\n\nBrowser will send `If-None-Match` to server to validate cache.\n\nETag has `Weak` and `Strong` validator.\n\n#### Weak\n\nA weak ETag is used to validate only **body** of response, when response body changed, ETag value will change.\n\n```text\nETag: W/\"value\"\n```\n\nExample\n\n```text\nETag: W/\"abcde\"\n```\n\n#### Strong\n\nA strong ETag is used to validate **both** response **body** and **headers**.\n\nEven if body is not changed but header is changed, a strong ETag must be changed to a new value.\n\nUse-cases: To cache byte-range response.\n\n```text\nETag: \"value\"\n```\n\nExample\n\n```text\nETag: \"abc1\" # for gzip\nETag: \"abc2\" # for br\n```\n\n#### Comparison\n\n| ETag 1 | ETag 2 | Strong Comparison | Weak Comparison |\n|--------|--------|-------------------|-----------------|\n| W/\"1\"  | W/\"1\"  | no match          | match           |\n| W/\"1\"  | W/\"2\"  | no match          | no match        |\n| W/\"1\"  | \"1\"    | no match          | match           |\n| \"1\"    | \"1\"    | match             | match           |\n\n(from [RFC-7232](https://tools.ietf.org/html/rfc7232#section-2.3.2))\n\n#### ETag Summary\n\nUse **Strong** ETag unless you know what you're doing 😁\n\nEx. you don't care about\n\n- `Content-Encoding`\n- `Content-Language`\n- `Range`\n- `Set-Cookie`\n- `Vary`\n- etc.\n\nthen you can use **Weak** ETag\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Facoshift%2Fhttp-caching-guide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Facoshift%2Fhttp-caching-guide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Facoshift%2Fhttp-caching-guide/lists"}