nginx location match rule, without matching trailing characters - nginx

I have the following rule:
location /foo {
Which matches well for the following examples:
mydomain.com/foo
mydomain.com/foo/
mydomain.com/foo/bar?example=true
However it is also matching for
mydomain.com/foobar
I don't want it to match to that last one (/foobar), it should only match if there is either nothing after the foo, or a slash and zero or more characters after it. I've tried location /foo/ { but that does not produce desired results either.
Can anyone shed some light on how to do this?

There are two ways to handle this, use a regular expression location block - or just handle /foo separately from /foo/.
Regular expression location blocks have a different evaluation order and are less efficient than prefix location blocks, so my preferred solution is the exact match location and prefix location.
Generally, /foo just redirects to /foo/, for example:
location = /foo {
return 302 /foo/;
}
location /foo/ {
...
}
See this document for more.

You can create one Location rule to /foobar whether you need it as an exception:
location = /foobar {
....
}
Nginx match first URI by = operator.

Related

Nginx Nested Location Priority

Here is part of my nginx configuration:
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
}
location /wp-content/uploads/ {
location ~ .(aspx|php|jsp|cgi)$ { return 410; }
}
As I understand it, the order of priority in location blocks goes like this:
= (exact match) --> ^~ (preferential prefix) --> ~ (regex) or ~* (case-insensitive regex) --> (prefix - no special character)
I put a PHP file in /wp-content/uploads. I got a 410 response code (which is what I want). But I don't understand why the first location block didn't capture the request, since regex blocks take precedence over prefix.
Furthermore, when two regex locations match, the first declared regex location match takes the request. Yet the latter processes the request here.
Why am I getting the 410 response code for /wp-content/uploads/info.php?!?
I found the answer. There's a little-known secret of nginx location matching.
Here is the order of precedence, according to artfulrobot.uk:
1. Exact string matches location = /foo
2. The longest of any location ^~ ... matches
3. The first regex match that is nested within the single longest matching prefix match!
4. The first other regex match location ~ regex
5. The longest prefix match location /foo
Everybody knows about #1, #2, #4, and #5.
But #3 is the gotcha. That's what is happening here.
You'll want to read the article as he very granularly describes this seemingly undocumented and strange behavior of nginx.

How to set wildcard reverse proxy in Nginx

Suppose you have a request for the following json file.
e.g. https://example.com/aaa/bbb/ccc.json
I want to have Nginx handle 'aaa' as a fixed string, and 'bbb' and 'ccc' as changing strings.
In this case, I want the proxy to work ignoring the 'bbb' path in the URL.
How should I describe this in a lcoation block in Nginx?
location /aaa {
# I want to ignore 'bbb' path and return ccc.json. 'ccc' varies.
alias /mydirectory/aaa;
try_files $url;
}
Locations in nginx can be defined with regular expressions (docs).
Depending on how specific you'd like to be, you might choose ~ for case-sensitive URI matching or ~* for case-insensitive URI matching. ^~ is also available as a prefix match, but note a match here will stop the engine from performing additional regex matching. Caveat emptor.
Something like this would be a relatively straightforward way to "absorb" the bbb (or any other combo of 1 or more letters, upper and lower case):
location ~ ^\/aaa\/[a-zA-Z]+ {
...
}
In order to capture the ccc.json, you'll want to add a capturing group as well:
location ~ ^\/aaa\/[a-zA-Z]+\/([\w\.]+) {
return 200 '$1';
}
Bonus: if you're using PCRE 7+ (docs), you can name your capture:
location ~ ^\/aaa\/[a-zA-Z]+\/(?<filename>[\w\.]+) {
return 200 '$filename';
}

Nginx redirect rule with arabic characters

I need to create an Nginx rule that redirects /ae/ar/أسافر%20من%20أجل/ to /ae/en. I've tried with these two rules:
rewrite (?i)^/ae/ar/أسافر%20من%20أجل(.*)$ http://$host/ae/en? permanent;
rewrite (?i)^/ae/ar/%D8%A3%D8%B3%D8%A7%D9%81%D8%B1%20%D9%85%D9%86%20%D8%A3%D8%AC%D9%84(.*)$ https://$host/ae/en? permanent;
But no one worked. Looks like the rule is ignored, any clues to make this rule work properly?
Thanks in advance!
Rewrite does not see the percent encoded characters. You should be able to leave the arabic characters as they are (UTF-8 characters within the configuration file) and replace the %20 with a single space. The entire regular expression should be surrounded with "s.
Alternatively, use a location statement:
location = "/ae/ar/أسافر من أجل/" { return 301 /ae/en; }

nginx escaping (+) sign in rewrite rule

I am trying to redirect some hits based on the image name in my Nginx configuration.
This is how it looks now:
location / {
rewrite ^/static-v3/(.*)/creditcard_sslseals_public.png https://somenewurl.com/credit-card-seals.png permanent;
rewrite ^/static-v3/(.*)/creditcard+sslseals_public.png https://somenewurl.com/credit-card-seals.png permanent;
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}
So the first condition with the filename creditcard_sslseals_public.png works properly, but the second is not working since there is a + sign in the image name like creditcard+sslseals_public.png, so I am getting a 404.
How can I escape the + in the second condition, but keep the regex before ^/static-v3/(.*)?
The + has a special meaning in a regular expression and needs to be escaped.
One option is to use a backslash to escape the character:
^/static-v3/(.*)/creditcard\+sslseals_public.png
Alternatively, create a character class containing just one character:
^/static-v3/(.*)/creditcard[+]sslseals_public.png

Nginx Regular expression to match first segment of a url

I have some urls like these:
domain.com/article/1234-newstitle
domain.com/article/1234-newstitle/
domain.com/article/1234-newstitle/abc
domain.com/article/1234-newstitle/xpto/abc
domain.com/article/1234-newstitle/qwerty/abc/xyz
I want to catch only the /1234-newstitle/ to redirect these urls and ignore everything after the 1º slash (whatever the number of segments) so I can have:
domain.com/news/newstitle-1234/
The best I could get is:
rewrite ^article/(\d+)-(.*)[^\/]* /new/$2-$1/ permanent;
but $2 matches everything after 1234-
How am I able to match only the first segment "1234-newstitle" and ignore the rest?
Looks like you've got a greedy match after the hyphen. Try this:
rewrite ^/article/(\d+)-([^/]+) /new/$2-$1/ permanent;

Resources