SkyGuardian42 SkyGuardian42 - 1 month ago 15
HTML Question

NGINX remove Remove .html extension

So, I found an answer to removing the .html extension on my page, that works fine with this code:

server {
listen 80;
server_name _;
root /var/www/html/;
index index.html;

if (!-f "${request_filename}index.html") {
rewrite ^/(.*)/$ /$1 permanent;
}

if ($request_uri ~* "/index.html") {
rewrite (?i)^(.*)index\.html$ $1 permanent;
}

if ($request_uri ~* ".html") {
rewrite (?i)^(.*)/(.*)\.html $1/$2 permanent;
}

location / {
try_files $uri.html $uri $uri/ /index.html;
}
}


But if I open mypage.com it redirects me to mypage.com/index


Wouldn't this be fixed by declaring index.html as index? Any help is appreciated.

Answer Source

UPDATED ANSWER: This question piqued my curiosity, and I went on another, more in-depth search for a "holy grail" solution for .html redirects in Nginx. Here is the link to the answer I found, since I didn't come up with it myself: https://stackoverflow.com/a/32966347/4175718

However, I'll give an example and explain how it works. Here is the code:

location / {
    if ($request_uri ~ ^/(.*)\.html$) {
        return 302 /$1;
        try_files $uri $uri.html $uri/ =404;
    }
}

What's happening here is a pretty ingenious use of the if directive. Nginx runs a regex on the $request_uri portion of incoming requests. The regex checks if the URI has an .html extension and then stores the extension-less portion of the URI in the built-in variable $1.

From the docs, since it took me a while to figure out where the $1 came from:

Regular expressions can contain captures that are made available for later reuse in the $1..$9 variables.

The regex both checks for the existence of unwanted .html requests and effectively sanitizes the URI so that it does not include the extension. Then, using a simple return statement, the request is redirected to the sanitized URI that is now stored in $1.

The best part about this, as original author cnst explains, is that

Due to the fact that $request_uri is always constant per request, and is not affected by other rewrites, it won't, in fact, form any infinite loops.

Unlike the rewrites, which operate on any .html request (including the invisible internal redirect to /index.html), this solution only operates on external URIs that are visible to the user.

You will still need the try_files directive, as otherwise Nginx will have no idea what to do with the newly sanitized extension-less URIs. The try_files directive works as such:

Nginx will first append .html to the end of the URI and try to serve it. If it finds an appropriate .html file, it will return that file and will maintain the extension-less URI. If it cannot find an appropriate .html file, it will try the URI without any extension, then the URI as a directory, and then finally return a 404 error.


Note that this is considered safe usage of the if directive, per the Nginx page If Is Evil:

The only 100% safe things which may be done inside if in a location context are:

return ...;

rewrite ... last;