Strange Nginx behavior with trailing slashes - nginx

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.

Related

NGINX Only path equals to without trailing slash and starts with path with trailing slash

I'm encountering an annoying error when it comes to my current NGINX app configuration.
I have a static web app which I am indexing on the path /admin/*. I want the index to be available on /admin, with and without a trailing slash, as well as available on a wildcard /admin/* (anything after the trailing slash).
The issue I am facing is that the index is accessable when appending anything after the admin path, for example /adminA/example.
The original NGINX configuration was as follows:
location /admin {
alias /home/user/app/static;
index index.html;
try_files $uri $uri/ /index.html;
}
The best I've been able to implement to stop this at the moment is as follows, however i'm sure it can be done more efficiently:
location = /admin {
alias /home/user/app/static;
index index.html;
try_files $uri $uri/ /index.html;
}
location /admin/ {
alias /home/user/app/static/;
index index.html;
try_files $uri $uri/ /admin/index.html;
}
The two location blocks are already efficient, but you could eliminate the redundant code in the first block by redirecting to the second.
Using an internal redirect will be invisible to the browser. For example:
location = /admin {
rewrite ^ /admin/ last;
}
location /admin/ {
...
}
Or use permanent instead of last for an external redirect, which will change the browser's address bar from /admin to /admin/. See the rewrite documentation.

Removing all slashes at the end of the url

I am trying to remove any trailing slashes from the url. This works everywhere except the homepage.
Here's where it works:
sub.example.com/ => sub.example.com
sub.example.com/test/ => sub.example.com/test
sub.example.com/test/// => sub.example.com/test
Here's where it doesn't work:
sub.example.com/// => sub.example.com///
My config file:
server {
server_name sub.example.com;
root /var/www/example.com/;
index index.php;
charset utf-8;
rewrite ^/(.*)/$ /$1 permanent;
location / {
try_files $uri /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
listen 443 ssl;
#SSL settings
}
Looks like you don't understand some very important parts going under the hood.
1. HTTP GET request can't contain an empty string as a path.
When you type sub.example.com/ at your browser address bar, it is a browser who hides a trailing slash. This is applied only for the root requests, in any other case (including sub.example.com//, sub.example.com/test/, sub.example.com/test//, etc.) you'll see the full path at the address bar. No matter if you type that slash or not, HTTP request issued by the browser will look like
GET / HTTP/1.1
Host: sub.example.com
...
2. rewrite nginx directive works with the normalized URI.
Both location and rewrite directive works with so-called normalized URIs:
The matching is performed against a normalized URI, after decoding the text encoded in the “%XX” form, resolving references to relative path components “.” and “..”, and possible compression of two or more adjacent slashes into a single slash.
That means, for all the requests like sub.example.com/test/, sub.example.com/test//, sub.example.com/test///, etc., nginx see the normalized request URI as /test/ (that's the reason your rewrite rule works in a single step rather than the four-step sub.example.com/test/// -> sub.example.com/test// -> sub.example.com/test/ -> sub.example.com/test loop).
And the same is true for any of the sub.example.com/, sub.example.com//, sub.example.com///, etc. requests, the normalized URI will be seen by nginx as / making any rewrite rule unusable.
However, slashes compression can be turned off using the merge_slashes directive (read the security considerations). And to prevent multiply redirects where each redirect removes only a single trailing slash, use a non-greedy * and a greedy + quantifiers for your regex pattern:
merge_slashes off;
rewrite ^(/.*?)/+$ $1 permanent;

Nginx - homepage redirects to /index

I am trying to configure nginx to serve pure static html pages. I am using Ubuntu 18.04.
The issue is as follows:
When trying to enter my website by url: http://www.mywebsite.com/ it changes URL automatically to http://www.mywebsite.com/index .
When using http://www.mywebsite.com/index.html it also changes URL to http://www.mywebsite.com/index - but I assume it's because of the rewrite rule which removes the .html extension from uri.
I would like to remove the silly "/index" ending when using both "/" and "index.html". I have found a solution, but not sure though if it's a "proper" one:
# If URI equals '/' then find index.html static page
location = / {
try_files /index.html $uri =404;
}
# After rewrite homepage URI equals '/index', it rewrites it to '/'
location = /index {
rewrite /index / permanent;
}
I have attempted to configure it by using simple try_files without any rewrites, returns etc. But still it always changed URI to "/index" no matter what, for the homepage.
My configs:
sites-available/mywebsite.com content
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
# remove the .html extension from request URI
rewrite ^(/.*)\.html(\?.*)?$ $1$2 permanent;
root /var/www/mywebsite.com;
index index.html;
# redirect rules for homepage '/', removes 'index' from URI
include /etc/nginx/sites-available/conf/redirects-homepage-template.conf;
# rule for any URI content. Searches for static file with .html extension or exact URI content file name. If not found, returns 404.
location / {
try_files $uri.html $uri =404;
}
}
redirects-homepage-template.conf content
# Rules for handling homepage redirects
# If URI equals '/' then find index.html static page
location = / {
try_files /index.html $uri =404;
}
# After rewrite homepage URI equals '/index', it rewrites it to '/'
location = /index {
rewrite /index / permanent;
}
The website is meant to serve only static content and it's suppose to work as fast as possible. I wonder about the consequences of my solution and if there's any better way of removing the silly "/index" from homepage.
Haven't configured domain yet, using bare ip address for testing purpose.

nginx rewrite nice urls with homemade CMS

I have a home-made CMS, serving a site which I inherited. I'm not really familiar with nginx rewrite rules, although I could set up tiny URLs. Here is my relevant part of the configuration:
*location / {
index index.php index.html;
root /var/www/www.valami.hu;
try_files $uri $uri/ #seo;
}
location #seo {
rewrite ^/([a-z]+)$ /index.php?oldal=$1 last;
break;
}*
The problem is that the site has a blog which is located on blogspot.com and the stuff from the blog is taken from there. So what I need help with is a rule for this sort of URL:
http://www.valami.hu/index.php?oldal=blog&options=2012/01/some-title-here.html
So, it would be fine like:
http://www.valami.hu/blog/2012/01/some-title-here
The most important is the first rule should be work also as it is more frequently used.
This is actually trivial. Watch and learn!
location / {
try_files $uri $uri/ #site;
}
location #site {
rewrite ^/blog/(.+)$ /index.php?oldal=blog&options=$1 last;
rewrite ^(.+)$ /index.php?oldal=$1 last;
}
The order makes all the difference. You can also do it by removing the last flag and redirecting to /blog with the options query string parameter explicitely set. No if is needed.
well seems we only have 2 cases, the /blog and the non /blog, I'd write 2 location blocks
location ~ ^/blog/(.*) {
try_files $uri /index.php?oldal=blog&options=$1;
}
location ~ /(.*) {
try_files $uri /index.php?oldal=$1;
}
I would have used just / and $request_uri in the second location but that would put a preceeding / in olda1, if that wouldn't matter with you then i'd prefer that method, cause it doesn't involve regex.
About index index.php index.html; and root /var/www/www.valami.hu;, it's better if you move them to the server block instead of the location block, if possible of course.

rewrite rule nginx

Can someone help me for my nginx rewrite rule. I have the problem like this
if file not found in www.abc.com/name_dir/* it will redirect to www.abc.com/name_dir/index.php .
for example :
not found in www.abc.com/xxx/* redirect to www.abc.com/xxx/index.php
not found in www.abc.com/yyy/* redirect to www.abc.com/yyy/index.php
not found in www.abc.com/zzz/* redirect to www.abc.com/zzz/index.php
not found in www.abc.com/kk/* redirect to www.abc.com/kkk/index.php
...
the problem i have thousand of name_dir. I have nginx.conf like this
if (-f $request_filename) {
break;
}
if (-d $request_filename) {
rewrite (^.+$) $1/
break;
}
if (!-e $request_filename) {
rewrite ^/xxx/(.*)$ /xxx/index.php?$1 last;
rewrite ^.+?(/.*\.php)$ $1 last;
}
In configuration above only redirect name_dir xxx. How rewrite rule to redirect all directory ?
Thank for your help
You want to use try_files to check for the existence of files instead of if statements here (because If's are Evil in Nginx).
To to a single directory, it would be like:
location /xxx/{
try_files $uri $uri/ /xxx/index.php;
index index.php
}
What this does is try the uri as a file first. If that doesn't work, it'll try as a directory. If neither work, it'll default to index.php of /xxx/. The extra index line is to keep it from showing a blank page if you go directly to whatever.com/xxx
Using regex, we can expand this rule to work with more than one directory:
location ~* ^(/.*)/{
try_files $uri $uri/ $1/index.php?$uri&$args;
index index.php
}
This should grab the full directory structure and rout it to the appropriate index.
abc.com/yyy/nonexistant.php ==> abc.com/yyy/index.php
abc.com/yyy/zzz/nonexistant.php ==> abc.com/yyy/zzz/index.php
If you only wanted the second example to go to yyy/index.php, use this regex in the location instead:
^(/.*?)/

Resources