Dynamic location block variable to string - nginx

I am trying to generate a single nginx location block that dymically changes depending on a captured matching location
location ~*\/(\w+)/archive {
fancyindex_footer "/fancyindex/footer-{$1}.html";
}
So if the location is /something/archive I want the string to say /fancyindex/footer-something.html
It's a simple request but I'm struggling to get it done or even being able to debug

Well, I've made some testing. Indeed, the fancyindex_footer directive can't interpolate variables from its parameter. Most likely the reason is that it is based on add_after_body directive from the ngx_http_addition_module, and that one can't do it too. However using a named capture group the following workaround should work:
location ~* /(?<idx>\w+)/archive {
# by using a named capture group, we make our prefix to be captured with the
# '$idx' variable rather than '$1' one, to be used later in the other location
add_after_body /fancyindex/;
# '/fancyindex/` string treated as a subrequest URI
}
location /fancyindex/ {
# don't allow external access, use only as internal location
internal;
# use the full path to the 'fancyindex' folder as a root here
root /full/path/to/fancyindex;
# rewrite any URI to the '/footer-$idx.html' and serve it as a footer file
rewrite ^ /footer-$idx.html break;
}
If I misunderstood your question and you just want to append a /fancyindex/footer-something.html string to your output instead of /fancyindex/footer-something.html file contents, change the internal location to
location /fancyindex/ {
internal;
return 200 "/fancyindex/footer-$idx.html";
}

Related

Nginx deny if location contains

How can I deny access to nginx if the path contains /local or /local-int to all networks except the local one?
For example https://example.com/api/local/settings. I tried this, but when accessed locally, the request goes to /etc/nginx/html/api/local/settings,and not to the desired backend
location = (local|local-int) {
allow 10.150.0.0/16;
allow 10.160.0.0/16;
allow 10.170.0.0/16;
deny all;
}
I have about 20 such sites, and I'm trying to come up with a solution that would not be tied to a specific location
I summarize: if I access a site from allowed ip, then it should show the page to which I am accessing, and if from a deny list, then 403
Config example:
server {
listen ip:80;
listen ip:443 ssl;
server_name test.com;
if_modified_since off;
location /api {
proxy_pass https://api.example.com;
}
location ~ (\/local) {
allow 10.150.0.0/16;
allow 10.160.0.0/16;
allow 10.170.0.0/16;
deny all;
}
}
This will simply work with both of your locations, since both starts with /local
location ~ (\/local) {
allow 10.150.0.0/16;
allow 10.160.0.0/16;
allow 10.170.0.0/16;
deny all;
}
Nginx takes a = location modifier as an exact match (docs are here). If you want to make a location that will catch every URI containing /local substring (obviously including /local-int), you can use a regex one:
location ~ /local {
...
}
The ^~ modifier makes the location block in #user973254 answer (original answer version, already fixed) a prefix one with the greater priority than any regex locations, so it will overtake only the URIs starting with /local (obviously not including /api/local/settings from your example).
However if your web backend requires an additional URI processing (which is a most common case nowadays), you'll need at least to replicate your main location behavior with this new location. Fortunately, there is a way to avoid such a problems, and can be easily applied to an arbitrary number of sites as you ask for in your original question. You can check required conditions to make a decision for blocking the request or not using the (very powerful) map block feature. And since we want to match address against a list of subnets, we will use a chain of map and geo blocks. To use regexes (PRCE/PCRE2 syntax) for a map block match use a ~ string prefix (~* for case-insensitive match), strings containing some special characters (e.g. curly braces) should be single- or double-qouted. Here is a generic example (you'll need only the first line of the following map block to fulfill your question requirements):
map $uri $restricted {
~/local 1; # regex to match any URI containing '/local' substring
~^/private/ 1; # regex to match any URI starting with '/private'
~*\.jpe?g$ 1; # regex to match any URI ending with '.jpg' or '.jpeg' (case-insensitive)
/some/protected/page/ 1; # exact URI match (string isn't starting with '~')
... any number of additional rules here
default 0;
}
geo $deny {
10.150.0.0/16 0;
10.160.0.0/16 0;
10.170.0.0/16 0;
default $restricted;
}
server {
...
if ($deny) { return 403; }
...
}
You can swap the logic to check the URI first (it can be some performance impact since the regex matching will be performed for every request including requests from the non-restricted networks, however if the majority of requests come from public addresses, there will be no significant difference). That way you can have a common non-restricted subnes list and per-site URI lists:
geo $restricted {
10.150.0.0/16 0;
10.160.0.0/16 0;
10.170.0.0/16 0;
default 1;
}
map $uri $deny1 {
~/local $restricted;
default 0;
}
map $uri $deny2 {
~^/admin $restricted;
default 0;
}
server {
server_name site1.com;
if ($deny1) { return 403; }
...
}
server {
server_name site2.com;
if ($deny2) { return 403; }
...
}
Of course, you are not limited to use 403 return code using this solution (which is the case when you are using allow/deny directives). It also has nothing to do with the famous "If is evil" article since this if is used in server context.

how to make nginx location match path both with slash and without slash?

currently i have this location in my nginx file, it does not work with http://mydomain/ab/cd. How can i make the browser to go to the same page when user type both http://mydomain/ab/cd and http://mydomain/ab/cd/?
location /ab/cd/ {
}
The fastest, in terms of performance, is simply two exact locations:
location = /ab/cd {
...
}
location = /ab/cd/ {
...
}
You can try
location ~* ^/ab/cd(|\/) {...}
It is a prefix matching regex that checks if it has trailing slash or not.

nginx server: how to remove first directory from URL

Can anybody please help me to remove first directory name from URL?
My Image location is _data/hotel/3/15377/hotel_image.jpg
But Image path gets changed due to relative URL in code and it become to something like this.
example.com/france/_data/hotel/3/15377/hotel_image.jpg
example.com/usa/_data/hotel/3/15377/hotel_image.jpg
example.com/india/_data/hotel/3/15377/hotel_image.jpg
is their any possibilities to remove dynamic country name from above URL
If you want to rewrite only this particular URL, you can use this location block in your config:
location ~ /[a-z]+/_data/hotel/3/15377/hotel_image.jpg {
try_files /_data/hotel/3/15377/hotel_image.jpg;
}
If you want to rewrite all URLs which lead to /<country>/_data/..., you can use:
location ~ /[a-z]+/_data/(.+) {
try_files /_data/$1;
}
or for stricter URL checking:
location ~ /(?:france|usa|india)/_data/(.+) {
try_files /_data/$1;
}
#Ivan Shatsky's answer is great for files but also if we want to redirect a general url is better if you use the rewrite directive.
Depending where you define the rewrite directive you have two ways to implement it:
A. In the server context
server {
...
rewrite ^/[a-z]+/_data/(.+)$ /_data/$1 last;
...
}
B. In the location context
location ~ /[a-z]+/_data/(.+) {
rewrite ^/[a-z]+/_data/(.+)$ /_data/$1 break;
proxy_pass http://backend;
}
Teo, why did you change the flag to break?* Because, if this directive is put inside of a location context, the last flag might make nginx to run 10 cycles and return the 500 error.
Note:
Remember not add / at the end of the proxy_pass directive. This example wont work:
...
proxy_pass http://backend/;
...

how to get url parameter and pass to proxy_pass using nginx

I need to get the parameter from an URL, for example, abc=MY_STRING:
https://my-address/test?abc=MY_STRING
And at the reverse proxy (my-address), is configured like this:
location /test?(.*) {
proxy_pass http://local-server:1234/test?$args
}
but it is not working.
I tried another configuration:
location /test?(.*) {
proxy_pass http://local-server:1234/test?$1
}
but not worked too.
You cannot match the query string part of the URI with a location or rewrite statement, as it is not part of the normalized URI.
But you don't need to. The URI (complete with query string) will be passed upstream
unless you redirect it using a rewrite or try_files statement.
For example:
location /test {
proxy_pass http://localhost:1234;
}
The URI /test?abc=MY_STRING will match the location and be passed to localhost:1234 exactly the same. See this document for more.

About priority of nginx location / {} and location = {}

When I study about the nginx location configuration, I have some questions.Here is my example.
the files structure is like this:
test1/index.html
test2/index.html
and the nginx.conf location part is like below:
location = / {
root test1;
index index.html;
# deny all;
}
location / {
root test2;
index index.html;
}
the question is , when i issue curl -v http://host/, I get the page of test2/index.html , but when I get rid of the # in the location = / {} part,
the result will be 403 forbidden. can anyone explain why ? when both the location = same_uri {A} and location same_uri {B} are in the configuration file,which configuration will match[A or B]? Thank you very much.
http://nginx.org/en/docs/http/ngx_http_core_module.html#location
When you request the URI /, nginx will process two requests.
The first request (for the URI /) is processed by the location = / block, because that has highest precedence. The function of that block is to change the request to /index.html and restart the search for a matching location block.
The second request (for the URI /index.html) is processed by the location / block, because that matches any URI that does not match a more specific location.
So the final response comes from the second location block, but both blocks are involved in evaluating access.
See this document for location syntax and this document on the index directive.

Resources