Removing all slashes at the end of the url - nginx

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;

Related

Nginx remove URL path and place it as a query parameter

I have a URL like so: https://example.org/v2?product=lifesum and I need to rewrite it to be: https://example.org?version=v2&product=lifesum. The URL may have more or less query params, so I need to keep all of those. Also, the /v2 may actually not be present, so I need to handle those cases. Here are some examples of how this should be rewritten:
https://example.org/v2?product=lifesum ->
https://example.org?version=v2&product=lifesum
https://example.org?product=lifesum ->
https://example.org?product=lifesum
https://example.org/v13/foo/bar?product=lifesum -> https://example.org/foo/bar?version=v13&product=lifesum
https://example.org/v1113 -> https://example.org?version=v1113
https://example.org -> https://example.org
Here is what I have tried so far, but it is not working:
# HTTP Server
server {
# port to listen on. Can also be set to an IP:PORT
listen 8080;
# This is my attempt to match and rewrite
location ~* (\/v\d+) {
rewrite (\/v\d+) /?api_version=$1 break;
}
location = / {
# I have also tried this rewrite but iit is not working either
rewrite (\/v\d+) /?api_version=$1 break;
try_files $uri $uri/ /index.html;
}
}
NOTE: This is a Single Page Application, if that helps.
To meet all of your requirements, you will need to capture that part of the URI which follows the version string.
For example:
rewrite ^/(v\d+)(?:/(.*))?$ /$2?version=$1 redirect;
The redirect flag causes Nginx to use an external redirect with a 302 status (see this document for details). An external redirect is necessary for the SPA to see the new URI.
The rewrite statement can be placed in the outer server block or within a location block that matches the original URI (for example: location ~* ^/v\d).
To avoid Nginx adding a port number to the redirected URI, use:
port_in_redirect off;
See this document for details.

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.

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: remove part of url permanantly

I have redesigned a website and changed the url formats too.
Now i need to change the old url to new one.
Here is my old url:
http://www.example.com/forum/showPost/2556/Urgent-Respose
The new url will be:
http://www.example.com/2556/Urgent-Respose
How to redirect to new url using nginx by removing /forum/showPost from url?
Edited:
Also this url:
http://www.tikshare.com/business/showDetails/1/Pulkit-Sharma-and-Associates,-Chartered-Accountants-in-Bangalore
New url:
http://www.tikshare.com/classifieds/1/Pulkit-Sharma-and-Associates,-Chartered-Accountants-in-Bangalore
Above link is complete removing whereas this link is to replace business/showDetails with classifieds
There are a number of options. You could protect the rewrite within a location block which would be quite efficient as the regular expression is only tested if the URI prefix matches:
location ^~ /forum/showPost {
rewrite ^/forum/showPost(.*)$ $1 permanent;
}
See this document for more.
You used permanent in your question - which generates a 301 response.
If you use redirect instead of permanent - a 302 response will be generated.
If you use last instead of permanent - an internal redirect will occur and the browser address bar will continue to show the old URL.
In response to your comment:
rewrite ^/forum/showPost(.*)$ /post$1 permanent;
server
{
listen 80; ## Listen on port 80 ##
server_name example.com; ## Domain Name ##
index index.html index.php; ## Set the index for site to use ##
charset utf-8; ## Set the charset ##
location ^~ /forum/showPost {
rewrite ^/forum/showPost(.*)$ $1 permanent;
}
location ^~ /business/showDetails {
rewrite ^(.*)business/showDetails(.*)$ classifieds$1 permanent;
}
}

Silly nginx rewrite issue - infinate loop

I'm totally baffled with this one. Its probably something silly, and I'm missing it after along day! Anyway, I have this rewrite rule setup in my nginx config for the site:
location / {
root /srv/www/site.co.uk/www;
index index.html index.htm index.php;
rewrite ^/(.*)/index.html$ http://site.co.uk/$1/ permanent;
rewrite ^/index.html$ http://site.co.uk/ permanent;
}
When I go to:
http://www.example.com/index.html
http://www.example.com/foo/index.html
..then it correctly sends to:
http://www.example.com/
http://www.example.com/foo/
If I comment those 2 rewrite rules out, restart nginx, then retry... the page loads fine!
Can anyone see where I'm going wrong? Maybe I'm just being blind!
You have constructed a rewrite loop.
The index directive effectively generates an internal rewrite to /index.html whenever a URL with a trailing / is presented.
One way to break the loop is to only apply your rewrite rules when the external URL contains index.html. The variable $request_uri contains the external URL and can be tested using an if directive. See this caution regarding if.
if ($request_uri ~* "/index\.html(?|$)") {
rewrite ^(.*/)index\.html$ $scheme://$server_name$1 permanent;
}
location / {
root /srv/www/site.co.uk/www;
index index.html index.htm index.php;
}

Resources