Click here to Skip to main content
14,635,174 members
Articles » Web Development » Caching » General
Posted 27 Jan 2015

Tagged as



10 bookmarked

HTTP 304 Not Modified - An Introduction

Rate this:
4.57 (8 votes)
Please Sign up or sign in to vote.
4.57 (8 votes)
27 Jan 2015CPOL
An introduction to the HTTP 304 cache mechanism


HTTP 304 Not Modified and relevant headers provide a cache mechanism that is widely used in website. It can reduce network traffic and avoid unnecessary response overhead. Most of the web servers such as IIS, Apache and Nginx support this mechanism for static resources (JavaScript, CSS, image file, etc.) by default so we do not feel its existence. But it actually has an important role in the web development. Understanding the story behind the scenes could be very beneficial when we are building our web application.

The Mechanism

The story of HTTP 304 Not Modified could be summarized by the following steps:

  1. Client sends a normal request to acquire content from server.
  2. Server responds a header value along with the content to indicate the current state. Client will preserve the header value for future requests.
  3. In future requests, client sends a header with preserved value to question server whether the state has changed or not. Response from server could be:
    • If state has changed, server repeats step 1 to respond the latest content along with the state. Client renders the content and saves it in cache.
    • If state does not change, server respond HTTP 304 Not Modified without content. Client renders the content from cache.

Figure 1 is the flow chart on the server side.

Image 1

Figure 1

In step 2, server has two options to indicate the state of current content.

  • To respond a Last-Modified date-time value which represents when the last change occurred. Client in next request will send an If-Modified-Since header value to question server the change.
  • To respond an ETag unique identifier which was generated whenever the change occurred. In this case, client will send an If-None-Match header value to question server the change in next request.


Let us take a look at a few examples by starting from a simple request.

GET /dota2/sheever.png HTTP/1.1
Host: localhost:8000 

In this example, client tries to render a PNG file. Because there is no If-Modified-Since or If-None-Match header present, server knows this is the first request of this image. We will see each of the options from server in the following section.

Option 1

In the case of the first option, server indicates the creation/modified time of this image from file system.

HTTP/1.1 200 OK
Date: Mon, 18 Sep 2015 22:19:01 GMT
Content-Type: image/png
Content-Length: 1393216 
Last-Modified: Thu, 15 Sep 2015 22:10:34 GMT

(PNG Content)

After receiving response from server, client stores this picture and Last-Modified header value in cache. With this information, client is able to question server with If-Modified-Since header value in future requests. Suppose a user just pressed F5 to reload the page, the second request to the same image will be:

GET /dota2/sheever.png HTTP/1.1
Host: localhost:8000
If-Modified-Since: Thu, 15 Sep 2015 22:10:34 GMT

Now server will check whether this image has been edited since Thu, 15 Sep 2015 22:10:34 GMT. If the answer is yes, server can just treat this request as normal one and updates Last-Modified header value to the date-time of latest change. And if no, the response will be very simple:

HTTP/1.1 304 Not Modified
Date: Mon, 18 Sep 2015 22:20:01 GMT 

Notice that no content is given for this status code. The response ends here immediately and the client will render image from cache. This is how this mechanism reduces the network traffic.

If we apply Figure 1 to this option, it can be changed to Figure 2 as below:

Image 2

Figure 2

Option 2

In the second option, server gives a unique identifier that represents the latest change of this image. Because the format of ETag value is not strictly defined in HTTP 1.1, we use GUID in this example. (In addition, the method of generating ETag value does not matter. What matters is whether the server can recognize the change behind it.)

HTTP/1.1 200 OK
Date: Mon, 18 Sep 2015 22:19:01 GMT
Content-Type: image/png
Content-Length: 1393216 
ETag: "82b600b9-c321-4f34-8065-cec076cfbacc"

​(PNG Content)

Now client is able to question server with If-None-Match header value in future requests. This header is used for test if the value held by client is the latest one.

GET /dota2/sheever.png HTTP/1.1
Host: localhost:8000
If-None-Match: "82b600b9-c321-4f34-8065-cec076cfbacc"

Because server has to regenerate and maintain ETag value whenever this image is changed, server is able to recognize whether the value is expired. If the answer is yes, server responds latest content along with updated ETag header value. And if no, the response will be similar to what we just saw in option 1:

HTTP/1.1 304 Not Modified
Date: Mon, 18 Sep 2015 22:20:01 GMT 

Same, no content is given for this status code. The response ends here and client will render image from its cache.

We can now change Figure 1 to Figure 3.

Image 3

Figure 3

In the next section of this article, we will discuss option selection depending on the characteristic of content.

The Good and The Bad

You have probably noticed that there is a problem in practice in option 2. Let us take a look back at the example and review this line.


server has to regenerate and maintain ETag value whenever this image is changed

The question is: how does server accomplish it? How does server notice the change in real-time? In .NET Framework, we can use FileSystemWatcher in our web application to watch over the files. In Java, we can use WatchService and so on in other platforms. But no matter the APIs, watching file system is always costly to our web application, especially watching multiple files in multiple directories.

Option 1 is relatively easier to implement in this example because server is able to retrieve file creation/modified time from file system anytime. Server knows whether the file has been changed since the time that given in If-Modified-Since header. Therefore, it does not have to watch over the files.

But there is also a problem in option 1. File creation/modified time could be easily modified by other applications without changing its content. Thus, it is not a very reliable solution. Additionally, the datetime format used by HTTP 1.1, the RFC1123 standard does not include millisecond part, which means if content is changed more than 2 times within one second, the Last-Modified header value is unable to reflect these changes.

So which option is better? It will be highly depending on the characteristic of content. If the content is an image file, a CSS file or something that is relatively insignificant in our web application, option 1 could be suitable. This is exactly how most of CDN (Content Delivery Network) services deliver these types of content. On the other hand, if the content is a dynamic generated JavaScript statement, JSONP or objects that we would like to temporarily keep in memory, then option 2 is the better choice. We can apply a simple design pattern such as <a href="" target="_blank">INotifyPropertyChanged</a> interface in NET. Framework to make our web application easily receive the change against the content. Therefore, maintaining ETag value for content is much easier to be accomplished.


The web development frameworks are always evolving and new frameworks are being invented all the time. But no matter what framework you are using, paying a little more attentions on HTTP itself and understanding its inherent mechanisms could facilitate building a better web application. HTTP 304 Not Modified is exactly one of them.

Further Reading


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Robert Vandenberg Huang
Software Developer
Taiwan Taiwan
Back-end developer, English learner, drummer, game addict, Jazz fan, author of LINQ to A*

Comments and Discussions

BugSmall error in Figure 1 Pin
George Jonsson2-Feb-15 16:40
professionalGeorge Jonsson2-Feb-15 16:40 
GeneralRe: Small error in Figure 1 Pin
Robert Vandenberg Huang3-Feb-15 3:48
professionalRobert Vandenberg Huang3-Feb-15 3:48 
NewsDesign "flaw" in If-Modified-Since Pin
Chad3F1-Feb-15 20:40
MemberChad3F1-Feb-15 20:40 
There is a design flaw of sorts, but really a limitation, of If-Modified-Since. If the server file is replaced with an older version (e.g. reverted), it can cause the "current" version not to be returned since it technically hasn't change "since". This can be an annoyance while trying to debug issues on the server when temporary modified copies are used, but then the original file is moved back when done (no copy with a new timestamp, just a rename/replace). A similar effect could happen if a server's data is restored from a backup (like after a crash), and the clients then have inconsistent files cached, with no simple way to resync them without a forced page reload and/or a browser (or proxy server) cache purge.

With ETag's format being undefined, a server implementation could just encode the file/resource's timestamp (even down to the sub-second, when available), so it would be as fast as doing If-Modified-Since, but without many of the current limitations.
GeneralRe: Design "flaw" in If-Modified-Since Pin
Robert Vandenberg Huang3-Feb-15 4:15
professionalRobert Vandenberg Huang3-Feb-15 4:15 
QuestionOpen questions Pin
Gerd Wagner28-Jan-15 23:09
professionalGerd Wagner28-Jan-15 23:09 
AnswerRe: Open questions Pin
Robert Vandenberg Huang29-Jan-15 2:10
professionalRobert Vandenberg Huang29-Jan-15 2:10 
GeneralRe: Open questions Pin
Gerd Wagner29-Jan-15 2:45
professionalGerd Wagner29-Jan-15 2:45 
QuestionWhat is the use of fingerprint then, when Etags and Caching Headers alone serve the puropse? Pin
Ankit123456789028-Jan-15 20:24
MemberAnkit123456789028-Jan-15 20:24 
AnswerRe: What is the use of fingerprint then, when Etags and Caching Headers alone serve the puropse? Pin
Robert Vandenberg Huang29-Jan-15 2:37
professionalRobert Vandenberg Huang29-Jan-15 2:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.