Why are custom headers missing in "304 (Not Modified)" responses?
Tags: webdev, programming, beginners
Published: 2022-08-23 02:35:13

`304 (Not Modified)` responses minimize the amount of information that needs to be transferred in subsequent requests made after an initial `200 (OK)` response if the resources being requested are unchanged[^1].

This has performance benefits for a website, particularly where larger assets such as images are involved.

If you are developing a website that makes use of custom headers in  responses, it's worth noting a behaviour with respect to headers that `304` responses have that differ from `200` responses.

If a visitor to the website were to inspect their network requests and look at a `200` response, they can expect to see not only custom headers defined for the response, but the following headers which relate to the caching of the resource[^2]:

* Cache-Control
* Content-Location
* Date
* ETag
* Expires
* Vary

 In a `304` response however, the specification details that[^1]:

> ... a sender *SHOULD NOT* generate representation metadata...unless said metadata exists for the purpose of guiding cache updates.

This means that that same request, if the resources are cached on the browser, will receive a response that will generally be missing the additional metadata (in this case the custom headers) since they don't tend to serve 'the purpose of guiding cache updates'.

As an example to illustrate this behaviour, let's use a demo site that Netlify has for the Next.js framework. The specific page that we're interested in can be found [here](https://netlify-plugin-nextjs-demo.netlify.app/old/image/).

Looking at the 'Network' tab within the browser console, the request fetching the image on the page is the one that we'll take a closer look at. It should look something like:

`GET https://netlify-plugin-nextjs-demo.netlify.app/_ipx/w_3840,q_75/%2F_next%2Fstatic%2Fmedia%2Funsplash.9a14a3b9.jpg?url=%2F_next%2Fstatic%2Fmedia%2Funsplash.9a14a3b9.jpg&w=3840&q=75`

and the `200` response headers:

```http
HTTP/2 200 OK
age: 0
cache-control: public, max-age=0, must-revalidate
content-type: image/jpeg
date: Tue, 23 Aug 2022 01:05:36 GMT
etag: "3b-XMihzCU33fI+mTMhk6V4yc9TZww"
server: Netlify
strict-transport-security: max-age=31536000
x-nf-request-id: 01GB43QGZ949EAFF6HBMX480RG
x-test: foobar
X-Firefox-Spdy: h2
```

Now if we were to refresh the page, we'll see a `304` response for that request instead:

```http
HTTP/2 304 Not Modified
cache-control: public, max-age=0, must-revalidate
date: Tue, 23 Aug 2022 01:08:03 GMT
etag: "3b-XMihzCU33fI+mTMhk6V4yc9TZww"
server: Netlify
x-nf-request-id: 01GB43W0VGYVR87CHSMZ70T4HQ
X-Firefox-Spdy: h2
```

Notice how the `x-test` custom header near the bottom of the list for the `200` response is now missing from the `304` response.

## Wrap up

I hadn't realized until recently that the minimization of data in a `304` response extended to the response headers, and so for those who have run into this behaviour and were also a bit puzzled as to why this happens, I hope this quick explanation was helpful.

Happy coding!


[^1]: https://httpwg.org/specs/rfc7232.html#status.304
[^2]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/304