nginx: Specify custom headers in rewritten location blocks
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;
}
Comments
-
Etienne Perot almost 2 years
I'm trying to set some headers only for specific
location
blocks in nginx.The problem I have is that those
location
blocks containrewrite
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 notf2.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-addingCache-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 haveexpires 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 firstrewrite
rule, I cannot add one for the second one (/posts
) because some files in/posts
are not cacheable resources and thus shouldn't haveexpires 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
-
sendmoreinfo over 11 yearsproxy_pass'ing to self works, but that's not really a solution. Perhaps you could use 'alias'?
-
Etienne Perot over 11 yearsAdded how the URLs look like in my real config
-
Sameer over 11 yearscan you share the full config
-
Etienne Perot over 11 yearsAdded working config and sample directory structure and expected behavior for those files
- Files inside
-
Etienne Perot over 11 yearsSorry but I don't think you understood the question. I'm not asking about case sensitive/insensitive matching here...
-
Andrei Mikhaltsov over 11 yearsI think Sameer is right: just change "location /static" to "location ~ /static"
-
Sameer over 11 yearsYou want to append headers based on a condition, so you'll have to match/search for it.
-
Etienne Perot over 11 yearsThe "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 therewrite
rule. Additionally, because the rewritten URL doesn't match/static
(solocation ~ /static
won't catch it), the header isn't added again after the rewrite. -
giorgiosironi about 7 yearsVery good, also notice that
?max=1
parameter is not exposed to end users, and can be named as you like. -
giorgiosironi about 7 yearsHowever, 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