It is exactly the $uri/
part that makes nginx assuming an URI can be a directory name and looking for an index file presence inside it.
Assume the following directory structure:
/var/www/html
├── a
│ └── index.html
├── b
│ ├── index.html
│ └── index.htm
├── c
│ └── test.html
...
and the following nginx config:
server {
root /var/www/html;
index index.htm index.html;
location / {
try_files $uri $uri/ =404;
}
}
Here is what happened with the following curl
requests:
-
curl http://localhost/a
Returns a redirect:
HTTP/1.1 301 Moved Permanently
Location: http://localhost/a/
-
curl http://localhost/a/
Returns the /var/www/html/a/index.html
file contents.
-
curl http://localhost/b
Returns a redirect:
HTTP/1.1 301 Moved Permanently
Location: http://localhost/b/
-
curl http://localhost/b/
Returns the /var/www/html/b/index.htm
file contents (index files existence is checked in order they are specified using the index
directive).
-
curl http://localhost/c
Returns a redirect:
HTTP/1.1 301 Moved Permanently
Location: http://localhost/c/
-
curl http://localhost/c/
Since there is no index file in the /var/www/html/c/
directory, curl
command will return an HTTP 403 Forbidden
unless you have an autoindex on;
directive in your config (in that case nginx will return the /var/www/html/c/
directory listing).
If your nginx config will look like
server {
root /var/www/html;
index index.htm index.html;
location / {
try_files $uri =404;
}
}
each of the above requests now will return an HTTP 404 Not Found
error. An autoindex
directive, if being present, will have no effect. The only way to get an index.html
contents will be to specify it explicitly, e.g. http://localhost/a/index.html
, http://localhost/b/index.htm
etc.
The very important yet absolutely non-obvious thing is that an index
directive being used with try_files $uri $uri/ =404
can cause an internal redirect. For example, if you will have the following configuration:
server {
root /var/www/html;
index index.htm index.html;
location / {
add_header X-Test test1;
try_files $uri $uri/ =404;
}
location /a/index.html {
add_header X-Test test2;
}
}
request http://localhost/a/
will cause an internal redirect from /a/
to /a/index.html
and return the /var/www/html/a/index.html
file contents with the custom X-Test
header set to test2
, not to test1
!
The last thing worth to mention is that try_files $uri $uri/ =404;
is the default nginx behavior, so
location / {
try_files $uri $uri/ =404;
}
and
location / {}
locations are totally equal.
Update
An additional question from OP:
My thoughts were: $uri
examines URI as it is and $uri/
examines URI as a directory looking out for an index file. For http://localhost/a
with try_files $uri /file.html =404;
I get file.html
. Good for now! For http://localhost/a
with try_files $uri/ /file.html =404;
I get file.html
too. Why? I was expecting the index.html
. Furthermore, try_files $uri $uri/ /file.html =404;
will get me the index.html
.
A really good question! Without answering it the whole answer will be somehow incomplete. Let's look what happens here.
Having the http://localhost/a/
request and try_files $uri/ /file.html =404;
directive in your nginx config, on a first step nginx checked the /var/www/html/a/
directory for being a directory, next checked it for an index file presence, found an index.html
file inside it and made an internal redirect from /a/
to /a/index.html
. On a second step, being inside the same location block, nginx checks the /var/www/html/a/index.html
for being a directory, but it isn't! And since you don't have an $uri
component as a try_files
directive parameter, it goes for the next check for the /var/www/html/file.html
file, found it and returns its content.
You may think that using the try_files
directive without an $uri
parameter is completely useless therefore. Usually it is, but it can be a use case to do it, for example when you want to hide your internal site structure. Here is an example:
server {
root /var/www/html;
index index.html;
location / {
try_files $uri/ =404;
}
location ~ /index\.html$ {
internal; # only accessible via internal URI rewrite
try_files $uri =404;
}
location ~ \.(js|css|jpe?g|png)$ {
# serve the assets in a usual way
try_files $uri =404;
}
}
Making that location ~ /index\.html$ { ... }
an internal, you prevent direct access to your index.html
files via requests like http://localhost/a/index.html
(an HTTP 404 Not Found
will be returned instead). However requests like http://localhost/a/
remains workable because of the internal URI rewrite by index
directive together with try_files $uri/ =404
one.