Skip to content

Commit 795cde1

Browse files
committed
feat: Add Safari Cache-Control Article
1 parent b619ad1 commit 795cde1

1 file changed

Lines changed: 147 additions & 0 deletions

File tree

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
---
2+
layout: post
3+
title: "When Safari’s ‘Ignore Cache’ Ignored Cache-Control"
4+
date: 2026-08-17 16:11:40
5+
categories: Web Development
6+
main: ""
7+
meta: "Safari Technology Preview 250 fixed a subtle Web Inspector bug that overwrote page-authored Cache-Control request headers when the resource cache was disabled."
8+
---
9+
10+
As you know, I’m a little bit obsessed with caching. I’ve written about
11+
[`Cache-Control`](/2019/03/cache-control-for-civilians/), [how much cache
12+
coverage sites leave on the table](/2024/08/cache-grab-how-much-are-you-leaving-on-the-table/),
13+
[better reuse with
14+
`No-Vary-Search`](/2026/05/better-browser-caching-with-no-vary-search/), and even
15+
[why we have a `Cache-Control` _request_
16+
header](/2025/03/why-do-we-have-a-cache-control-request-header/). That is why
17+
this otherwise rather vague line in the [Safari Technology Preview 250 release
18+
notes](https://webkit.org/blog/18191/release-notes-for-safari-technology-preview-250/)
19+
caught my eye:
20+
21+
> Fixed disabling the resource cache overwriting a `Cache-Control` header
22+
> already set by the page.
23+
24+
At first glance, that almost sounds like the feature working as intended: of
25+
course disabling cache has to override caching somehow. But disabling WebKit’s
26+
local resource cache and replacing a header that the page deliberately added to
27+
its request are two very different things.
28+
29+
The release note piqued my interest, so I dug into [the underlying WebKit
30+
change](https://github.com/WebKit/WebKit/commit/96437189e7eecba977fe0a452b7e35fecc6ea776).
31+
The bug is small, specific, and absolutely fascinating.
32+
33+
{% include promo.html %}
34+
35+
## Web Inspector Was Changing the Outgoing Request
36+
37+
Safari Web Inspector’s [<cite>Ignore
38+
Cache</cite>](https://webkit.org/web-inspector/network-tab/#controlling-resource-caching)
39+
toggle tells WebKit not to use cached resources for future requests while Web
40+
Inspector is open. That is a useful development convenience, particularly when
41+
you need to know that you’re looking at the latest copy of a resource.
42+
43+
Before this fix, WebKit did two things when that toggle was active:
44+
45+
* it set an internal policy that bypassed its resource cache; and
46+
* it set the outgoing request’s `Cache-Control` and `Pragma` headers to
47+
`no-cache`.
48+
49+
The first behaviour is exactly what we asked for. The second is where things got
50+
interesting: WebKit _set_ those headers even if the page had already supplied
51+
its own. The debugging setup didn’t just change whether Safari could reuse
52+
a local response; it changed the request that the application sent.
53+
54+
## This Is a `Cache-Control` Request Header
55+
56+
We tend to think of `Cache-Control` as a response header:
57+
58+
```http
59+
HTTP/2 200
60+
Cache-Control: max-age=31536000, immutable
61+
```
62+
63+
That is the origin telling browsers and other caches how they may store and
64+
reuse its response. But, as [we’ve looked at
65+
before](/2025/03/why-do-we-have-a-cache-control-request-header/),
66+
`Cache-Control` can also appear on a _request_, where the client tells caches
67+
what kind of stored response it is prepared to accept.
68+
69+
A page can add one itself:
70+
71+
```js
72+
fetch('/data.json', {
73+
headers: {
74+
'Cache-Control': 'max-age=12345'
75+
}
76+
});
77+
```
78+
79+
That is almost the exact case in WebKit’s new regression test. With caching
80+
enabled, the server receives `max-age=12345`. With caching disabled and no
81+
page-authored header, it receives WebKit’s `no-cache`. Crucially, with caching
82+
disabled _and_ the page setting `max-age=12345`, the server should still receive
83+
`max-age=12345`.
84+
85+
Before the fix, it received `no-cache` instead. The value that the page asked
86+
for never reached the network.
87+
88+
## Cache Bypass and Request Semantics Are Now Separate
89+
90+
The fix is pleasingly small. When Web Inspector has disabled caching, WebKit now
91+
adds its `no-cache` headers only if the request doesn’t already contain them. It
92+
still applies its internal `ReloadIgnoringCacheData` policy, so the local
93+
resource cache remains bypassed either way.
94+
95+
In other words:
96+
97+
```text
98+
Page-authored request header: preserved
99+
Web Inspector cache bypass: still applied
100+
```
101+
102+
This is the distinction that the release note hides. Web Inspector can ignore
103+
its local cache without rewriting the application’s request to achieve it.
104+
105+
This also wasn’t a general Safari caching failure. The affected condition was
106+
much narrower: Web Inspector had disabled the resource cache, and the request
107+
already contained a page-authored `Cache-Control` header. The release note
108+
doesn’t establish which shipping Safari versions contained the old behaviour,
109+
only that Safari Technology Preview 250 contains the fix.
110+
111+
## How This Could Trip You Up
112+
113+
Changing `max-age=12345` to `no-cache` is not cosmetic. A shared cache, CDN, or
114+
origin that receives the request may respond differently because `no-cache`
115+
requires a stored response to be successfully validated before reuse. The tool
116+
that was meant to help inspect the request had changed the request’s caching
117+
semantics before it reached the network.
118+
119+
That could lead to some wonderfully confusing investigations. Server logs might
120+
show a `no-cache` header that the application never sent. An endpoint might
121+
behave differently only while Web Inspector is open and <cite>Ignore
122+
Cache</cite> is active. Or a supposed warm-cache test might make every request
123+
look as though the user had explicitly demanded revalidation.
124+
125+
None of those observations would be false, exactly; they would be true only
126+
under the debugging conditions that produced them.
127+
128+
For caching work, I keep the conditions separate:
129+
130+
* **Cold cache:** clear the relevant data once, then load with caching enabled.
131+
* **Warm cache:** load or revisit normally with caching enabled.
132+
* **Cache disabled:** treat this as a separate diagnostic mode, not a warm-cache
133+
test.
134+
135+
It is also worth recording the browser version, whether Web Inspector was open,
136+
and whether <cite>Ignore Cache</cite> was active. If the request headers
137+
themselves are in question, compare what Web Inspector shows with CDN/origin
138+
logs or another capture outside that setup.
139+
140+
This is a tiny browser bug with a tiny fix, not a particularly harmful caching
141+
failure. But it is a lovely bit of trivia about how developer tools work: before
142+
the fix included in Safari Technology Preview 250, asking Web Inspector to
143+
ignore its own cache could also make it ignore what the page had already said
144+
about caching.
145+
146+
That is exactly the kind of distinction that keeps caching interesting long
147+
after any sensible person would have moved on.

0 commit comments

Comments
 (0)