# Value of "Expires" header differ for caching and content serving servers

**URL:** <https://www.ruby-forum.com/t/value-of-expires-header-differ-for-caching-and-content-serving-servers/205430>\
**Category:** NGINX\
**Created:** [April 6, 2011, 12:47pm UTC](https://www.ruby-forum.com/t/value-of-expires-header-differ-for-caching-and-content-serving-servers/205430 "2011-04-06T12:47:51Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![dubstep](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/cdc98d/32.png) [@dubstep](https://www.ruby-forum.com/u/dubstep)\
**Post date:** [April 6, 2011, 12:47pm UTC](https://www.ruby-forum.com/t/value-of-expires-header-differ-for-caching-and-content-serving-servers/205430/1 "2011-04-06T12:47:51Z")

</div>

I have a following servers structure:  
content.server contains an Apache instance, which processes one simple  
PHP script. On the same node Nginx is set as a caching proxy to that  
Apache.  
caching.server contains Nginx with proxy\_cache directive, with upstream  
set to content.server

Php script generates an XML file, that has a lifetime of 30 minutes.  
This is achieved by setting headers in PHP code: “Cache-Control:  
max-age=1800”, “Date: now()” and “Expires: now()+1800seconds”.

content.server serves the cached file correctly:

> Date Wed, 06 Apr 2011 09:50:49 GMT  
> Cache-Control public, must-revalidate, max-age=1800  
> Expires Wed, 06 Apr 2011 10:14:25 GMT  
> Etag 26ee7051c360ee28d2a5697fb2b97a03

But approximately half of the time caching.server serves the content  
where Date header is larger than Expires, and Expires is different from  
the one on content.server:

> Date Wed, 06 Apr 2011 09:49:58 GMT  
> Cache-Control public, must-revalidate, max-age=1800  
> Expires Wed, 06 Apr 2011 09:44:23 GMT  
> Etag 26ee7051c360ee28d2a5697fb2b97a03

My questions is: what can cause such behaviour, when caching nginx sets  
its own Expires header?

Posted at Nginx Forum:

> **[Value of "Expires" header differ for caching and content serving servers](https://forum.nginx.org/read.php?2,188725,188725#msg-188725)**
>
> I have a following servers structure: content.server contains an Apache instance, which processes one simple PHP script. On the same node Nginx is set as a caching proxy to that Apache. caching.server contains Nginx with proxy\_cache directive, with...

---

<div class="post-metadata">

**Author:** ![i.s.ivanov2](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/i/c4cdca/32.png) [@i.s.ivanov2](https://www.ruby-forum.com/u/i.s.ivanov2)\
**Post date:** [April 6, 2011, 1:40pm UTC](https://www.ruby-forum.com/t/value-of-expires-header-differ-for-caching-and-content-serving-servers/205430/2 "2011-04-06T13:40:21Z")

</div>

And an additional question: why haven’t nginx requested a new file, when  
“Expires” header becomes bigger than current date? Can it be caused by  
proxy\_cache\_valid any 10m; directive?

Posted at Nginx Forum:

> **[Re: Value of "Expires" header differ for caching and content serving servers](https://forum.nginx.org/read.php?2,188725,188736#msg-188736)**
>
> I have a following servers structure: content.server contains an Apache instance, which processes one simple PHP script. On the same node Nginx is set as a caching proxy to that Apache. caching.server contains Nginx with proxy\_cache directive, with...

---

<div class="post-metadata">

**Author:** ![i.s.ivanov2](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/i/c4cdca/32.png) [@i.s.ivanov2](https://www.ruby-forum.com/u/i.s.ivanov2)\
**Post date:** [April 7, 2011, 1:46pm UTC](https://www.ruby-forum.com/t/value-of-expires-header-differ-for-caching-and-content-serving-servers/205430/3 "2011-04-07T13:46:37Z")

</div>

I’ve captured the debug output of the outlined problem: Expires tag is  
less than the date of request processing, but there is no expiration  
message like “http file cache expired: 4 1302149578 1302149592”  
Debug output:

2011/04/07 18:42:15 [debug] 10373#0: \*6 epoll add event: fd:16 op:3  
ev:80000005  
2011/04/07 18:42:15 [debug] 10373#0: \*6 http script var: “request”  
2011/04/07 18:42:15 [debug] 10373#0: \*6 http script var: “?”  
2011/04/07 18:42:15 [debug] 10373#0: \*6 http script var: “param=value”  
2011/04/07 18:42:15 [debug] 10373#0: \*6 http cache key: “request”  
2011/04/07 18:42:15 [debug] 10373#0: \*6 add cleanup: 0925FDD4  
2011/04/07 18:42:15 [debug] 10373#0: \*6 http file cache exists: 0 e:1  
2011/04/07 18:42:15 [debug] 10373#0: \*6 cache file:  
“/var/cache/a/22/ad1686833f0e0b0411e5bb582061822a”  
2011/04/07 18:42:15 [debug] 10373#0: \*6 add cleanup: 0925FE1C  
2011/04/07 18:42:15 [debug] 10373#0: \*6 http file cache fd: 21  
2011/04/07 18:42:15 [debug] 10373#0: \*6 read: 21, 0925FE68, 361, 0  
2011/04/07 18:42:15 [debug] 10373#0: \*6 http upstream cache: 0  
2011/04/07 18:42:15 [debug] 10373#0: \*6 http proxy status 200 “200 OK”  
2011/04/07 18:42:15 [debug] 10373#0: \*6 http proxy header: “Server:  
nginx”  
2011/04/07 18:42:15 [debug] 10373#0: \*6 http proxy header: “Date: Thu,  
07 Apr 2011 11:16:40 GMT”  
2011/04/07 18:42:15 [debug] 10373#0: \*6 http proxy header:  
“Content-Type: text/xml”  
2011/04/07 18:42:15 [debug] 10373#0: \*6 http proxy header: “Connection:  
close”  
2011/04/07 18:42:15 [debug] 10373#0: \*6 http proxy header:  
“X-Powered-By: PHP/5.3.3-fpm.1”  
2011/04/07 18:42:15 [debug] 10373#0: \*6 http proxy header:  
“Cache-Control: public, must-revalidate, max-age=1800”  
2011/04/07 18:42:15 [debug] 10373#0: \*6 http proxy header: “Expires:  
Thu, 07 Apr 2011 11:25:35 GMT”  
2011/04/07 18:42:15 [debug] 10373#0: \*6 http proxy header: “ETag:  
26ee7051c360ee28d2a5697fb2b97a03”  
2011/04/07 18:42:15 [debug] 10373#0: \*6 http proxy header:  
“Content-Length: 2102”  
2011/04/07 18:42:15 [debug] 10373#0: \*6 http proxy header done  
2011/04/07 18:42:15 [debug] 10373#0: \*6 http file cache send:  
/var/cache/a/22/ad1686833f0e0b0411e5bb582061822a  
2011/04/07 18:42:15 [debug] 10373#0: \*6 posix\_memalign: 092300C0:4096  
@16  
2011/04/07 18:42:15 [debug] 10373#0: \*6 HTTP/1.1 200 OK  
Server: nginx/0.8.54  
Date: Thu, 07 Apr 2011 11:42:15 GMT  
Content-Type: text/xml  
Connection: keep-alive  
X-Powered-By: PHP/5.3.3-fpm.1  
Cache-Control: public, must-revalidate, max-age=1800  
Expires: Thu, 07 Apr 2011 11:25:35 GMT  
ETag: 26ee7051c360ee28d2a5697fb2b97a03  
Content-Length: 2102

Posted at Nginx Forum:

> **[Re: Value of "Expires" header differ for caching and content serving servers](https://forum.nginx.org/read.php?2,188725,189076#msg-189076)**
>
> I have a following servers structure: content.server contains an Apache instance, which processes one simple PHP script. On the same node Nginx is set as a caching proxy to that Apache. caching.server contains Nginx with proxy\_cache directive, with...

---

<div class="post-metadata">

**Author:** ![i.s.ivanov2](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/i/c4cdca/32.png) [@i.s.ivanov2](https://www.ruby-forum.com/u/i.s.ivanov2)\
**Post date:** [April 7, 2011, 9:55pm UTC](https://www.ruby-forum.com/t/value-of-expires-header-differ-for-caching-and-content-serving-servers/205430/4 "2011-04-07T21:55:15Z")

</div>

On Thu, Apr 7, 2011 at 7:15 AM, Maxim D. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> > 
> 
> This doesn’t actually correct: Cache-Control max-age and Expires disagree.  
> According to max-age, document should expire at 10:20:49, while  
> expires set to 10:14:25.
> 
> Unless configured to ignore some headers (with  
> proxy\_ignore\_headers directive) nginx will use first relevant  
> header it sees to actually expire cached document, Cache-Control  
> max-age in this case.

Really? That behavior would seem to be at odds with the HTTP 1.1  
specification. From RFC 2616 section 14.9.3:

“If a response includes both an Expires header and a max-age  
directive, the max-age directive overrides the Expires header, even if  
the Expires header is more restrictive”

## As almost all origin servers send both Expires and Cache-Control in their default configurations, with the Expires header coming first, wouldn’t nginx end up doing the wrong thing most of the time?

RPM

---

<div class="post-metadata">

**Author:** ![i.s.ivanov2](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/i/c4cdca/32.png) [@i.s.ivanov2](https://www.ruby-forum.com/u/i.s.ivanov2)\
**Post date:** [April 7, 2011, 2:16pm UTC](https://www.ruby-forum.com/t/value-of-expires-header-differ-for-caching-and-content-serving-servers/205430/5 "2011-04-07T14:16:17Z")

</div>

Hello!

On Wed, Apr 06, 2011 at 06:47:10AM -0400, i.s.ivanov2 wrote:

> content.server serves the cached file correctly:
> 
> > Date Wed, 06 Apr 2011 09:50:49 GMT  
> > Cache-Control public, must-revalidate, max-age=1800  
> > Expires Wed, 06 Apr 2011 10:14:25 GMT  
> > Etag 26ee7051c360ee28d2a5697fb2b97a03

This doesn’t actually correct: Cache-Control max-age and Expires  
disagree.  
According to max-age, document should expire at 10:20:49, while  
expires set to 10:14:25.

Unless configured to ignore some headers (with  
proxy\_ignore\_headers directive) nginx will use first relevant  
header it sees to actually expire cached document, Cache-Control  
max-age in this case.

> its own Expires header?  
> Value of Expires header is preserved from original request, while  
> Date is current date on the server. Since document isn’t expired  
> yet (see above), you see Expires in the past.

Maxim D.

---

<div class="post-metadata">

**Author:** ![i.s.ivanov2](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/i/c4cdca/32.png) [@i.s.ivanov2](https://www.ruby-forum.com/u/i.s.ivanov2)\
**Post date:** [April 8, 2011, 3:28am UTC](https://www.ruby-forum.com/t/value-of-expires-header-differ-for-caching-and-content-serving-servers/205430/6 "2011-04-08T03:28:01Z")

</div>

Hello!

On Thu, Apr 07, 2011 at 02:54:32PM -0500, Ryan M. wrote:

> > > set to content.server
> 
> specification. From RFC 2616 section 14.9.3:
> 
> “If a response includes both an Expires header and a max-age  
> directive, the max-age directive overrides the Expires header, even if  
> the Expires header is more restrictive”

Yes, I’m aware of the fact that it’s not exactly correct per  
RFC2616. The excuse is that nginx is HTTP/1.0 client. 😉

> As almost all origin servers send both Expires and Cache-Control in  
> their default configurations, with the Expires header coming first,  
> wouldn’t nginx end up doing the wrong thing most of the time?

I believe most servers either don’t sent Expires/Cache-Control at  
all, or they agree.

If you do really care - workaround is to use proxy\_ignore\_headers.

Maxim D.

---

<div class="post-metadata">

**Author:** ![i.s.ivanov2](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/i/c4cdca/32.png) [@i.s.ivanov2](https://www.ruby-forum.com/u/i.s.ivanov2)\
**Post date:** [April 11, 2011, 7:37am UTC](https://www.ruby-forum.com/t/value-of-expires-header-differ-for-caching-and-content-serving-servers/205430/7 "2011-04-11T07:37:50Z")

</div>

I’ve removed sending max-age directive from php script, and that  
helped.  
Thanks a lot!

Posted at Nginx Forum:

> **[Re: Value of "Expires" header differ for caching and content serving servers](https://forum.nginx.org/read.php?2,188725,190037#msg-190037)**
>
> I have a following servers structure: content.server contains an Apache instance, which processes one simple PHP script. On the same node Nginx is set as a caching proxy to that Apache. caching.server contains Nginx with proxy\_cache directive, with...
