Settle on a five-minute public cache and drop the purge rationale

The edge-purge integration was reverted in the previous commit; this
finishes the job. Public pages, the feed and /issues.json now say
`public, max-age=300` for browsers and shared caches alike, the code
comments and README explain why the longer s-maxage plus purge design
was dropped, and the rollout runbook loses its purge step, marks the
HTML cache rule optional, and warns to keep the record DNS-only until
the Universal SSL certificate is active.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Va5eMEmWEnjMXBsBob5FDW
This commit is contained in:
2026-09-04 22:04:07 +00:00
co-authored by Claude Fable 5.1
parent de332cd4b3
commit f634f06bf1
6 changed files with 57 additions and 99 deletions
+1 -1
View File
@@ -304,7 +304,7 @@ async fn handle_issues_json(State(state): State<AppState>) -> Response {
})
.collect();
match serde_json::to_string_pretty(&issues) {
// Only publishing changes this, and publishing purges the edge (§3.12).
// Only publishing changes this; five minutes of staleness is fine (§3.12).
Ok(body) => (
StatusCode::OK,
[
+4 -4
View File
@@ -1671,7 +1671,7 @@ mod tests {
assert_eq!(issue.status(), StatusCode::OK);
assert_eq!(
issue.headers().get(header::CACHE_CONTROL).unwrap(),
"public, max-age=300, s-maxage=86400"
"public, max-age=300"
);
let html = String::from_utf8(
to_bytes(issue.into_body(), 1024 * 1024)
@@ -1705,7 +1705,7 @@ mod tests {
assert_eq!(archive.status(), StatusCode::OK);
assert_eq!(
archive.headers().get(header::CACHE_CONTROL).unwrap(),
"public, max-age=300, s-maxage=86400"
"public, max-age=300"
);
let feed = app
@@ -1724,7 +1724,7 @@ mod tests {
);
assert_eq!(
feed.headers().get(header::CACHE_CONTROL).unwrap(),
"public, max-age=300, s-maxage=86400"
"public, max-age=300"
);
let feed = String::from_utf8(
to_bytes(feed.into_body(), 1024 * 1024)
@@ -1777,7 +1777,7 @@ mod tests {
.unwrap();
assert_eq!(
reports.headers().get(header::CACHE_CONTROL).unwrap(),
"public, max-age=300, s-maxage=86400"
"public, max-age=300"
);
let reports =
String::from_utf8(to_bytes(reports.into_body(), 4096).await.unwrap().to_vec()).unwrap();
+1 -1
View File
@@ -771,7 +771,7 @@ mod tests {
assert!(anonymous.headers().get(header::SET_COOKIE).is_none());
assert_eq!(
anonymous.headers().get(header::CACHE_CONTROL).unwrap(),
"public, max-age=300, s-maxage=86400"
"public, max-age=300"
);
assert_eq!(
anonymous.headers().get("speculation-rules").unwrap(),
+8 -8
View File
@@ -360,14 +360,14 @@ pub async fn robots() -> Response {
/// `Cache-Control` for an anonymous public page (§3.12).
///
/// The two ages are aimed at two different caches. `max-age=300` is the
/// browser's: a reader who leaves a tab open revalidates within five minutes of
/// a new issue landing. `s-maxage=86400` is the CDN's: an issue changes once a
/// day, so the edge should be allowed to answer for a day rather than asking
/// the origin every five minutes. That long edge life is only safe because
/// [`crate::cdn::purge_all`] runs right after a publish; without the purge the
/// edge would keep yesterday's paper for its whole day.
pub const PUBLIC_CACHE: &str = "public, max-age=300, s-maxage=86400";
/// Five minutes, for browsers and shared caches alike. An issue changes once a
/// day, so the only staleness this allows is the few minutes after a publish,
/// and the operator never sees even that: signed-in requests get
/// [`PRIVATE_CACHE`]. A longer shared-cache age was tried and rejected: it
/// needs a purge call after every publish to stay correct, and at this site's
/// traffic an edge rarely keeps a page long enough for the extra age to pay
/// for that machinery.
pub const PUBLIC_CACHE: &str = "public, max-age=300";
/// `Cache-Control` for anything a signed-in reader sees, and for every
/// authenticated download. `private` keeps it out of shared caches even if a