# Conditional "Expires" header config bug

**URL:** <https://www.ruby-forum.com/t/conditional-expires-header-config-bug/200254>\
**Category:** NGINX\
**Created:** [December 7, 2010, 10:11pm UTC](https://www.ruby-forum.com/t/conditional-expires-header-config-bug/200254 "2010-12-07T22:11:25Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Dobai-Pataky\_BSSSSl](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/77aa72/32.png) [@Dobai-Pataky\_BSSSSl](https://www.ruby-forum.com/u/Dobai-Pataky_BSSSSl)\
**Post date:** [December 7, 2010, 10:11pm UTC](https://www.ruby-forum.com/t/conditional-expires-header-config-bug/200254/1 "2010-12-07T22:11:25Z")

</div>

Hi,

I’m trying to conditionally set the “Expires” header. This tiny  
snippet does not work as expected:

```
location ~* testing/.*\.png$ {
  if ($request_uri ~* "\.png\?[0-9]+$") {
    expires max;
  }
  try_files $uri /testing/oops.png;
}

```

The basic problem is that if the png does not exist and there is a  
query string, I get a 404 instead of oops.png. All other combinations  
seem to work fine. I have read IfIsEvil, but the alternatives  
presented (try\_files, return, redirect) do not cover the case of  
setting headers.

Is this possible with nginx config? Is there some extension module I  
could use to achieve this?

-Greg

P.S. Yes, this is a contrived example. My actual case is to continue  
on to an image generating proxy instead of “oops.png”. but the error  
is the same.

---

<div class="post-metadata">

**Author:** ![greg](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/71c47a/32.png) [@greg](https://www.ruby-forum.com/u/greg)\
**Post date:** [December 7, 2010, 10:37pm UTC](https://www.ruby-forum.com/t/conditional-expires-header-config-bug/200254/2 "2010-12-07T22:37:14Z")

</div>

Hello!

On Tue, Dec 07, 2010 at 01:09:54PM -0800, [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> ```
> }
> 
> ```
> 
> The basic problem is that if the png does not exist and there is a  
> query string, I get a 404 instead of oops.png. All other combinations

This is expected - try\_files isn’t inherited into implicit  
location created by “if”.

> seem to work fine. I have read IfIsEvil, but the alternatives  
> presented (try\_files, return, redirect) do not cover the case of  
> setting headers.

Not “redirect” but “rewrite … last”. Anyway, they are cover the  
case in question.

Generally safe parttern using only “return” looks like the  
following:

```
location ~ \.png$ {
    recursive_error_pages on;
    error_page 418 = @expires;

    if ($args ~ "^[0-9]+$") {
        return 418;
    }

    try_files $uri /testing/oops.png;
}

location @expires {
    expires max;
    try_files $uri /testing/oops.png;
}

location = /testing/oops.png {
    # we don't want expires max on oops
}

```

Maxim D.

---

<div class="post-metadata">

**Author:** ![greg](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/71c47a/32.png) [@greg](https://www.ruby-forum.com/u/greg)\
**Post date:** [December 7, 2010, 10:44pm UTC](https://www.ruby-forum.com/t/conditional-expires-header-config-bug/200254/3 "2010-12-07T22:44:09Z")

</div>

On Tue, Dec 7, 2010 at 1:36 PM, Maxim D. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> > ```
> > }
> > 
> > ```
> > 
> > The basic problem is that if the png does not exist and there is a  
> > query string, I get a 404 instead of oops.png. All other combinations
> 
> This is expected - try\_files isn’t inherited into implicit  
> location created by “if”.

I would very much like some more examples of what you mean here. It’s  
not clear to me why IfIsEvil occurs, or how to think about what the if  
statement is actually doing.

> > seem to work fine. I have read IfIsEvil, but the alternatives  
> > presented (try\_files, return, redirect) do not cover the case of  
> > setting headers.
> 
> Not “redirect” but “rewrite … last”. Anyway, they are cover the  
> case in question.

Er yes, sorry.

> }  
> This is clever! I didn’t know you could set an internal location with  
> error\_page. Not that it isn’t documented, just that I never even  
> considered looking there for that sort of thing since the case I’m  
> using it for is not an error.

Thanks for your help!

-Greg

---

<div class="post-metadata">

**Author:** ![greg](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/71c47a/32.png) [@greg](https://www.ruby-forum.com/u/greg)\
**Post date:** [December 8, 2010, 3:41am UTC](https://www.ruby-forum.com/t/conditional-expires-header-config-bug/200254/4 "2010-12-08T03:41:12Z")

</div>

2010/12/7 Maxim D. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

> > > > ```
> > > > if ($request_uri ~* "\.png\?[0-9]+$") {
> > > > 
> > > > ```
> 
> problems. As in your case - they don’t handle try\_files (which  
> isn’t normally inherited, too).

It all makes so much sense now! If that explanation were on  
[GitHub - nginxinc/nginx-wiki: ARCHIVED -- Source for the now archived NGINX Wiki section of https://www.nginx.com · GitHub](http://wiki.nginx.org/IfIsEvil) perhaps other people could benefit from  
it. Also a list of inherited commands / handlers might be helpful.

I managed to get my config fully working to my satisfaction, with 6  
locations sections and 3 teapot error code jumps (there was more than  
I pasted here, obviously). Thanks again.

-Greg

---

<div class="post-metadata">

**Author:** ![greg](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/71c47a/32.png) [@greg](https://www.ruby-forum.com/u/greg)\
**Post date:** [December 8, 2010, 12:24am UTC](https://www.ruby-forum.com/t/conditional-expires-header-config-bug/200254/5 "2010-12-08T00:24:07Z")

</div>

Hello!

On Tue, Dec 07, 2010 at 01:43:16PM -0800, [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> > > try\_files $uri /testing/oops.png;  
> > > statement is actually doing.  
> > > What “if” statement (inside location) actually doing is creating  
> > > implicit nested location (conditional one), with some hacks to  
> > > make handler modules (proxy\_pass, fastcgi\_pass, …) to be  
> > > inherited into this location (normally they aren’t). Basically  
> > > there are two problems:

1. People expect

location / {  
set $true 1;

```
if ($true) {
    add_header X-Header-One 1;
}

if ($true) {
    add_header X-Header-Two 2;
}

```

}

to return both headers. Though this isn’t something going to  
happen, as actually the above config means something like

location / {  
location …implicit-location-1 {  
add\_header X-Header-One 1;  
}

```
location ...implicit-location-2 {
    add_header X-Header-Two 2;
}

```

}

And as usual nginx will select only one location to process  
request. Hence only X-Header-Two will be returned.

1. Mentioned hacks are incomplete / broken and may cause various  
problems. As in your case - they don’t handle try\_files (which  
isn’t normally inherited, too). E.g. something like

location / {  
try\_files /file @fallback;

```
set $true 1;

if ($true) {
    # nothing
}

```

}

is actually means for nginx something like

location / {  
try\_files /file @fallback;

```
location ...implicit-location-which-matches-everything {
    # no try_files here
}

```

}

and try\_files will not work - as there is no try\_files in location  
which actually used to process request.

More cases are listed on [GitHub - nginxinc/nginx-wiki: ARCHIVED -- Source for the now archived NGINX Wiki section of https://www.nginx.com · GitHub](http://wiki.nginx.org/IfIsEvil).

> > following:  
> > }  
> > This is clever! I didn’t know you could set an internal location with  
> > error\_page. Not that it isn’t documented, just that I never even  
> > considered looking there for that sort of thing since the case I’m  
> > using it for is not an error.

One more hint: using “error\_page fallback” like

log\_not\_found off;  
error\_page 404 = @fallback;

is actually better than try\_files in simple cases. It a) saves  
one stat() syscall and b) isn’t racy (try\_files aproach has race  
between stat() and then open() in static handler, which makes it  
possible to generate 404 anyway - if you happen to delete file  
between stat() and open()).

On the other hand - try\_files is easier to use, especially  
in complex cases.

Maxim D.
