How to make map-derived variable reflect the current $uri? - nginx

A request for /olddir1/img.jpeg using the following nginx configuration will do a correct internal redirect to /newdir1/img.jpeg but it will leave the variable $extra_uri set to the value it would have during the first rewrite cycle, that is /olddir1/img.jpeg.
Just before executing the rewrite directive, $alias_uri and $extra_uri will have the following values, all correct:
$alias_uri: /newdir1/img.jpeg
$extra_uri: /olddir1/img.jpeg
After the rewrite directive is run, setting $uri to /newdir1/img.jpeg, the variables will have the following values:
$alias_uri: /newdir1/img.jpeg
$extra_uri: /olddir1/img.jpeg
$alias_uri has the correct value (nothing has matched in the map, so the default is being used, i.e. the current $uri). $extra_uri instead has a stale value.
How come $extra_uri isn't set to the new $uri?
server {
listen 80 default_server;
server_name 'test.example.local';
location / {
root '/var/www/test/content';
set $alias_uri $example__alias_uri;
set $extra_uri $example__extra_uri;
if ($alias_uri != $uri) {
rewrite ^ $alias_uri last;
}
add_header X-Alias "uri: >$uri< alias_uri: >$alias_uri<" always;
add_header X-Extra "uri: >$uri< extra_uri: >$extra_uri<" always;
try_files $uri =404;
}
map $uri $example__alias_uri {
default $uri;
~/olddir1(?<file>/[^/]*)$ /newdir1$file;
~/olddir2(?<file>/[^/]*)$ /newdir2$file;
}
map $uri $example__extra_uri {
default $uri;
}

Marking the maps as volatile makes this problem go away.
map $uri $example__alias_uri {
volatile;
default $uri;
~/olddir1(?<file>/[^/]*)$ /newdir1$file;
~/olddir2(?<file>/[^/]*)$ /newdir2$file;
}
map $uri $example__extra_uri {
volatile;
default $uri;
}
It seems that maps are evaluated only once during the very first rewrite phase and never afterwards.

Related

replace part of request_uri before passing it to proxy_pass

i'm trying to replace specific part or request_uri using rewrite, but it won't work for some reason
example url: http://example.com:3000/?soft=55191&src1=changethis&src2=HOME&type=0&id=7700458
server {
server_name example.com;
listen 3000;
location / {
resolver 8.8.8.8;
rewrite ^(?<=&src1=)(.*)(?=&src2)$ changewiththis$1 break;
proxy_pass http://example2.com;
}
}
so the Goal here is to replace the exact string between 'src1=' and '&src2' so it can be passed to proxy_pass with the changed string
The location and rewrite directives use a normalised URI which does not include the query string (anything from the ? onwards).
To manipulate the query string, you will need to look at the $request_uri or $args variables, or the individual parameters using the $arg_ family of variables (e.g. $arg_src1).
The simplest solution may be to use a map directive to manipulate $request_uri before passing the new value upstream.
For example:
map $request_uri $changethis {
default $request_uri;
~(?<prefix>.*[?](|.*&)src1)=[^&]*(?<suffix>.*)$ $prefix=newvalue$suffix;
}
server {
...
location / {
resolver ...;
proxy_pass http://example.com$changethis;
}
}
See this document for details.

NGINX regular expression to match location with optional parameter

I like to match location with two optional parameters so it it matches
/images/my.jpg
/images/my.jpg/200
/images/my.jpg/200/
/images/my.jpg/200/400
/images/my.jpg/200/400/
I am using three match groups so that I can get the matched part in $image, $width and $height variables.
I tried this query. It works only if the full location is given but it does not match if one of the parameter is missing
~ ^/images/(?<img>.+?)/(?<width>\d+)/(?<height>\d+)?$
I also tried which does not work at all
~ ^/images/(?<img>.+?)?(/?<width>\d+)?(/?<height>\d+)?$
please help - I have input data here. https://regex101.com/r/qU7tI7/6
nginx config here
server {
listen 80;
server_name f.myhost.com;
set $sitename f.myhost.com;
root $root_folder/myhost.com/files;
set $path_info "";
location / {
root /var/www/myhost.com/app/api/;
include conf.d/site.template;
add_header X-test "root";
}
location /files {
root /var/www/myhost.com/app/;
include conf.d/site.template;
}
location ~ ^/images/(?<img>[^/]+)(/(?<width>\d*))?(/(?<height>\d*))?$ {
# root /var/www/myhost.com/files/;
alias /var/www/myhost.com/app/test/$image;
# include conf.d/site.template;
add_header X-test "test";
add_header X-width "$width";
add_header X-height "$height";
add_header X-name "$image";
image_filter resize $width -;
image_filter_jpeg_quality 75;
image_filter_buffer 8M;
}
}
Try to try the following regular expression:
/^(\/images\/)([\da-z\.-]+\.[a-z\.]{2,6}|[\d\.]+)\/(\d+)\/(\d+)?$/igm

Disable logging for specific useragent in nginx conf

I want to disable logging for a specific useragent. This is a part of my current conf-file.
if ($http_user_agent ~ (bingbot|AhrefsBot|DotBot|Exabot|Baiduspider|SemrushBot) ) {
return 403;
}
I've tried adding access_log off; but get the following error:
nginx: [emerg] "access_log" directive is not allowed here
I'm assuming this is because I only have a server block. I need a location block also. I've tried the following code:
location / {
if ($http_user_agent ~ (bingbot|AhrefsBot|DotBot|Exabot|Baiduspider|SemrushBot) ) {
return 403;
}
}
But I get the following error:
duplicate location "/"
In my conf-file I already have this code:
location / {
try_files $uri $uri/ =404;
}
Can I combine the two location snippets into one? Or how do I proceed?
As your question indicates, the access_log directive cannot be used within an if block unless enclosed within a location. However, the access_log directive does include an if=condition which can be controlled by a map. There is an example at the end of this section of the manual.
For example:
map $http_user_agent $goodagent {
default 1;
~(bingbot|AhrefsBot|DotBot|Exabot|Baiduspider|SemrushBot) 0;
}
server {
access_log ... if=$goodagent;
if ($goodagent = 0) { return 403; }
...
}
The map directive must be placed outside of the server block. The access_log statement can be placed inside or outside the server block depending on whether it applies to all server blocks or just one.
At the http level declare a map like so.
map $http_user_agent $ignore_status_checks {
default 0;
"~Pingdom.*" 1;
"~*\(StatusCake\)" 1;
"~*mod_pagespeed*" 1;
"~*NodePing*" 1;
}
Then in your server's location block add:
if ($ignore_status_checks) {
access_log off;
}
This will turn off the access_log for anything returns a 1 in the map. Of course, you can do want ever you want in the if.

Conditional url rewrite and proxy

I have a Javascript app, and for Social Sharing puposes I would like to return the result form another server when the facebook crawler visits the page.
Basically, if I detect a Social bot, I would like to do:
GET https://example.net/shared/route/123 -> GET https://rendered.example.net/robots/shared/route/123
I am failing to complete this, as I cannot use proxy_pass or try_files inside an if statement.
What I've tried:
location /shared/route/ {
if ($http_user_agent ~ (?!(facebookexternalhit|Facebot|Twitterbot|Pinterest|Google.*snippet))) {
try_files $uri $uri/ /index.html =404;
}
rewrite /shared/(.*) /robots/$1 break;
proxy_pass https://rendered.example.net/;
}
But I receive the error:
"try_files" directive is not allowed here in /etc/nginx/sites-enabled/webapp:22
And I've also tried
location /shared/route/ {
if ($http_user_agent ~ (?!(facebookexternalhit|Facebot|Twitterbot|Pinterest|Google.*snippet))) {
rewrite /shared(.*) /robots/$1 break;
proxy_pass https://rendered.example.net/;
}
}
But then I receive:
"proxy_pass" cannot have URI part in location given by regular expression, or inside named location, or inside "if" statement, or inside "limit_except" block in /etc/nginx/sites-enabled/app:24
How can I solve this?

try_files always returns 404 with variable

The try_files directive seem to always fail for me:
...
root /images;
location /media {
set $do_resize $uri;
# if ?resize=blah query present, rewrites do_resize to small/$uri
if ($arg_resize != '') {
set $do_resize 'small/${do_resize}';
}
try_files $do_resize $do_resize/ #fallback;
}
# placeholder for image resize server
location #fallback {
add_header Content-Type text/html;
return 200 "not found :/";
}
My folder structure:
| images
| test.jpeg
| small
test.jpeg
When requesting to http://foo/media/test.jpeg, it returns the image correctly. but when requesting http://foo/media/test.jpeg?resize=blah, it returns 404 not found. It should return the test.jpeg image inside the small folder.
Is it possible to try_files with a custom variable at all? I've never seen something else besides $uri being used for the try_files directive.
The if statement does not work inside the location block for some reason, but the use of if inside a location block is known to cause problems. See this application note.
Also, all nginx URIs begin with a leading /. So, the $do_resize inside the if block should be /small$uri.
For example:
set $do_resize $uri;
if ($arg_resize != '') {
set $do_resize /small$uri;
}
location /media {
try_files $do_resize $do_resize/ #fallback;
}

Resources