Stripping index.html and .html from URLs with nginx
My basic goal is to serve the following clean URLs with nginx:
-
/
serves/index.html
-
/abc/
serves/abc/index.html
-
/abc/def
serves/abc/def.html
-
/abc
redirects to/abc/
In order to have canonical names for each resource, I also want to normalize any URL with superfluous file names or extensions:
-
/index.html
redirects to/
-
/abc/index.html
redirects to/abc/
-
/abc/def.html
redirects to/abc/def
The directives I thought would accomplish this:
index index.html;
try_files $uri.html $uri $uri/ =404;
# Redirect */index and */index.html to *.
rewrite ^(.*)/index(\.html)?$ $1 permanent;
# Redirect *.html to *.
rewrite ^(.+)\.html$ $1 permanent;
However, the result of this is different than I expected:
-
/
,/index
and/index.html
all redirect to/
(loop). -
/abc
and/abc/
both redirect to/abc/
(loop).
(It works as designed for /abc/def.html
and /abc/def
; only the directory URLs don't work.)
I'm not sure what is happening here; maybe I'm misunderstanding how the rewrite
works?
(I already tried using location blocks instead, but this also results in loops as try_files
performs an internal redirect to the location block that sends the HTTP 301.)
Edit: Fundamentally, I need something like a location block that only matches the original request URI, but is ignored for the purpose of internal redirects, so it doesn't create a loop in combination with the try_files directive.
You might be looking for a solution like the one explained here:
server {
listen 80;
server_name mysite.com;
index index.html;
root /var/www/mysite/public;
location / {
try_files $uri $uri/ @htmlext;
}
location ~ \.html$ {
try_files $uri =404;
}
location @htmlext {
rewrite ^(.*)$ $1.html last;
}
}