Cache-control and Expires headers

  • Thread starter Thread starter Mike Kraley
  • Start date Start date
M

Mike Kraley

In my application, I'm using a wildcard mapping so that all requests go via
ASP.NET. Then I'm using an HttpModule to look at all the requests and do some
URL rewriting. Some of the requests then also pass thru custom HttpHandlers,
based on extension.

For some file types, e.g. .js and .swf, I want these files to be cached on
the browser. I am trying to set Cache-Control to public, and would like
either no Expires header, or at least something with a long expiration date,
e.g. 1 month or longer.

You may ask why I'm doing all this: We'd like our .swf and .js files to be
cached in the user's browser - they are big, so we'd like them to stay
around. When we update our code, we will effectively change our URL, which
will cause a new version to be downloaded. But we serve our files under SSL,
and firefox 3, at least, seems to require an explicit cache-control: public
in order to cache SSL content.

So I've tried setting the various properties of Response.Cache in different
event handlers of the HttpModule. Setting cacheability to Public seems to
work fine, but then the response comes out with an expiration date of 1 day
in the future. I then tried to use cache.SetExpires to lengthen the
expiration date. Doesn't work.

A lot of reading of reflector later, what I learned is that something in the
code I can't see is setting _utcExpires to 1 day after now. But the logic in
the HttpCachePolicy object uses the minimum value of any call to SetExpires.
So if I set the expiration date to 1 month early in the pipeline, e.g. at
BeginRequest time, the value is subsequently overridden to 1 day. If I try to
overwrite the value late in the pipeline (anything after
PostRequestHandlerExecute) nothing happens since the headers have already
been written.

[In IIS5, I used a hack that used reflection to set the private variable
_isExpiresSet to false; then I could set a new value with SetExpires. But in
IIS6, which is where I have to get this to work, that doesn't work since the
headers have been written.]

I tried setting expiration in the IIS management tool, but that just affects
the max-age value in Cache-control, but doesn't include public.

Any thoughts on how I can get IIS to vend the headers I want?
 
Hi Mike,

As for setting Cache-control header for those non-aspnet documents(such as
js or swf), first we need to forward those requests(the certain extension)
to ASP.NET isapi in IIS, I think you should have done this, correct?

Then, generally for header manipulation in ASP.NET(for multiple documents
and requests), httpmodule is indeed the reasonable approach. Here is a blog
article mentioned using httpmodule set MaxAge and Public cache-contro field.

#Cache-Control header cannot be set properly in ASP.NET 2.0 - A solution
http://msmvps.com/blogs/omar/archive/2006/08/02/cache-control-header-cannot-
be-set-properly-in-asp-net-2-0-a-solution.aspx

I've tried on my side, the HttpCachePolicy does control the
"Public/Private/..." attribute of the Cache-Control header.

Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead


Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.

--------------------
Thread-Topic: Cache-control and Expires headers
thread-index: AcisnsDWdrj7UTSkSjOM2vVOG/r/xA==
X-WBNR-Posting-Host: 207.46.19.168
From: =?Utf-8?B?TWlrZSBLcmFsZXk=?= <[email protected]>
Subject: Cache-control and Expires headers
Date: Fri, 2 May 2008 14:52:02 -0700
In my application, I'm using a wildcard mapping so that all requests go via
ASP.NET. Then I'm using an HttpModule to look at all the requests and do some
URL rewriting. Some of the requests then also pass thru custom HttpHandlers,
based on extension.

For some file types, e.g. .js and .swf, I want these files to be cached on
the browser. I am trying to set Cache-Control to public, and would like
either no Expires header, or at least something with a long expiration date,
e.g. 1 month or longer.

You may ask why I'm doing all this: We'd like our .swf and .js files to be
cached in the user's browser - they are big, so we'd like them to stay
around. When we update our code, we will effectively change our URL, which
will cause a new version to be downloaded. But we serve our files under SSL,
and firefox 3, at least, seems to require an explicit cache-control: public
in order to cache SSL content.

So I've tried setting the various properties of Response.Cache in different
event handlers of the HttpModule. Setting cacheability to Public seems to
work fine, but then the response comes out with an expiration date of 1 day
in the future. I then tried to use cache.SetExpires to lengthen the
expiration date. Doesn't work.

A lot of reading of reflector later, what I learned is that something in the
code I can't see is setting _utcExpires to 1 day after now. But the logic in
the HttpCachePolicy object uses the minimum value of any call to SetExpires.
So if I set the expiration date to 1 month early in the pipeline, e.g. at
BeginRequest time, the value is subsequently overridden to 1 day. If I try to
overwrite the value late in the pipeline (anything after
PostRequestHandlerExecute) nothing happens since the headers have already
been written.

[In IIS5, I used a hack that used reflection to set the private variable
_isExpiresSet to false; then I could set a new value with SetExpires. But in
IIS6, which is where I have to get this to work, that doesn't work since the
headers have been written.]

I tried setting expiration in the IIS management tool, but that just affects
the max-age value in Cache-control, but doesn't include public.

Any thoughts on how I can get IIS to vend the headers I want?
 
see below
--
....Mike


Steven Cheng said:
Hi Mike,

As for setting Cache-control header for those non-aspnet documents(such as
js or swf), first we need to forward those requests(the certain extension)
to ASP.NET isapi in IIS, I think you should have done this, correct?

Yes, as I mentioned, I'm using a wildcard mapping to send all requests thru
asp.net
Then, generally for header manipulation in ASP.NET(for multiple documents
and requests), httpmodule is indeed the reasonable approach. Here is a blog
article mentioned using httpmodule set MaxAge and Public cache-contro field.

#Cache-Control header cannot be set properly in ASP.NET 2.0 - A solution
http://msmvps.com/blogs/omar/archive/2006/08/02/cache-control-header-cannot-
be-set-properly-in-asp-net-2-0-a-solution.aspx

Yes, this is exactly the hack I used - see my original message in the []
paragraph.
I've tried on my side, the HttpCachePolicy does control the
"Public/Private/..." attribute of the Cache-Control header.

It's great that you agree with what I've done, but unfortunately, you
haven't answered my question. The problem is that I can't figure out how to
set the expires headers in a way that works around the system's insistence on
setting them to 1 day from now.

Please re-read my original message - if I haven't been clear, I'll be happy
to answer questions.
Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead


Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.

--------------------
Thread-Topic: Cache-control and Expires headers
thread-index: AcisnsDWdrj7UTSkSjOM2vVOG/r/xA==
X-WBNR-Posting-Host: 207.46.19.168
From: =?Utf-8?B?TWlrZSBLcmFsZXk=?= <[email protected]>
Subject: Cache-control and Expires headers
Date: Fri, 2 May 2008 14:52:02 -0700
In my application, I'm using a wildcard mapping so that all requests go via
ASP.NET. Then I'm using an HttpModule to look at all the requests and do some
URL rewriting. Some of the requests then also pass thru custom HttpHandlers,
based on extension.

For some file types, e.g. .js and .swf, I want these files to be cached on
the browser. I am trying to set Cache-Control to public, and would like
either no Expires header, or at least something with a long expiration date,
e.g. 1 month or longer.

You may ask why I'm doing all this: We'd like our .swf and .js files to be
cached in the user's browser - they are big, so we'd like them to stay
around. When we update our code, we will effectively change our URL, which
will cause a new version to be downloaded. But we serve our files under SSL,
and firefox 3, at least, seems to require an explicit cache-control: public
in order to cache SSL content.

So I've tried setting the various properties of Response.Cache in different
event handlers of the HttpModule. Setting cacheability to Public seems to
work fine, but then the response comes out with an expiration date of 1 day
in the future. I then tried to use cache.SetExpires to lengthen the
expiration date. Doesn't work.

A lot of reading of reflector later, what I learned is that something in the
code I can't see is setting _utcExpires to 1 day after now. But the logic in
the HttpCachePolicy object uses the minimum value of any call to SetExpires.
So if I set the expiration date to 1 month early in the pipeline, e.g. at
BeginRequest time, the value is subsequently overridden to 1 day. If I try to
overwrite the value late in the pipeline (anything after
PostRequestHandlerExecute) nothing happens since the headers have already
been written.

[In IIS5, I used a hack that used reflection to set the private variable
_isExpiresSet to false; then I could set a new value with SetExpires. But in
IIS6, which is where I have to get this to work, that doesn't work since the
headers have been written.]

I tried setting expiration in the IIS management tool, but that just affects
the max-age value in Cache-control, but doesn't include public.

Any thoughts on how I can get IIS to vend the headers I want?
 
Thanks for your reply Mike,

I'll do some furthe research on this and let you know if I get any further
information.

Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead


Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.

--------------------
From: =?Utf-8?B?TWlrZSBLcmFsZXk=?= <[email protected]>
References: <[email protected]>
Subject: RE: Cache-control and Expires headers
Date: Mon, 5 May 2008 10:31:01 -0700
see below
--
...Mike


Steven Cheng said:
Hi Mike,

As for setting Cache-control header for those non-aspnet documents(such as
js or swf), first we need to forward those requests(the certain extension)
to ASP.NET isapi in IIS, I think you should have done this, correct?

Yes, as I mentioned, I'm using a wildcard mapping to send all requests thru
asp.net


Then, generally for header manipulation in ASP.NET(for multiple documents
and requests), httpmodule is indeed the reasonable approach. Here is a blog
article mentioned using httpmodule set MaxAge and Public cache-contro field.

#Cache-Control header cannot be set properly in ASP.NET 2.0 - A solution
http://msmvps.com/blogs/omar/archive/2006/08/02/cache-control-header-cannot-
be-set-properly-in-asp-net-2-0-a-solution.aspx

Yes, this is exactly the hack I used - see my original message in the []
paragraph.
I've tried on my side, the HttpCachePolicy does control the
"Public/Private/..." attribute of the Cache-Control header.

It's great that you agree with what I've done, but unfortunately, you
haven't answered my question. The problem is that I can't figure out how to
set the expires headers in a way that works around the system's insistence on
setting them to 1 day from now.

Please re-read my original message - if I haven't been clear, I'll be happy
to answer questions.
Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead


Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.

--------------------
Thread-Topic: Cache-control and Expires headers
thread-index: AcisnsDWdrj7UTSkSjOM2vVOG/r/xA==
X-WBNR-Posting-Host: 207.46.19.168
From: =?Utf-8?B?TWlrZSBLcmFsZXk=?= <[email protected]>
Subject: Cache-control and Expires headers
Date: Fri, 2 May 2008 14:52:02 -0700
In my application, I'm using a wildcard mapping so that all requests go via
ASP.NET. Then I'm using an HttpModule to look at all the requests and
do
some
URL rewriting. Some of the requests then also pass thru custom HttpHandlers,
based on extension.

For some file types, e.g. .js and .swf, I want these files to be cached on
the browser. I am trying to set Cache-Control to public, and would like
either no Expires header, or at least something with a long expiration date,
e.g. 1 month or longer.

You may ask why I'm doing all this: We'd like our .swf and .js files to be
cached in the user's browser - they are big, so we'd like them to stay
around. When we update our code, we will effectively change our URL, which
will cause a new version to be downloaded. But we serve our files under SSL,
and firefox 3, at least, seems to require an explicit cache-control: public
in order to cache SSL content.

So I've tried setting the various properties of Response.Cache in different
event handlers of the HttpModule. Setting cacheability to Public seems to
work fine, but then the response comes out with an expiration date of 1 day
in the future. I then tried to use cache.SetExpires to lengthen the
expiration date. Doesn't work.

A lot of reading of reflector later, what I learned is that something
in
the
code I can't see is setting _utcExpires to 1 day after now. But the
logic
in
the HttpCachePolicy object uses the minimum value of any call to SetExpires.
So if I set the expiration date to 1 month early in the pipeline, e.g. at
BeginRequest time, the value is subsequently overridden to 1 day. If I
try
to
overwrite the value late in the pipeline (anything after
PostRequestHandlerExecute) nothing happens since the headers have already
been written.

[In IIS5, I used a hack that used reflection to set the private variable
_isExpiresSet to false; then I could set a new value with SetExpires.
But
in
IIS6, which is where I have to get this to work, that doesn't work
since
the
headers have been written.]

I tried setting expiration in the IIS management tool, but that just affects
the max-age value in Cache-control, but doesn't include public.

Any thoughts on how I can get IIS to vend the headers I want?
 
I am having the exact same problem. I am using VS2008 (.Net 3.5) and I
can't set the Expires date out any further than one day.

Doing a little reflection, I see that in the HttpCachePolicy class,
SetExpires() method, if the expires is already set, it will only change
if the new expiration date is less than the existing one.

I have removed all of my custom HttpModules and discovered that none of
mine are setting the expiration.

Somwhere in the pipeline, something built in is pre-setting the
expiration date to one day forward from the request.

I experience the same effect whether I use Response.Cache.SetExpires(),
Response.Expires, or Response.ExpiresAbsolute.


Sometimes the greatest solutions come from the simplest logic.
Being told "No" is merely the incentive to do it anyway.
 
Thanks for your inputs.

I've also forwarded this question to some other ASPNET dev engineers. I'll
post here is if there is any further information about this issue.

Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead


Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.

--------------------
Subject: RE: Cache-control and Expires headers
 
Hi Mike & WALDO,

After some further discussion with some other engineers, they said that the
one 1 day fixed expire value is not the exact behavior and the actual code
logic is a bit different from that. For the problem you encounter, it might
be a specific issue. Due to the support limitation in newsgroup, it is a
bit hard for me to further involve them. If this is an important issue, I
would suggest you consider contacting CSS for further troubleshooting:

Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx

Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead


Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
--------------------
From: (e-mail address removed) (Steven Cheng [MSFT])
Organization: Microsoft
Date: Fri, 09 May 2008 06:16:47 GMT
Subject: RE: Cache-control and Expires headers
 
'Fraid not. We use reflection to unset the boolean flag changed by
SetExpires, then re-called SetExpires providing the values that we want.

Sometimes the greatest solutions come from the simplest logic.
Being told "No" is merely the incentive to do it anyway.
 
In doing a little reflection on a different problem, I came across some
code thatactually does the work of setting the expires to one day.

If you reflect System.Web.StaticFileHandler and inspect the
ProcessRequestInternal method, you will see the magic code setting the
expiration forward to one day.

I don't know if that helps anyone any, but just passing along the info.

Credit to Omar AL Zabir's blog that at least pointed me in the right
direction.
http://msmvps.com/blogs/omar/archive/2008/06/30/deploy-asp-net-mvc-on-ii
s-6-solve-404-compression-and-performance-problems.aspx

I'm still using reflection to negate the setting of that flag, but at
least now I know WHERE it's being set from.

Sometimes the greatest solutions come from the simplest logic.
Being told "No" is merely the incentive to do it anyway.
 
Back
Top