Click here to Skip to main content
12,549,320 members (45,027 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as


10 bookmarked

HTTP 304 Not Modified - An Introduction

, 27 Jan 2015 CPOL
Rate this:
Please Sign up or sign in to vote.
An introduction about the HTTP 304 cache mechanism.


HTTP 304 Not Modified and revelant headers provide a cache mechanism that is widely used in website. It can reduce network traffic and avoid unnecessary response overhead. Most of 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 acts an important role in the web development. Understanding the story behind the scenes could be very benificial when we are building our web application. 

The Mechanism

The story of HTTP 304 Not Modified could be summarized by 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 server side.

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 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 options from server in following section. 

Option 1

In the case of 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 these information, client is able to question server with If-Modified-Since header value in future requests. Suppose a user who 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 client will render image from cache. This is how this mechanism reduce the network traffic.

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

Figure 2
Option 2

In 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. 

Figure 3

In 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/modifed time from file systeam 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/modifed 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 INotifyPropertyChanged 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 developement frameworks are always envolving 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
Hello. I am software developer, video game enthusiast, drummer and also a huge Jazz music fan. I have been working on software development since early age. I use C, C++, C# and JavaScript (Node.js) as to build my own world. My motto is "Crafting a system is just like playing drums - both requires the view of art and logic."

Skype ID: live:rvhuang

You may also be interested in...

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 
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.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.161021.1 | Last Updated 28 Jan 2015
Article Copyright 2015 by Robert Vandenberg Huang
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid