Chapter 8
Caching and Conditional Requests
May I Reuse This Response?
Chapter 7 left the reading GET /books every five seconds. The books change a few times a day, so almost every reply repeats the last one byte for byte. The server builds the same
The four directives
That header is Cache-Control. Its cache-control: max-age=60, private. A directive is a name, sometimes followed by = and a value. Four of them cover most of what servers say:
| Directive | What a client may do with a copy it kept |
|---|---|
max-age=N | reuse it without asking until it is N seconds old; after that it is stale |
no-cache | keep it, but ask the server before every reuse |
no-store | not keep it at all |
private | a cache that serves one user may keep it; a shared cache that serves many users may not |
A cache is anything that keeps responses in order to reuse them. Your client gets one in Lesson 5, and it serves one user: whoever runs it. A shared cache sits between many clients and a server, for example a proxy run by a company, and hands one stored response to everyone who asks for the same private exists: a reply that holds one person’s data must not be handed to the next person. This course runs no shared cache; you only need to recognize the word.
no-cache is the directive whose name misleads. It does not mean “do not cache”. It means “you may keep it, but check with me before you use it”. Checking costs little, as Lesson 4 shows, because the server can answer “still the same” without sending the body again. no-store is the one that means “do not keep it”, and a server sends it on replies that should never sit in a cache, such as a reply that names who is logged in. Lesson 6 puts it on exactly those replies.
Directive names are case-insensitive (RFC 9111, section 5.2), so Max-Age=60 means the same as max-age=60. A reply may also carry more than one cache-control line, and together they make one list. You will meet other directives in real replies, such as public, must-revalidate and s-maxage; this course does not use them.
Reading the header
Reading a Cache-Control value is a split at commas, the way Chapter 1 split header lines at a colon. Predict what the split alone gives:
What does this print?
print("max-age=60, private".split(","))
The second piece starts with a space, because the space after the comma belongs to no directive. So each piece is stripped before it is compared, and lowercased, because the names ignore case. Run this:
Change value to "no-store" or to "private,max-age=5" and run it again. The pieces come out the same whether or not a space follows the comma.
Expires and the Date line
Before Cache-Control existed, a server said how long a reply stayed good with the expires header. It holds a date and a time, not a number of seconds, in the HTTP-date format: the same format as the Date line that http.server puts on every reply your server sends. Python’s email.utils
import email.utils
print(email.utils.formatdate(0, usegmt=True)) # Thu, 01 Jan 1970 00:00:00 GMT
0 is the first second of time.time() returns. http.server writes its Date line with this same
A date has a weakness that a count of seconds does not: it only works when the client’s clock agrees with the server’s. So when a reply carries both, max-age wins. RFC 9111 (section 5.3) tells a cache to ignore expires whenever max-age is present. You will still see expires on real replies, usually next to a max-age that says the same thing for newer clients. Your server will send only max-age.
A reply carries cache-control: no-cache. May the client keep the response?
What does cache-control: private allow?
A response has expires one hour ahead and cache-control: max-age=60. How long is it fresh?
Reuse is half the story. When a stored copy goes stale, the client still has to download the whole body again, even if nothing in it changed. The next lessons let a client ask “has it changed?” and receive a short answer, and that question needs a name for one version of a body. Lesson 2 makes that name: a fingerprint computed from the body’s bytes.