Unexpected nginx behavior - nginx

I have the following block in my nginx config:
location ~ /$ {
index index.html index.cgi index.pl index.php index.xhtml index.htm index.shtml;
}
location /cat {
try_files /index.php?url=$uri =404;
}
What I expect this to do: Any request sent to http://www.example.com/cat<whatever> will be sent to index.php with the GET variable url set to the request uri. What happens instead based on the rewrite log is that the location /cat block is never hit at all. Here is a relevant excerpt from the rewrite log when I request http://www.example.com/cart/testing/:
2017/03/21 00:32:49 [error] 25711#0: *20566477 "/var/www/vhosts/example.com/httpdocs/cat/testing/index.html" is not found (2: No such file or directory), client: <ip redacted>, server: example.com, request: "GET /cat/testing/ HTTP/1.1", host: "www.example.com"
There are no other entries related to the request.
It was my understanding that nginx processes any prefix location blocks (i.e. location /cat) BEFORE any location blocks involving regex (i.e. location ~ /$). So I'm stumped by this behavior.

Your understanding is incorrect. See this document for details.
Also, your try_files directive is incorrect. Either /index.php?url=$uri or =404 should be the last element in the statement. See this document for details.
You probably want:
index index.html index.cgi index.pl index.php index.xhtml index.htm index.shtml;
location /cat {
try_files $uri $uri/ /index.php?url=$uri;
}
Not sure of the purpose of the other location block, as the index directive is already going to process any URI with a trailing /. See this document for details.

Related

try_files vs an empty location block in NGINX?

Wondering about the difference between doing:
location / {
try_files $uri $uri/ =404;
}
And doing:
location / {}
They both seem to serve files\folders only if they exist, or returning 404 error page if they don't.
There is no difference, try_files $uri $uri/ =404; is exactly the default nginx behavior:
Check if $document_root$uri is a file, and if it is, serve the request with the contents of this file;
Check if $document_root$uri is a directory, and if it is, serve the request with the first found index file from this directory. If no index files found, nginx will return directory list if you have autoindex on in your nginx config or HTTP 403 Forbidden error otherwise;
Return HTTP 404 Not found if $document_root$uri nor a file neither a directory.
Advantage of the try_files directive is that you can specify additional files/directories to check and select what to do if none of them are found (jump to another URI / jump to some named location / return any HTTP error code).
However try_files may give you some performance penalty depending of your settings, read this article by #DanilaVershinin for more details.

nginx sends header from different location directive

I have the following nginx config, which basically has two locations which matches
a) routes including a dot (e.g. "/js/script.js") and
b) all other routes.
server {
listen 80;
server_name localhost;
root /app;
# files
# for all routes matching a dot, check for files and return 404 if not found
# e.g. /file.js returns a 404 if not found
location ~ \. {
add_header Cache-Control "public, max-age=2678400";
try_files $uri $uri/ =404;
}
# normal routes
# serve given url and default to index.html if not found
# e.g. /, /user and /foo/bar will return index.html
location / {
add_header Cache-Control "no-store";
try_files $uri $uri/ /index.html;
}
}
All routes matching a dot should be cached with the cache-control header and should return a 404 if not found.
Routes not matching a dot, e.g. "/users", should send the index.html by default.
However, this results in really strange behavior.
I have an index.html with the contents <h1>wazzup</h1>, and it gets correctly served when requesting a route without a dot, e.g. "/asdf":
$ curl -v localhost:8000/asdf
> Cache-Control: public, max-age=2678400
> Accept-Ranges: bytes
> ...
> <h1>wazzup</h1>
However, a Cache Control header is sent too, although this is defined in another location block. To summarize, the result is correct, but somehow the header gets mixed up. Why?
To try for yourself, you can run a docker image like so:
docker run -p 8000:80 -v /tmp/test:/app steebchen/nginx-spa
Create an index.html and script.js in the directory /tmp/test, and then access it:
curl -v localhost:8000/asdf // should return the contents of index.html
curl -v localhost:8000/script.js
The full Dockerfile is available on Github.
The answer is actually quite simple, I just figured it out by myself.
The second location is matching because the fallback is index.html and obviously this route contains a dot.
I simply edited the location to \.(?!html) which matches everything but html files:
location ~ \.(?!html) {
add_header Cache-Control "public, max-age=2678400";
try_files $uri $uri/ =404;
}

nginx location directive doesn't look for specified file first

I have a simple nginx configuration file -
server {
listen 80 default_server;
root /var/www/example.com;
#
# Routing
#
location / { index index.html; }
location /foo { index foo.html }
#
# Logging
#
access_log /var/log/nginx/{{ www_domain }}.log;
error_log /var/log/nginx/{{ www_domain }}-error.log error;
server_name example.com;
charset utf-8;
}
As you can see, there's only 2 routes - the / and /foo paths.
When I go to www.example.com/ all works correctly. I can see the index.html page being served.
When I go to www.example.com/foo, I get a 404 error when I should be getting the foo.html page.
Looking at the logs, I see this error:
2018/08/13 21:51:42 [error] 14594#14594: *6 open() "/var/www/example.com/foo" failed (2: No such file or directory), client: XX.XX.XX.XX, server: example.com, request: "GET /foo HTTP/1.1", host: "example.com"
The error implies that it's looking for a file named /var/www/example.com/foo and not /var/www/example.com/foo.html like I would expect.
Why does this happen in general, and specifically why does it not happen on my root path / ?
Thanks!
Edit: It does work if I visit www.example.com/foo.html directly
The index directive will append the filename, when you give the URI of a directory.
So / points to the root directory (/var/www/example.com/), and the index index.html; statement causes nginx to return the file /var/www/example.com/index.html.
The /foo URI does not point to a directory. If the directory /var/www/example.com/foo/) did in fact exist, the index foo.html; statement would cause nginx to return the file /var/www/example.com/foo/foo.html. Which is not /var/www/example.com/foo.html.
What you are attempting to achieve is some kind of extension-less scheme, which is nothing to do with the index directive.
See this document for details of the index directive.
There are many solutions that will work, for example, using try_files instead of index:
location /foo { try_files /foo.html =404; }
See this document for details.

nginx config with spa and subdirectory root

I always seem to have problems with nginx configurations. My SPA is located at /mnt/q/app (pushstate is enabled) and the frontend root is located at client/public. Everything should be mapped to index.html, where the app picks up the route and decides what to do.
Full path to the index is /mnt/q/app/client/public/index.html.
I think I ran out of options by now. No matter what I do, I just get a 404 back from nginx, I think the configuration is simple enought and have no clue what's wrong.
server {
listen 80;
server_name app.dev;
root /mnt/q/app;
location / {
root /client/public;
try_files $uri #rewrites =404;
}
location #rewrites {
rewrite ^(.+)$ /index.html last;
}
}
Any help is appreciated.
If nginx views the file system from the root, then the root should be set to /mnt/q/app/client/public, and not either of the two values you are using.
The last element of the try_files directive can be a default action (e.g. /index.html), a named location or a response code. You have a named location in the penultimate element - which will be ignored.
Your named location should work, but is unnecessary, as try_files is capable of implementing it more simply. See this document for more.
For example:
root /mnt/q/app;
location / {
root /mnt/q/app/client/public;
try_files $uri $uri/ /index.html;
}
location /api {
}
location /auth {
}
The $uri/ element will add a trailing / to directories, so that the index directive can work - you do not have to add it if you do not need it.

unable to configure nginx try_files correctly for files in subfolder

Trying to configure nginx to find files from subfolder. Code from nginx documentation:
location / {
try_files $uri $uri/index.html $uri.html =404;
}
request "/" opens file index.html - it's ok
request "/folder" opens file /folder/index.html - ok
but request "/folder/file" as I suggest, should open /folder/file.html - but it returns 404
Please help me configure nginx try_files setting correctly so nginx can find file.html in "folder" directory when "/folder/file" request is received

Resources