Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

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
share|improve this question
    
proxy_pass'ing to self works, but that's not really a solution. Perhaps you could use 'alias'? –  sendmoreinfo Dec 26 '12 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) –  Etienne Perot Dec 26 '12 at 20:51
    
Added how the URLs look like in my real config –  Etienne Perot Dec 26 '12 at 20:55
    
can you share the full config –  Sameer Dec 27 '12 at 3:07
    
Added working config and sample directory structure and expected behavior for those files –  Etienne Perot Dec 27 '12 at 5:03
add comment

2 Answers

up vote 1 down vote accepted
+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;
    }
share|improve this answer
    
Thanks, this approach works fine –  Etienne Perot Dec 28 '12 at 23:27
add comment

For case insensitive location matching.

location ~* /static/

case insensitive remove the " * "

location ~* /static/

Source Nginx location directive documentation

share|improve this answer
    
Sorry but I don't think you understood the question. I'm not asking about case sensitive/insensitive matching here... –  Etienne Perot Dec 24 '12 at 3:45
    
I think Sameer is right: just change "location /static" to "location ~ /static" –  Andrei Mikhaltsov Dec 25 '12 at 16:41
    
You want to append headers based on a condition, so you'll have to match/search for it. –  Sameer Dec 25 '12 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. –  Etienne Perot Dec 25 '12 at 20:41
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.