5

I'm trying to set some headers only for specific location blocks in nginx.

The problem I have is that those location blocks contain rewrite statements, which apparently seem to drop the custom headers.

In this example, I have two rules I want:

  • Files inside /static should have expires max; (which sets the headers Cache-Control: max-age=some huge value and Expires: some future date really far off) and have their names be rewritten to something that doesn't contain /static
  • Files everywhere else should have Cache-Control: public (no max-age)

Here's the configuration I tried:

server {
    listen [::]:80;
    root /somepath;
    location /static {
        expires max;
        rewrite /static(.*) /whatever$1;
    }
    add_header Cache-Control public;
}

And having the following directory structure:

/somepath
/somepath/f1.txt
/somepath/static/f2.txt

Then we get the following:

  • f1.txt: Cache-Control: public, no Expires header
  • f2.txt: Cache-Control: public, no Expires header

That's valid for f1.txt but not f2.txt. I want it to be like this:

  • f1.txt: Cache-Control: public, no Expires header
  • f2.txt: Cache-Control: max-age=some huge value, Expires: some future date really far off

The problem, I think, stems from the rewrite /static(.*) /whatever$1; line, which makes nginx cancel the headers it has added so far and then add them again (thus re-adding Cache-Control). As such, a trivial workaround would be this:

server {
    listen [::]:80;
    root /somepath;
    location /static {
        rewrite /static(.*) /whatever$1;
    }
    location /whatever {
        expires max;
    }
    add_header Cache-Control public;
}

The problem is that in my real config file, the rewrite isn't as friendly-looking as that. The rewritten URL is not easily matchable in a way that wouldn't also match some files that shouldn't have expires max, so I can't really use this workaround.

Is there a way to make those headers stick after a rewrite?

EDIT: Here's what my real URLs look like:

location ~ /(?:posts-)?img/.*-res- {
    access_log               off;
    expires                  max;
    rewrite                  "/img/(.*)-res-.{8}(.*)" /img/$1$2;
    rewrite                  "/posts-img/(.*)-res-.{8}(.*)" /posts/$1$2;
}

While I can add a location block for /img which would take care of files rewritten using the first rewrite rule, I cannot add one for the second one (/posts) because some files in /posts are not cacheable resources and thus shouldn't have expires max.

EDIT 2: Full config (or at least containing all the relevant parts):

server {
    listen [::]:80;
    root /somepath;
    server_name domain.tld;
    location ~ /(?:posts-)?img/.*-res- {
        access_log               off;
        expires                  max;
        rewrite                  "/img/(.*)-res-.{8}(.*)" /img/$1$2;
        rewrite                  "/posts-img/(.*)-res-.{8}(.*)" /posts/$1$2;
    }
    add_header Cache-Control public;
}

Directory structure:

/somepath
/somepath/img/f1.png
/somepath/posts/post1.html
/somepath/posts/d1/f2.png
/somepath/posts/d2/f2.png

Expected behavior according to HTTP request:

  • GET /somepath: Serves /somepath with Cache-Control: public
  • GET /somepath/img/f1.png: Serves /somepath/img/f1.png with Cache-Control: public
  • GET /somepath/img/f1-res-whatever.png: Serves /somepath/img/f1.png with the headers sent by expires max
  • GET /somepath/posts/post1.html: Serves /somepath/posts/post1.html with Cache-Control: public
  • GET /somepath/posts/d1/f2.png: Serves /somepath/posts/d1/f2.png with Cache-Control: public
  • GET /somepath/posts-img/d1/f2-res-whatever.png: Serves /somepath/posts/d1/f2.png with the headers sent by expires max
5
  • proxy_pass'ing to self works, but that's not really a solution. Perhaps you could use 'alias'? Dec 26, 2012 at 20:16
  • alias would work in the simple example above but unfortunately it wouldn't work in my real example, because the filename changes (from something-somehash.extension to something.extension) Dec 26, 2012 at 20:51
  • Added how the URLs look like in my real config Dec 26, 2012 at 20:55
  • can you share the full config
    – Sameer
    Dec 27, 2012 at 3:07
  • Added working config and sample directory structure and expected behavior for those files Dec 27, 2012 at 5:03

2 Answers 2

2
+50

This should work (I verified this with somewhat simpler config, though). Igor Sysoev recommends to use regex locations as little as possible, by the way.

    location /img {
        if ($arg_max) { expires max; }
        ...
    }

    location /posts-img {
        if ($arg_max) { expires max; }
        ...
    }

    location ~ /(?:posts-)?img/.*-res- {
        access_log               off;
        expires                  max;
        rewrite                  "/img/(.*)-res-.{8}(.*)" /img/$1$2?max=1;
        rewrite                  "/posts-img/(.*)-res-.{8}(.*)" /posts/$1$2?max=1;
    }
2
  • Very good, also notice that ?max=1 parameter is not exposed to end users, and can be named as you like. Apr 28, 2017 at 16:13
  • However, be wary that if you have other headers set in the first and second location blocks, they won't work... along with many other cases; if is listed as unsafe nginx.com/resources/wiki/start/topics/depth/ifisevil Apr 28, 2017 at 16:19
0

For case insensitive location matching.

location ~* /static/

case insensitive remove the " ***** "

location ~* /static/

Source Nginx location directive documentation

4
  • Sorry but I don't think you understood the question. I'm not asking about case sensitive/insensitive matching here... Dec 24, 2012 at 3:45
  • I think Sameer is right: just change "location /static" to "location ~ /static" Dec 25, 2012 at 16:41
  • You want to append headers based on a condition, so you'll have to match/search for it.
    – Sameer
    Dec 25, 2012 at 20:13
  • The "append header based on a condition" part works, the original URL (/static/something) is matched. What doesn't work is that the added headers do not stick after the rewrite rule. Additionally, because the rewritten URL doesn't match /static (so location ~ /static won't catch it), the header isn't added again after the rewrite. Dec 25, 2012 at 20:41

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .