Direct answer: we requested the first CSS and first external JS on each of 25 equipment-company homepages, 50 resource requests in total: 43 returned 200, two 404, one was unreachable and four were not declared. Of the 43 200 responses, 23 had a positive max-age (cacheable), 18 had no Cache-Control and two had max-age=0. Among the 23 cacheable files, 12 used 14 days, four 12 hours and three one year; the median was 14 days.
“No Cache-Control” does not mean “no caching”: a browser may cache heuristically, but the policy is not controlled. Among the 43 200 responses, CSS 20 and JS 19 still had Last-Modified, and CSS 13 and JS 11 had ETag; these are validators, not a substitute for an explicit cache policy.
Method: first CSS/JS, HEAD first, GET fallback
We reused the 25 cached homepages from study 4 and took each site's first declared stylesheet and first external script (inline excluded). On 2026-11-07 at 22:41 each received one request, HEAD first with Accept-Encoding: gzip, br and Referer; when HEAD returned 400/403/405/501 or the connection failed, GET was used. We recorded status, method, Cache-Control, ETag, Last-Modified, Vary, Content-Encoding, Content-Type, length, Age, Server and CDN markers.
Cache policy classification: cacheable = max-age > 0; no-store / private / no-cache = private/no-cache; max-age=0 separately; no header = missing. No browser was driven, and CDN edge hits were not verified.
| Item | CSS | JS |
|---|---|---|
| Requests | 25 | 25 |
| 200 | 21 | 22 |
| 404 | 1 | 1 |
| Unreachable | 0 | 1 |
| Not declared | 3 | 1 |
| Cacheable among 200 | 12 | 11 |
| No Cache-Control among 200 | 9 | 9 |
| max-age=0 among 200 | 0 | 2 |
Result: 18/43 have no explicit cache policy
| Page | CSS policy / max-age | JS policy / max-age |
|---|---|---|
| E01 | missing / — | max-age=0 |
| E02 | cacheable / 12h | cacheable / 12h |
| E03 | cacheable / 14d | cacheable / 14d |
| E04 | cacheable / 1y | cacheable / 1y |
| E06 | cacheable / 14d | cacheable / 14d |
| E08 | missing / — | missing / — |
| E09 | cacheable / 14d | cacheable / 14d |
| E11 | cacheable / 12h | unreachable |
| E12 | cacheable / 12h | max-age=0 |
| E13 | missing / — | missing / — |
| E14 | cacheable / 14d | cacheable / 14d |
| E16 | missing / — | missing / — |
| E17 | missing / — | missing / — |
| E18 | not declared | not declared |
| E19 | missing / — | missing / — |
| E20 | not declared | missing / — |
| E22 | cacheable / 14d | cacheable / 14d |
| E23 | cacheable / 14d | cacheable / 14d |
| E24 | private / 404 | private / 404 |
| E25 | not declared | cacheable / 1y |
| E26 | missing / — | missing / — |
| E27 | missing / — | missing / — |
| E28 | cacheable / 1h | cacheable / 30d |
| E29 | cacheable / 5m | cacheable / 5m |
| E30 | missing / — | missing / — |
Among the 23 cacheable files, max-age was 14 days for 12, 12 hours for four, one year for three, 5 minutes for two, 1 hour for one and 30 days for one; the median was 14 days. E01 and E12 JS use max-age=0, requiring revalidation on every use; E24's two 404s still carry private/no-cache, showing that error responses also have a policy.
Three ways to misread it
- No Cache-Control is not no caching. A browser may cache heuristically, but the policy is uncontrolled; 18/43 are in this state, not “all uncached”.
- ETag / Last-Modified are not an explicit max-age. Of the 43 200s, 39 had Last-Modified and 24 had ETag, but a validator answers “did it change”, not “how long may I skip asking”.
- Record HEAD and GET separately. E13 CSS and E19 JS returned 502/failure to HEAD but 200 to GET; E11's external JS was unreachable both times. Without the method, a tool failure looks like a server policy.
What this means for acceptance
- Set explicit Cache-Control on CSS/JS. At minimum separate HTML from fingerprinted static assets; 18/43 had no such header here.
- Version URLs before long caching. One-year max-age fits only hash-named files; E04 uses one year for both resources.
- Confirm max-age=0 is necessary. E01/E12 JS require revalidation every time; unchanged files pay an extra round trip.
- Give error responses a policy too. E24's 404s carry private/no-cache; do not treat a 404 as a cacheable static asset.
- Record validators separately from max-age. ETag/Last-Modified cannot replace Cache-Control; acceptance needs both columns.
Limits
- 25-site pilot: the same homepage cache; not the industry and not all 134 CSS / 204 JS.
- First resource only: no long tail, hashed build output, CDN edge hit or real browser cache.
- HEAD first: three resources needed GET and one remained unreachable; methods are recorded row by row, and failures are not treated as policy.
- One snapshot: 2026-11-07 22:41; no redesign or regional recheck.
- Headers only: content correctness, render blocking and TTFB are not judged.
Data and reproduction
Anonymised data: equipment-homepage-css-js-cache-headers-2026.csv (50 rows × 20 columns: kind, found, external, status, method, Cache-Control, policy, max-age, ETag, Last-Modified, Vary, encoding, Content-Type, length, Age, Server, CDN, error, time). In-repo copy: docs/research/2026-11-homepage-css-js-cache-headers.csv.
Collection: Python urllib, HEAD first and GET fallback, with Accept-Encoding: gzip, br and Referer; the policy classifier is evaluate() from scripts/cache_compression_probe.py. Site names and full URLs are not published.
Related: the homepage resource inventory pilot, the homepage HTML compression pilot, and the homepage cache and compression pilot.
Sources and definitions
- Sample: 25 homepage caches from study 4 (2026-10-31); the first declared CSS and first external JS per site.
- Requests: 2026-11-07 22:41, HEAD first with GET fallback, 25 sites × 2 kinds, 50 requests.
- Rule: max-age > 0 cacheable; no-store/private/no-cache private; max-age=0 separate; missing header = missing.
Frequently asked questions
Does no Cache-Control mean no caching?
No. A browser may cache heuristically, but the policy is uncontrolled; this study records headers only and does not test the browser cache.
Why is a 404 listed as private/no-cache?
E24's CSS/JS returned 404 but still carried private/no-cache; it is not a usable static asset, and error responses also need a policy.
What does max-age=0 mean?
The resource must be revalidated before each use; E01 and E12 JS are in this state, and revalidation can add a round trip.
Can ETag / Last-Modified replace Cache-Control?
No. A validator answers whether the resource changed; max-age says how long it may be reused without asking. Of 43 200s, 39 had Last-Modified and 24 had ETag.
Why do HEAD and GET differ?
E13 CSS and E19 JS returned 502/failure to HEAD but 200 to GET; E11's external JS was unreachable both times. The method is recorded row by row.
Can the data be reproduced?
Yes. The 50-row × 20-column anonymised CSV is in the repository and records time, method, status, Cache-Control, max-age, ETag, Last-Modified, Vary, encoding, length and error; site names and full URLs are not published.
Related: the first homepage image cache pilot.
Related: the homepage canonical and og:url pilot.