Periodically, I’ll get tickets in from customers who are concerned because an object in cache doesn’t behave the way they’re expecting. When they purge the object, everything looks great, then, when the object’s TTL expires, some weird things might happen. They might get a cache HIT after the TTL has expired on an object. Or the TTL might change in an unexpected way. What’s happening here?
It’s not every case, but sometimes, if a response from origin contains a Last-Modified or ETag header, the culprit turns out to be a 304 response. Let’s explore why.
Let’s start with the standard request process to make sure we’re all on the same page. When a request is made to Fastly, we’ll check our cache to see if we have the object stored. If we do, we just return it to the client. If we don’t, we’ll go back to the origin and retrieve a copy of the object. We’ll cache the object (if we’re told to do so) and we’ll send a response back to the client.
Now let’s look at what the standard expected behavior is when we hit an object’s TTL. When we cache the object, we determine how long to hold onto the object. We’ll calculate this value based on headers set at the origin or within your VCL or through variables such as beresp.ttl. When that TTL is reached, we will no longer serve the object from cache. So, the next request comes in asking for that object, we we see that the TTL has been reached, and we go back to the origin to request the object.
So far, pretty easy. But! When an object cached on Fastly’s servers contains a Last-Modified or ETag header, rather than going to the origin and simply requesting a brand new object, we’ll include an If-Modified-Since or If-None-Match header. This allows the origin to check if the object has changed and return a simple 304 response rather than the much larger object.
Let’s take the example of an object that has a TTL of 2 hours. A request comes in 10 minutes after the object is cached. We respond with the object in cache. Neat! Another request comes in 2 hours and 2 minutes after the object was cached. The TTL has been exceeded, so we can see that we can’t use the object in cache. We send a request back to the origin.
This is where those headers come into play. If the object was cached with a Last-Modified or ETag header, Fastly sends a request to the origin with an If-Modified-Since or If-None-Match header. This tells the origin, “hey, check to see if this object has changed since we last got it please!” The origin will check the contents of the If-Modified-Since or If-None-Match header against the Last-Modified or ETag contents of the object. If the object hasn’t been updated, the origin will return a simple 304 Not Modified response. It’s an incredibly small response, just the headers, no response body, so it saves on egress traffic from origin. Neat!
But! What does Fastly do with that response? Well, when we get the 304, we re-up the TTL of the object we have in cache… HOWEVER, the way we do it is different than the way we process a 200 response from origin. There is no VCL modification of the object because we don't receive the object from origin. If there’s no cache-control header on a 304, it will just refresh the object’s existing TTL accordingly. Otherwise, we will use the cache-control header on the 304 to set the objects new TTL.
It’s important to note here that we do NOT get another object from the origin when we get a 304 response. We just keep the object we already have in cache and renew the TTL on it. So, if you change your VCL and expect a new header to be added to the object, you won’t see that until the object has either been purged, or the origin returns a non-304 status code, or we evict the object because it has become a least recently used object.
As a final note, when Fastly receives a 304 response from origin, because of the way request is processed going through Fastly, there’s a significant chance that we’ll deliver a 200 back to the client. So this can be a little obfuscated when troubleshooting! The key identifiers to look for are those Last-Modified or ETag headers and a response that doesn’t behave as expected after the TTL is expired. As always, if you’re seeing responses from Fastly behave in a way you aren’t expecting, contact support. We’re happy to dig into your questions with you and use every tool in our arsenal to help you find a solution.
If you have any followup questions, please don’t hesitate to comment below or reach out to support at firstname.lastname@example.org.
Please sign in to leave a comment.