Skip navigation.

Count Your Online VisitorsAll recent postsTell Me What I Am Downloading

Stop Image Flicker With Cache-Control Extensions

Image flicker in Internet Explorer/Win causes IE to get bitchslapped quite often. To get rid of this effect Dean Edwards suggests to configure Apache to keep images cached. Otherwise you need to manually change caching preferences in IE which a lot of people don’t know how to do. Besides, when images flicker, it’s you who looks bad, not the users’ IE. Dean says he’s no server expert, and neither am I, but there’s a way to configure IIS in a similar fashion.

Cache-Control Extensions is a little-known feature that was rolled out with Internet Explorer 5.0. In a nutshell, it’s a proprietary extension to the Cache-Control header IE 5.x and 6.x understand. The two extensions are pre-check and post-check.

Object request timing with cache-control extensions

Internet Explorer applies the following logic to objects served with these extensions:

  1. Upon first request, the object is cached and is served from cache until the post-check interval expires.
  2. Once the post-check interval expires IE fetches the object from cache and checks for an updated one in the background. If a newer object is available it caches it. Upon every subsequent request this updated (and now cached) object is served until the pre-check interval expires.
  3. Once the pre-check interval elapses the object is treated as expired. IE will first ask the HTTP server if the object has changed since it was requested by the browser. If it has, IE will load the updated object.

Note: I’m not referring to web pages. I refer to objects because we might be talking about images, web pages, style sheets, external JavaScript files, etc.

As MSDN states, the Refresh button will not trigger this logic because Refresh always sends the if-modified-since request to the server. Hyperlinks do trigger this logic.

How about an example? Suppose an HTTP server sends an image with the following header:

Cache-Control: post-check=3600,pre-check=43200

Both pre-check and post-check specify time intervals in seconds. We tell IE to cache the mentioned image for 12 hours (60 * 60 * 12 seconds). The first hour (60 * 60 seconds) IE will simply display the image from its local cache. However, after 60 minutes we want it to check for a newer one in the background, i.e. it will display the cached one and then do a background check. When 12 hours are up, IE checks for a modified image first.

Set Cache-Control Extensions In IIS

The only missing piece of the puzzle is where to set these extensions to get the ball rolling. You do it in the IIS Manager snap-in. You may choose to set them on a specific folder, such is a folder with images. You may also set them on your entire web app, but hardly ever would you want to cache every single page on your site.

Fire up the IIS Manager snap-in from Administrative Tools, pick a folder in your web app, right click, go to Properties, switch to the HTTP Headers tab, and click Add. Add cache-control extensions like this:

Add/edit custom HTTP header

Click Ok to dismiss the dialog. Your HTTP Headers tab should have extensions listed.

The Properties window

Set Cache-Control Extensions Programmatically

I was in for a big surprise when I found documentation for HttpCachePolicy.AppendCacheExtension. You can accomplish what we’ve talked about in C# like this:

Response.Cache.AppendCacheExtension(
        "post-check=900,pre-check=3600");

Now, if you want to serve images with this cache policy (which is a good idea) you need to assign them to the ASP.NET ISAPI extension in IIS because by default ASP.NET is not configured to pass them through its HTTP pipeline.

What I Don’t Know

What happens if either pre-check or post-check is missing? I don’t know. What happens if post-check is greater than pre-check? I don’t know either. I found no documentation on MSDN that talks about it.

Comments

Comment permalink 1 Roger |
Looks like something I'll take a look at when I get back to work (no IIS at home - when I have a choice I use Apache ;-)). I hope it works as well as the Apache solution you mentioned above. I have implemented it on my Inverted Sliding Doors tabs demo, and it seems to do the trick.
Comment permalink 2 santogiacomo dominique |
no comment ok..
Comment permalink 3 Brent Railey |
Thank you for this information, this got rid of my flickering problem I was also dealing with. I implemented the C# fix to a generated image.
Comment permalink 4 Roger Johansson |
I can't figure out how to do this with Classic ASP, which is what most of our sites are on. Any hints?
Comment permalink 5 Milan Negovan |
Those IIS settings are the same for ASP or ASP.NET applications. Or for any web applications hosted in IIS, for that matter.

I believe in classic ASP you can use Response.AddHeader to accomplish the same programmatically:

Response.AddHeader "Cache-Control", "post-check=3600,pre-check=43200"

Shoot me an email if you get stuck. ;)
Comment permalink 6 Roger Johansson |
The IIS settings aren't always easily accessible if the site is hosted externally (which all of our clients' sites are), so being able to change it programatically is great. What you're suggesting looks like it could do the trick. Will report back when I have tested it.
Comment permalink 7 Milan Negovan |
Aaaaah, your sites are hosted elsewhere. To cache images programmatically IIS needs to pass their processing to your code. Otherwise it serves them on its own.

In ASP.NET I would (1) write a custom HttpHandler, and (2) map images in IIS to the ASP.NET ISAPI. Normally, I call my hosting company and have them do this mapping because I don't have this much control over their IIS.

In classic ASP... Hmmm... There are really no HttpHandlers per se. Old-style ISAPI extensions and filters are written in C++, and I'd think a hosting company would throw a fit if you ask them to install yours.
Comment permalink 8 Roger Johansson |
Just like you suspected, doing this programatically doesn't work in Classic ASP. Adding the header in IIS works, so I contacted our hosting company and had them add it where needed. Took them all of five minutes ;)
It's great to finally be flicker-free even when IE is configured the way many developers like it to be.
Comment permalink 9 Andreas Wallberg |
Did anyone find a way to map these to only images, like
the ExpiresByType directive in HtAccess?

/Andreas
Comment permalink 10 Jesse Hansen |
After implementing the fix above, I was still getting flickers in IE6 with Windows XP SP2 when the "Every time visit to the page" developer setting. I was able to eliminate the flicker by using the following header:

Cache-control: max-age=2592000;post-check=31449600,pre-check=31449600

You can adjust the numeric items accordingly.

I also had to check the Enable Content Expiration checkbox and set the expiration up to 30 days.
Comment permalink 11 Milan Negovan |
Thank you for sharing this, Jesse!
Comment permalink 12 Chris Neppes |
You can set IIS cache control manually, but for easy developer access to cache control with having to touch the Internet Services Manager, try CacheRight (http://www.cacheright.com).

You can also apply global changes more easily with this tool for IIS cache control -- it is all managed from one simple text rules file that lives in the Web root.

Cheers,
Chris @ Port80
Comment permalink 13 George |
Just implemented this on my new websites. Not it flies !!!!!
Have been working as a web developer for ages, Know how to prevent caching but never thought that on images it's better to have cahing working. That thought never crossed my mind untill i saw this article.
Comment permalink 14 George |
Question: Does this line "Cache-Control: post-check=3600,pre-check=43200" affect IE only or any standard browser ?
Basically is it part of the standard?
Comment permalink 15 Milan Negovan |
George, as it says in this post it's "a proprietary extension to the Cache-Control header IE 5.x and 6.x understand". It's not a part of any standard.
Comment permalink 16 George |
Milan, sorry i did not read carefully. I guess i was too excited. It really made a difference, especially in E-Commerce project when people often go back and forth between items and the catalog.
-----------------------------------
No the question is how do i make the same thing for FireFox for example? Will it work if i do something like that.
"Cache-Control:max-age=432000;post-check=36000,pre-check=432000" ?
Comment permalink 17 Milan Negovan |
As a proprietary header to IE it shouldn't work for non-IE browsers (unless they choose to implement it).
Comment permalink 18 Mike |
These two headers are completely unnecessary. Cache-Control: max-age= does just that, and much more. IE honors it very well. It's in HTTP/1.1 standard. It's honored in all proxy caches.

BTW someone mentioned earlier that they need "Cache-Control: max-age=2592000;post-check=31449600,pre-check=31449600" to get rid of the flicker. Guess what? you only need the max-age part.

In the book "HTTP Essential" the Cache-Control header is explained very clearly. I suspect Microsoft abandoned their header hack in IE6.
Comment permalink 19 Johan |
Response.AddHeader "Cache-Control", "post-check=3600,pre-check=43200"

Is there an equivalent to do this for Apache php configuring the cahce settings?
Comment permalink 20 Johan |
.htaccess
< FilesMatch "\.(gif|jpe?g|png)$" >
Header append Cache-Control "max-age=86400, must-revalidate"
< /FilesMatch >

would this do the job for Apache cache settings?
Comment permalink 21 Simon |
I've added all the suggestions for header info, only the problem still persists. If you install ieHttpHeaders you will see IE6 still making image requests to the server.

This may be a bug in IE6. I don't have the same problem with firefox.
Comment permalink 22 Travis Savo |
Seems that only masks the problem instead of hiding it.

First of all, with an expires/max-age of 'Way way way in the future', how do you deal with changing resources being cached forever on the client?

Secondly, if after caching out an image, does setting your clock ahead to after the cache expiration time cause the problem to reappear? It does for me... and I'm having a heck of a time fixing it.
Comment permalink 23 Milan Negovan |
Travis, I definitely see images getting cached on the client forever (or until they clean out the cache). If it's not desired, and you expect them to change, set a reasonable timeout.

I haven't tried tweaking the system clock, but I'd imagine it can be an issue, as you point out.
Comment permalink 24 Zareh |
I've set the IE setting to Check for a "Newer Page on Every Visit", and did some testing. I found the following:

Programmatic approach
I added the line of code Response.Cache.AppendCacheExtension("max-age=2592000; post-check=31449600, pre-check=31449600"); at the end of the Page_Load method.)
- Pages get cached (look at IE temp files folder, you will see a value in the expires column; web server log confirms page was not requested.)
- Images, stylesheets, etc. do NOT get cached (temp files folder entry has value "none" in the expires column; web-server log shows request for images/stylesheets.)

Setting IIS Custom Headers
I added the custom header "Cache-Control:max-age=3600" fr the virtual site.
- Pages get cached (as evidenced by value in temp files entry, and web server log)
- Images/stylesheets get cached (as evidenced by value in temp file entry expires colum - they now have an value!!! Also confirmed by web server log where there is NO request for these objects.)

The colclusion I take away is that the programmatic approach only affects the generated page, NOT the images/stylesheets/etc. the page may contain. The ONLY way to ask for caching of these objects appears to be to set a custom header value in IIS. Again, only the "max-age" value was needed 8-)
Comment permalink 25 Milan Negovan |
Thank you, Zareh. Good to know this.
Comment permalink 26 alkhan |
i'm amazed at the level of knowledge everyone on this post has. Unfortunately, I do not. This will sound like a "dumb" question but what is the flicker that everyone experiences? Our ASP.NET web application is strictly for data entry, running calculations and reports. It's a desktop data application transformed for the web. We have various toolbars and image files for buttons and logo. I don't seem to experience any flicker. The only annoying flicker is from the page post back when dynamically filtering/setting web controls or tree controls. You feel as though you are in an "epileptic" state when filling in data for a form. I need help with that without giving up the functionality of dynamic filtering/settings.

If someone has an example of the flickering that you are experiencing please pass it on. I'd like to see if I am experiencing the same thing without realizing ... as I said i don't have the level of knowledge as the people posting on this topic.

Al
Comment permalink 27 Milan Negovan |
Al, it's not a stupid question at all. I can't think of a site that exhibits a flicker off the top of my head. Usually, you see it happen when a navigation menu has images as its backgound. You hover over the manu and IE takes a second or two to fetch the background image. This delayed change of state is the "flicker."
Comment permalink 28 George |
Works great! No more annoying flickering when switching pages in IE. Please note that FireFox does a better job in eliminating flickering (caching) when switching pages.

Here's my setup in Apache 2 (.htaccess of vhost-conf):


ExpiresActive On
ExpiresByType text/html A1
ExpiresByType text/css "access plus 1 day"
ExpiresByType text/javascript "access plus 1 day"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/jpg "access plus 1 month"
ExpiresByType image/gif "access plus 1 month"



Header append Cache-Control "post-check=3600, pre-check=43200"


Please note that the modules expires and headers must be enabled in Apache to function.

Regards,
George
Comment permalink 29 George |
Uhm, some tags were filtered in my previous post. Again:


ExpiresActive On
ExpiresByType text/html A1
ExpiresByType text/css "access plus 1 day"
ExpiresByType text/javascript "access plus 1 day"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/jpg "access plus 1 month"
ExpiresByType image/gif "access plus 1 month"


< IfModule mod_headers.c >
Header append Cache-Control "post-check=3600, pre-check=43200"
Comment permalink 30 George |
Sorry, I give up. Please remember to enclose the directives with the appropiate module checks.
Comment permalink 31 Simon |
Thanks for the Apache Fix - but asp.net runs on Windows/IIS.
Comment permalink 32 Paul van Steenoven |
In case if you want to have flicker free IE mouseovers this code is for you:

a { mouseover.gif }
a:link, a:visited { regular.gif }
a:hover { mouseover.gif }

tadaah IE is flicker free :)
Comment permalink 33 John Whistler |
Internet Information Services 5.0
Internet Explorer 6

I added the custom header "Cache-Control:max-age=3600" ,
but ONLY to the (virtual) directories which contain image files.
(ie., I did NOT add the header to any other content files -I want my asp application pages to never be cached).

The image flicker is gone.
Outstanding !!
Comment permalink 34 Bob Everland |
Comment 33:
This does not work, this is the issue everyone is having is that styles are not cached by IE. If you follow the directions it will work. You can not programatically add the cache control using ColdFusion and classic ASP. I am curious if adding the cache control with ASP.NET makes it site wide, or only for your application.
Comment permalink 35 Simone Busoli |
The only solution seems to be adding the Expires header to the response:

(C#)

Response.Expires = 10;

Where the number represents the minutes after which the response will expire.
Comment permalink 36 Greg |
These solutions look great. But what if you (me in this case) are developing an application that is sold to companies around the world. Not every company uses the same web server to host the application. I would love to avoid having to do different fixes for this problem on different servers.
Comment permalink 37 Simone Busoli |
You are absolutely right Greg, but actually the problem is much wider than this.
It's not only the servers that are different, but the clients too, which often is a much bigger problem.
Comment permalink 38 Chris Scanlon |
I totally agree with what you're saying. I wish more people felt this way and took the time to express themselves. Keep up the great work.

Chris Scanlon
http://www.asphostingfun.com
Comment permalink 39 Milan Negovan |
I hear you, Greg. I would like to avoid kludges, too. This is one of those cases where you can give yourself home field advantage if you have any degree of control over the server.
Comment permalink 40 Sid |
As a note, if you want to make images stay cached forever using max-age=[big number], but you want the ability to update them, there is a solution... use a querystring. The image file "foo.jpg?1" works the same as, yet is different from, "foo.jpg?2". If you're going to use it all over the place, consider writing some helper JS code to cut down on maintenance.
Comment permalink 41 Jason Holtzman |
I've heard several differnet solutions here so far, but I would like to know which one exactly works for IIS 5.0. I don't have access to our website's webserver as it is hosted remotely. I've already had them add the 'Cache-Control:max-age=3600' HTTP Header, but it doesn't seem to be working. After clearing out my Temp IE Files and reloading the home page, there still is no expiration date on the images I want cached. And the flicker is still there...
Comment permalink 42 Jason Holtzman |
After further review, using the max-age header DID work for me. The only problem I still have is a slight delay with the image-shifting during rollover. Otherwise this worked like a charm.
Comment permalink 43 praveen Ratnakar |
Hi!
I have set HTTP Header of IIS Folder(Video) Cache-Control:post-check=60,pre-check=120
I m saving different video file in this folder with same name(temp.wmv) But every time the same video file is played until i manually delete that file from Temporary internet Files.
Please please please Someone help me..........
I m really getting frustrated......
Comment permalink 44 Ryan |
Had ISP set this header exactly as the article above said to for IIS (Cache-Control: post-check=3600,pre-check=43200) and it worked perfectly! - Thanks for info!
Comment permalink 45 Kishan |
How to avoid cacheing of .js files in ASP.

Please help me I am struck very badly.

Thanks in advance.
Comment permalink 46 Eric Lawrence |
Eric Lawrence from the IE Networking team here.

If you don't have both post-check and pre-check present, both directives are ignored.

If post-check > pre-check, both directives are ignored.
Comment permalink 47 bughouse |
hint: mod_gzip
Comment permalink 48 Jeffrey Schrab |
This saved our butts! Thanks a lot!
Comment permalink 49 Julian |
Hi,

From the comments above is it better practice to use the:
Cache-Control:max-age=3600
or
Cache-Control: post-check=3600,pre-check=43200

Can you advise what happens when the 3600 or 43200 seconds expire? How do you get the images to still be cached and checked 'after the first hour'?

Similarly what do you recommend for the best practice when using the IIS HTTP Headers Enable Content Expiration. For example do you set directories to expire on a specific date say Fridays and then use that date to update the files, and then allocate another future expiration date?

If you use the Expire After... option what happens after that expiry timeframe - how do you get the content cached again?

Thanks in advance
Comment permalink 50 Andrew Buck |
I spent ages trying to do this and finally found a great solution. I used a Perl script to serve the images (normal and hover images) with appropriate headers:

#!/usr/bin/perl
$|++;

my $file = '../WWW/tabhover.gif';
open(PIC, $file);

my(@MON) = qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/;
my(@WDAY) = qw/Sun Mon Tue Wed Thu Fri Sat/;
my($sec, $min, $hour, $mday, $mon, $year, $wday) = gmtime(time() + 24*3600);
$year += 1900;
$expires = sprintf("%s, %02d %s %04d %02d:%02d:%02d GMT", $WDAY[$wday], $mday, $MON[$mon], $year, $hour, $min, $sec);

print "Expires: $expires\n";
print "Content-Type: image/gif\n\n";

binmode STDOUT;
print ;
close(PIC);
exit 0;

I did it like this as I didn't want my pages cached at all. Otherwise I could have maybe put these headers in the pages themselves.
Comment permalink 51 David Levin |
Thanks so much for this article! I spent hours trying to fix this myself. You saved me at least 2 days of work!
Comment permalink 52 Cezary Okupski |
Travis, if you know a change took place, refer to a resource with some version identifier, id est:
http://www.example.com/example.jpg?v=20050101
http://www.example.com/example.jpg?v=20060917
Comment permalink 53 Mr. Pixel |
A js only solution: add this in the HEAD of the page:

try {
document.execCommand("BackgroundImageCache", false, true);
} catch(err) {}
Works for IE6 Service Pack1

Not needed for IE7.

Check my blog for details
Comment permalink 54 tomek |
How to avoid cacheing of .js files in ASP.

Please help me I am struck very badly.

Thanks in advance.
Comment permalink 55 kiran |
i had tried all these ie adding postcheck,precheck etc in IIS still flickering problem doesnt go,iam getting flickering problem only in pages where we have infragistics webgrid ..if anyone have suggesttions or solns please let me know.
Comment permalink 56 Milan Negovan |
My guess is you'll need to ask Infragistics. :)
Comment permalink 57 kiran |
yeah,i need to do that before asking them i want to know wether anyone had faced this problem before,thanks for sugestion though
Comment permalink 58 praca |
I was reading your comments kiran and i have to say i got a similar problem;/ Did you find a solution for this? Plz let me now!
Comment permalink 59 Plasma |
Really interesting, i must try this in one of my sites...
Comment permalink 60 Killtek |
Mr. Pixel's javascript solution works.. Thank you!!!
Comment permalink 61 Adam Rogas |
Using any caching at all works with


try
{
document.execCommand("BackgroundImageCache", false, true);
} catch(err) {}

Works for IE6 Service Pack1

as per Mr. Pixel

it also does not cause the images to be stuck in cache forever as a forced page reload will refresh the images.
Comment permalink 62 Arzt |
I just want to say thanks, your solution works for me really good...
Comment permalink 63 Master |
Thanks a lot for this article.
Comment permalink 64 Plater |
As Andrew Buck did, I used a perl script to serve pages with custom headers. Now I've made the jump to ASP.NET to do the exact same thing.

Try something like the following:

Response.Clear();
Response.Cache.SetExpires(DateTime.Now.AddHours(12));
Response.ContentType="image/jpeg";
Response.WriteFile(filename);
Response.End();

The nice thing is you can use a query string or almost anything to decide what 'filename' is. I have taken to using tokens in the querystring to decide what file to return back to the browser.
Comment permalink 65 Logodesign |
After further review, using the max-age header DID work for me. The only problem I still have is a slight delay with the image-shifting during rollover. Otherwise this worked like a charm.
Comment permalink 66 Florian |
No more image flicker problems. Thank you so much!
Comment permalink 67 Mattias |
@Plater:
Well done, now it works 100% without flickering...
Comment permalink 68 Mag |
Are there any recent updates for the article?
Comment permalink 69 Alex |
The Cache-Control HTTP Headers is part of the HTTP 1.1 standard.
It has a certain number of parameters that can be used:

* max-age=seconds - the number of seconds from the time of the request you wish this objcet to be keep into the cache;
* s-maxage=seconds - like max-age but it only applies to proxy;
* public - tell to handle the content has cacheable even if it would normally be uncacheable, it is used for example for authenticated pages;
* no-cache - force both proxy and browser to validate the document before to provide a cached copy;
* must-revalidate - tell the browser to obey to any information you give them about a webpage;
* proxy-revalidate - like must-revalidate but applies to proxy;
Comment permalink 70 Guitar Teacher |
Oh God.

I only became aware of the flickering problem very late into the design stage.
The only solution I could come up was some ugly client-side hack.

Substitute the scriptless CSS menu by javascript if IE is detected -
Otherwise do nothing.

example in my guitar website

I wouldn't mind removing all the extra js junk tho
I like clean code.
but I have no .NET extension there or admin access to IIS
to force the change in cache policy.
Comment permalink 71 Milan Negovan |
Check out this comment for a JavaScript-only solution.

Emails and Notifications

Would you like to be notified when somebody responds to this post?  Would you like to have these comments emailed to you?

Sorry, discussion of this post is closed.