double try_files directive for the same location in nginx - nginx

I discovered a nginx config snippet in serveral gists and config examples (mostly for PHP apps):
#site root is redirected to the app boot script
location = / {
try_files #site #site;
}
#all other locations try other files first and go to our front controller if none of them exists
location / {
try_files $uri $uri/ #site;
}
But I just do not get it: Does the first try_files directive ever match? To me this looks like some nonsense hacking.
Please confirm or explain why not - thanks :)

This is what happens here:
The first location = / is only used when the path in the request is /, e.g. http://example.com/. The second location /is used for all other URLs, e.g. http://example.com/foo or http://example.com/bar.
The reason for the first location is to avoid any interference from index-directives that do a redirect to index.html or something similar.
Inside the first location the try_files-directive first looks for a file named #site, which does not exist and then redirects to the named location #site. The reason for this construct is that the redirect to the named location is purely internal, i.e. the #site location can not be accessed directly from the client and the $uri is kept unmodified during this redirect (which would not be the case for other redirects). The first parameter #site can be anything except a real existing file. I prefer to call it DUMMY for clarity.
The second location tries static files first and, if not found, then also redirects to the named location.

Related

What is difference between using $uri and $uri$args in try_files directive

For my other Angular apps, I am using the below config and everything seems to work fine.
location / {
try_files $uri$args $uri$args/ /index.html;
}
Now the one which I am working has nested folders within the dist folder.
And folder structure is something like:
dist \
-- assets
-- folder1
-- folder2
-- folder3
-- folder4
-- folder5
index.html
index.html
And the inner index.html is called with query params, and the url will be like - <ip>/folder1/folder2/index.html?a=1&b=2&c=3. But this is returning the fallback index.html at the root location. Later, I changed the above location block like this and it started working properly.
location / {
try_files $uri $uri/ /index.html;
}
So I am not clear why the first location block didn't work. And I can't find anything in docs - try_files
All of the parameters of a try_files statement except the last parameter are looking for filenames in the local file system.
So given the URI /foo/bar, the term $uri$args will search for a local file at /path/to/root/foo/bar, and if it does not exist will move on to the next term or the default clause at the end of the statement.
In the above case, $args is empty. However, given the URI /foo/bar?baz, the term $uri$args will search for a local file at /path/to/root/foo/barbaz.
I don't know why anyone would use $uri$args or $uri$args/ as file terms on a try_files statement, but there may well be a legitimate use case.
The last parameter of a try_files statement is special. It can be a status code (for example =404), a named location to branch to, or a URI.
In the case of a URI, Nginx will internally redirect to that URI. A typical example might be /index.php$isargs$args - in this case it is perfectly legitimate to append the $args parameter, as we are creating a new URI and keeping the original argument string.
See this document for details.

NGINX – Serving multiple SPA’s on a single server

We have a development server with lots of single page apps that also handle routing in the frontend.
Normally for a single page app I would assume you need to configure something like:
location /some/path {
try_files $uri $uri/ /index.html?$args;
}
Now on our development server it is quite a lot of work to re-configure nginx for every small test app people put on there.
I want to:
Serve the file if found
Serve the index.html file if the path is a folder
If not found, go back one folder and try look for index.html and serve that
Try previous step until you find an index.html file
Stop trying when you reach the defined root path e.g. /some/path, if that doesn’t have an index.html, return the folder content
If some sort of while loop is not possible (performance is less critical since it's for development purposes only), I could limit it to up to 6 folders back. That should cover most SPA's.
Example:
Let's say I have a single page app on:
/some/path/my-app
And one goes to:
/some/path/my-app/page1/subpage2/id3
It should try:
/some/path/my-app/page1/subpage2/id3 (no match)
/some/path/my-app/page1/subpage2/id3/index.html (no match)
/some/path/my-app/page1/subpage2/index.html (no match)
/some/path/my-app/page1/index.html (no match)
/some/path/my-app/index.html (MATCH !)
P.S. I'm mainly a front-end developer, my nginx knowledge is very limited.
You can use a named location as the last parameter of a try_files statement to perform an internal rewrite to climb up the directory tree. Nginx will limit this to about 10 iterations before declaring a redirection loop.
For example:
root /path/to/root;
index index.html;
location / {
try_files $uri $uri/ #rewrite;
}
location #rewrite {
rewrite ^/(.+/)?. /$1 last;
}
The index and try_files directives handle looking for index.html, and the rewrite statement truncates the URI by removing one or more characters following a /.

NGINX try_files works for all but one url

I have a simple dashboard for my site. Here is the directive:
location /dashboard {
try_files $uri /dashboard/index.php;
}
It works for all items after /dashboard. For example, /users or /pages - all CRUD operations work as expected.
The index.php file at /dashboard is my "controller". It parses the url and includes and runs scripts from there.
For example: /dashboard/group/edit/123456 works as expected and I get the edit page for group number 123456.
But when I post from that page to /dashboard/group/update, it serves /dashboard/group/index.php
So, in the first example, The edit page is loaded and the url at the top of the screen does not change.
In the second example, NGINX is CHANGING the url so my script cannot get the url parts to do the job.
I thought it may have something to do with POST, but I have other forms that use POST without issue.
In addition, or possibly a clue, try_files is returning /dashboard/group/index.php while the directive should return /dashboard/index.php.
Is there another NGINX file that could have so old code in it that is overwriting this domain's config?
I've been at this a few hours and have run out of ideas. Any thoughts?
* One More Clue *
When I BROWSE to /dashboard/group/update, NGINX shows the page as expected. It is only when I POST to that page that NGNIX sends me to /dashboard/group/index.php.
Again, at the very least, it should be sending me to /dashboard/index.php and NOT /dashboard/group/index.php.
You not send all after /dashboard try this:
location /dashboard {
try_files $uri /dashboard/index.php?$uri&$args;
}
OR
location /dashboard {
try_files $uri /dashboard/index.php?$query_string;
}
Nginx docs: https://nginx.org/en/docs/http/ngx_http_core_module.html#try_files
Instead of
location /dashboard {
try_files $uri /dashboard/index.php;
}
Try
location /dashboard {
index index.php; #adding this may work alone
try_files $uri /dashboard/index.php?$uri;
}
I have concluded that I have a cache problem. The location directive works on all items that I have not yet accessed.
So, my configuration - as described - works as it should.
I just have to figure out how to clear my cache ( which in NOT set up in NGINX that I can see!)
Thank you all who helped!

Nginx URL routing

I think this is kind of basic stuff, but I'm struggling to find proper guide that would explain these things:
I have a index.php file and nginx config so that https://dev.something.com works ok.
But I need to change nginx config so that that address produces blank page, and index.php only works from https://dev.something.com/lists. I could put index.php inside lists directory, but isn't there more subtle solution?
And here's the hard part:
Users should be able to access
https://dev.something.com/lists/userName
https://dev.something.com/lists/userName/listName
userName and listName should be used as GET-parameters.
Can anyone help how I could achieve this kind of config with nginx?
You're asking a few (relatively basic) questions, and I would suggest you start with their free e-book https://www.nginx.com/blog/announcing-oreillys-new-book-nginx-a-practical-guide-to-high-performance/
You can define where nginx looks for index files with the root clause, and though they normally use the URL context relative to the server's root, it can be override in each location.
http://nginx.org/en/docs/http/ngx_http_core_module.html#root
You can use portions of URLs as variables, which can be passed as paramters too.
location = /lists { # '=' will match exactly, no trailing url
root /path/where/index.php/lives;
try_files $uri /index.php;
}
location /lists { # this will match anything under that url
rewrite ^/lists/(\d+)/?$ /lists?user=$1; # matches username
rewrite ^/lists/(\d+)/(\d+)/?$ /lists?user=$1&list=$2; # matches username/list
}
location /{ #everything else
root /path/where/index.html/lives; #index.html is empty file.
try_files $uri /index.html;
}

Nginx match location for root path and all other paths separately

I am running Play framework server behind nginx server. At the root path, I am serving static website and all other paths should be redirected to the Play server. I have the following default.conf file in /etc/nginx/conf.d (The system is RHEL 6.7)
# to match the root path only to serve static website
location = / {
root /usr/share/nginx/html;
index index.html index.htm;
# try_files $uri $uri.html $uri/ /index.html;
}
# to match the cms login page
location /cms/ {
proxy_pass http://localhost:9000/;
}
# to match all the requests from the cms
location / {
proxy_pass http://localhost:9000/;
}
However, this configuration doesn't match the root path request. It gives 404 error. However, if I remove the third location rule, then it serves the static page at the root path.
Also, I noticed that first time I tried this, it worked. But now, it's not working any more. Please help.
The result you are getting is most likely due to the 2nd and 3rd location blocks not having "index" directives set. Except for well understood specific reasons, such as overriding the default index file type(s), the "index" should always be set at least within the server context or, preferably, within the http context. Similarly, the "root" directive should be set in the server context.
With your config, when a request hits the 3rd location block, there is no information your what to do with it. Actually, the 2nd block should not be needed from what you have described.
Also, as you are proxying to what appears to be another webserver, you need to ensure that this has the equivalent of "index" and "root" set.
Not sure exactly how the backend you are using works with respect to these. If not configurable there, then you must ensure that that every request hitting it has the URI spelt out fully.
To start with, depending on how exactly things are set up on your server, I will move the "index" and "root" directives up to the "server" level

Resources