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 haveexpires max;
(which sets the headersCache-Control: max-age=some huge value
andExpires: 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
(nomax-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
, noExpires
headerf2.txt
:Cache-Control: public
, noExpires
header
That's valid for f1.txt
but not f2.txt
. I want it to be like this:
f1.txt
:Cache-Control: public
, noExpires
headerf2.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
withCache-Control: public
GET /somepath/img/f1.png
: Serves/somepath/img/f1.png
withCache-Control: public
GET /somepath/img/f1-res-whatever.png
: Serves/somepath/img/f1.png
with the headers sent byexpires max
GET /somepath/posts/post1.html
: Serves/somepath/posts/post1.html
withCache-Control: public
GET /somepath/posts/d1/f2.png
: Serves/somepath/posts/d1/f2.png
withCache-Control: public
GET /somepath/posts-img/d1/f2-res-whatever.png
: Serves/somepath/posts/d1/f2.png
with the headers sent byexpires max
Best Answer
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.