How can I match any path containing a dot with nginx? - nginx

I am trying to implement a proxy using the nginx configuration
The idea is to have a http server hosting my website (my SPA). and having one route on my http server pointing to another api.
this is my nginw configuration file below
client_max_body_size 100M;
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html index.htm;
location ^~ /proxyapi/ {
proxy_read_timeout 180s;
proxy_send_timeout 180s;
proxy_pass http://localhost:5000/;
}
location ~* /static/* {
try_files $uri =404;
expires 1y;
access_log off;
add_header Cache-Control "public";
}
# Any route that doesn't have a file extension (e.g. /devices)
location / {
try_files $uri $uri/ /index.html;
}
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
it works fine most of the time
when I call http://my-nginx-host/proxyapi/search/login, it works...
unless there is a dot in the login
http://my-nginx-host/proxyapi/search/login => works fine
http://my-nginx-host/proxyapi/search/log.in => fails with a "404 resource not found"
is there any way to make it work?
I couldn't find any solution

Matching any dot in URI can be done with this:
location ~* \..*$ {
# try_files $uri $uri/ =404;
# ...
}
Let's take the regex apart:
~* Tells nginx to do case-insensitive regex matching (~ for case-sensitive)
\. Matches to literal dot symbol .
.* The dot here equals to any symbol except whitespace, asterisk modifies the preceding symbol to "match as many of this as possible"
$ Matches end of line
You can use this regex if you want to match even "malformed" uris like log.in
EDIT: In your situation, you would have to place this regex after your
location ~* /static/* so it won't match uris with dots like /static/image.png. See notes for explanation.
Notes
Take in mind this location block will match any dot anywhere in passed URI. So it will match URIs like these /a.ssets/images/, /assets/favicon.ico too. Any non-terminating locations (ones without ^~ or =) will not be used even if they should match, if the dot regex matches and it's first matching regex location it takes precedence over anything else.
Important snippet from nginx's docs about matching preference of location:
Regular expressions are specified with the preceding “~*” modifier (for case-insensitive matching), or the “~” modifier (for case-sensitive matching). To find location matching a given request, nginx first checks locations defined using the prefix strings (prefix locations). Among them, the location with the longest matching prefix is selected and remembered. Then regular expressions are checked, in the order of their appearance in the configuration file. The search of regular expressions terminates on the first match, and the corresponding configuration is used. If no match with a regular expression is found then the configuration of the prefix location remembered earlier is used.

I was trying your solution but no luck. My problem is that context root location has a dot
https://example.com/something.else/test/me
I tried to match but no luck, I got 404 every time.
These are just some regex variation that I tried:
#location ~ /[\w.]+ {
#location ^~ ^/[\w.]+/[\w]+.* {
#location ^~ /[\w.]+/[\w]+.* {
#location ^~ [\w.]+/[\w]+.* {
#location ~* [\w.]+/[\w]+.* {
#location ~ ^[\w.]+/[\w]+.* {
location ^~ /something\.else {

Related

NGINX ignore hash css file and fetch any css that exists

I'm cache-busting with hashed css files (app-123456.css). The css file requests are proxied to a cdn with nginx. I need to keep the files statically named on the cdn, as there is a requirement to allow the customer to modify some css and re-upload the file. How can I pass the hashed file request to cdn and return the statically named file? For example a request to app-123456.css would return app.css, if it existed on the cdn. I'm trying to use try files but have been unsuccessful. Will cache-busting still work in this scenario, if the returned file is statically named? Thanks for any help.
location ~* (.+)\.(?:\d+)\.(css)$ {
try_files $uri $1.$2 #styles;
}
location #styles {
autoindex on;
proxy_pass http://[url].net; # needs to go to http://[url].net/styles/
}
EDIT
location ~* (.+)-(?:\d+)\.(css)$ {
try_files $uri $1.$2 #styles;
}
location #styles {
autoindex on;
rewrite ^(.+)-(?:\d+)\.(css)$ /styles$1.$2 break;
proxy_pass http://[url].net; # needs to go to http://[url].net/styles/
}
Fixed
^(.+)\-([a-zA-Z0-9]*)\.(css)$
You need to modify the URI within the named location before passing it upstream with proxy_pass. This can be accomplished using a rewrite...break statement. See this document for details.
For example, using your updated regular expression:
location ~* ^(.+)\-(?:[a-zA-Z0-9]*)\.(css)$ {
try_files $uri $1.$2 #styles;
}
location #styles {
rewrite ^(.+)\-(?:[a-zA-Z0-9]*)\.(css)$ /styles$1.$2 break;
proxy_pass http://...;
}
The above solution basically applies the same regular expression to the URI twice, which seems inefficient and redundant.
If the /styles/ URI prefix is unique to the upstream server, you could perform the translation in the original try_files statement. See this document for details.
For example:
location ~* ^(.+)\-(?:[a-zA-Z0-9]*)\.(css)$ {
try_files $uri $1.$2 /styles$1.$2$is_args$args;
}
location ^~ /styles/ {
internal;
proxy_pass http://...;
}
The ^~ operator give the prefix location a high precedence (see this document for details) and the internal directive prevents the URI from being directly accessible (see this document for details).

Nginx two angular apps

I'd like to add separated angular app under a specific path (the path should be at the end of an URL to be matched) - I want to keep both versions which are current and a new one but the new should only be available under the specified path. I tried using alias + try_files. My config:
server {
listen 80;
root /dir/project1
server_name ...;
index index.html;
location ~ /path {
alias /dir/project2
try_files $uri $uri/ /index.html;
}
The thing is that when try_files fires up, it takes the path from the root directive - not from the alias. How to fix it? I can only add I cannot use proxy_pass here and root instead of the alias does not work either as it adds paths etc.
The alias directive works differently when placed inside a regular expression location, but you should probably be using a prefix location anyway. See this document for details.
Also, the use of alias and try_files together can cause problems (see this long standing bug).
You are rewriting the URI to /index.html which is the wrong application, and should instead be /path/index.html.
Try:
location ^~ /path {
alias /dir/project2;
if (!-e $request_filename) {
rewrite ^ /path/index.html last;
}
}
See this caution on the use of if.

Strange Nginx behavior with trailing slashes

I've got a quite interesting behavior. I want to avoid trailing slashes in URL's on my site. I've put rewrite ^/(.*)/$ /$1 permanent; rule into my server block, so
https://example.com/something/,
https://example.com/something////
redirect to
https://example.com/something;
and
https://example.com/
redirects to
https://example.com
But https://example.com//// is redirected to ... https://enjoygifts.ru//// (actually don't redirected, it's 200 code). Why?
Here is my server block:
server {
listen 443 ssl;
...
... ssl directives
...
root /var/www/mysite.com;
index index.php;
server_name mysite.com;
rewrite ^/(.*)/$ /$1 permanent;
location / {
rewrite ^/.*$ /index.php last;
}
location ~ ^/index.php {
try_files $uri =404;
include /etc/nginx/fastcgi.conf;
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
}
location ~ ^/storage/app/uploads/public { try_files $uri 404; }
...
... lot of similar location blocks
...
}
https://example.com doesn't really exist, the root URI is / - how it's displayed in the browser's address bar is browser dependent - some will automatically show the solitary / whereas others will remove a solitary /.
So you cannot redirect from https://example.com/ to https://example.com - it will be silently interpreted as a redirect from https://example.com/ to https://example.com/.
Nginx uses a normalized URI when evaluating location and rewrite statements, and generating the $uri variable. Multiple consecutive occurrences of / are folded into a single /.
Although the regular expression ^/(.*)/$ matches the URI //, the statement will never see it. Because Nginx has already normalised that URI to /, which does not match the regular expression.
If a root URI with multiple /s is a problem, apply a regular expression to the $request_uri variable, which contains the original URI before normalization and also includes the query string (if any).
For example:
if ($request_uri ~ "^/{2,}(\?|$)") {
return 301 /$is_args$args;
}
This can be placed inside your location / {...} block. See this caution on the use of if.

nginx: block directory but allow access to subdirectory

I am trying to figure out how I can block a particular directory with nginx, while allowing the resources (including any subdirectories) from that directory to be accessible. For example,
/static should be blocked, however
/static/whatever.css should be accessible.
I tried something like this, but it didn't work:
location ^~ /static/ {
deny all;
return 404;
}
location ^~ /static/* {
allow all;
}
Is there a way to do this?
That is incorrect syntax. You probably meant to write:
location = /static/ {
deny all;
}
location /static/ {
}
The return 404 is redundant as the deny all will return 403. The allow all is redundant as it is the default action. I have remove the ^~ modifier, which should be added to prevent the location being overridden by any potential regex location. See this document for location syntax.
However, it seems that the only URI you want to deny is the directory itself, perhaps to disable an inherited index directive. This may also work (with or without the ^~ modifier - see above):
location /static/ {
try_files $uri =404;
}
By omitting the $uri/ element (note trailing /) from the try_files directive, the index directive is ignored. See this document for more.

Nginx : Serve JPG images from different root and location

I have 3 different stockage area : "avatars" , "articles", "trends" where I store my images.
I want to "link" the URL "/trends/123.jpg" to trends folder , "/avatars/23.jpg" to avatar and so on.
Configuration 1:
server
{
listen 8089;
server_name localhost;
root /var/www;
location /trends/
{
alias /var/storage/hottrend/;
}
location ~* ^.+\.(jpeg|gif|png|jpg)
{
add_header Cache-control "public";
access_log off;
expires 90d;
}
}
Configuration 1 : "GET /trends/123.jpg" never match /trends/ location, why ?
Configuration 2:
server
{
listen 8089;
server_name localhost;
root /var/www;
location ~ ^/trends/(.*)\.jpg$
{
rewrite ^/trends/(.*)$ /$1 break;
root /var/storage/hottrend;
}
location ~* ^.+\.(jpeg|gif|png|jpg)
{
add_header Cache-control "public";
access_log off;
expires 90d;
}
}
Configuration 2: The last rule with the caching stuff is not matched. What is the best approach to server JPG files from different location/root ?
The two configurations have different but related problems. The two issues are:
the order in locations are matched; and
what happens when a location is matched.
I'll first explain how it works, and then I'll address your configurations.
How it works
Location matching
You can find the details on this nginx wiki page, but I have always found the wording to be confusing. (It mixes implementation details in the description of behaviour.) What it means is that locations are matched in the following order:
exact matches like location = /robots.txt
eager non-regex prefixes like location ^~ /trends/
regex matches like location ~* \.(jpg|png), or case-sensitive location ~ \.(jpg|png)
lazy non-regex prefixes like location /trends/ or location /
If multiple regular expressions match, then the first match beats the others. If multiple non-regex prefix match, I think it selects the most specific match -- I'll check this and update.
Location behaviour
A matching location is responsible for serving the designated content. It is also responsible for providing cache-control headers and so on. You can have a location that matches particular URL patterns to apply specific headers, but that location must also serve the content. If it cannot serve the content, you will most likely get an 404 error -- it won't look for other matching locations.
Lastly, be extra careful if you have a rewrite within a location. An internal redirect can happen earlier than some directives, in which case, those directive may not apply before the redirect causes the locations to be searched again.
Configuration 1
Your trends location is a lazy non-regex prefix, so it would only match if the regex location fails to match. You can fix this by using an eager non-regex match, such as
location ^~ /trends {
...
}
However, doing so will highlight the other configuration problem.
Configuration 2
You have two locations that could potentially match jpg files. Only one will succeed. If the first matches, then the cache control of the second location won't be applied. If the second matches, then the alias won't take effect.
The fix is to make sure that all directives needed are applied within the location that matches. You can either be explicit in one file, such as
location ^~ /trends
{
alias /var/storage/hottrend;
add_header Cache-control "public";
access_log off;
expires 90d;
}
location ~* ^.+\.(jpeg|gif|png|jpg)
{
add_header Cache-control "public";
access_log off;
expires 90d;
}
The neater solution for managing directives that must be applied to several locations is to factor those details into another file, and then include it at both locations. (Mohammad AbuShady did so in his example.) Something like this:
# Inside your main .conf
location ^~ /trends
{
alias /var/storage/hottrend;
include image-headers.conf;
}
location ~* ^.+\.(jpeg|gif|png|jpg)
{
include image-headers.conf;
}
# Inside image-headers.conf
add_header Cache-control "public";
access_log off;
expires 90d;
well I don't usually use alias but I think this is a good example to use alias for, you can put your caching settings inside /etc/nginx/image_caching.conf. If trends is only used for images i would not try to complicate the regex and assume that it always has images
location /trends {
alias /var/storage/trends;
include /etc/nginx/image_caching.conf;
}
And for your example config, you should have not added /trends inside root /var/storage/trends; because it's already used in the location, so i think it would show 404 becuase it's looking for /var/storage/trends/trends/123.jpg
Nginx checks locations defined as regular expressions in the order in which they appear in the configuration file and uses the location with the first matching expression.
Therefore, you should make sure location ~\.jpg$ appears before any other locations defined as regular expressions

Resources