Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about a specific programming problem, a software algorithm, or software tools primarily used by programmers. If you believe the question would be on-topic on another Stack Exchange site, you can leave a comment to explain where the question may be able to be answered.
Closed 9 months ago.
Improve this question
this nginx config is supposed to load index.php from a folder (test), and if it doesn't exist then load index.php from parent folder.
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ ^/test/(.*)$ {
try_files $uri $uri/ /test/index.php?$query_string /index.php?$query_string;
}
if I go to /test it works as expected.
but if I go to /test/something it loads index.php from root instead of the index.php from the parent folder.
Weirdly, if I remove the last /index.php?$query_string from the 2nd location block, then it actually loads the index.php from the parent folder.
So apparently the parent index is found, but for some reason nginx is deciding to look further.. Anyone know why?
Did you read the try_files directive documentation carefully? The very last parameter, if not being specified as HTTP return code or a named location name, treated as a new URI to re-evaluate from the beginning. Every other parameter treated as a file name relative to the location root. That does include every character (well, except the last slash if being specified), so nginx will check the whole $document_root/test/index.php?$query_string file existence. The ?$query_string part is not treated as an URI query string part here, but as a filename part. And even if such a file will be found, it will be served using this location content handler, which is a static one by default (unless you specified something else explicitly using the content handler declaration directive like proxy_pass, fastcgi_pass, uwsgi_pass, etc.)
The reason your PHP files gets processed with the PHP handler (usually something like location ~ \.php$ { ... }) even if your request URI doesn't include the PHP filename is either implicit redirect issued by the index directive if your try_files include the $uri/ argument (read that one documentation carefully too) or explicit redirect issued via the last /index.php?$query_string parameter of the try_files directive. Here is a quote of the most relevant index documentation part to explain this with an example:
It should be noted that using an index file causes an internal redirect, and the request can be processed in a different location. For example, with the following configuration:
location = / {
index index.html;
}
location / {
...
}
a / request will actually be processed in the second location as /index.html.
Take into attention that the regex matching locations, while having a greater priority over the prefix ones (yes, the location directive documentation is useful too), are checked in order of appearance, and the very first matched the URI will be chosen to handle the request.
Summary
Assuming your location ~ \.php$ { ... } PHP handler located above the location ~ ^/test/(.*)$ { ... }:
If your URI is /test/, it is an implicit internal redirect to /test/index.php made by index directive that makes that file to be interpreted via the PHP handler;
If your URI is /test/something and your try_files directive is try_files $uri $uri/ /test/index.php?$query_string /index.php?$query_string;, explicit internal redirect to /index.php?$query_string happens;
If your URI is /test/something and your try_files directive is try_files $uri $uri/ /test/index.php?$query_string;, explicit internal redirect to /test/index.php?$query_string happens.
If your location ~ \.php$ { ... } PHP handler will be located below the location ~ ^/test/(.*)$ { ... }:
If your URI is /test/, an implicit internal redirect by index directive to /test/index.php will took its place on the first step; on the next step you've got your /test/index.php source code in response due to the static content handler of location ~ ^/test/(.*)$ { ... };
If your URI is /test/something and your try_files directive is try_files $uri $uri/ /test/index.php?$query_string /index.php?$query_string;, explicit internal redirect to /index.php?$query_string happens;
If your URI is /test/something and your try_files directive is try_files $uri $uri/ /test/index.php?$query_string;, explicit internal to /test/index.php?$query_string happens on the first step; on the second step you've got /test/index.php source code in response by the reason already explained.
Related
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.
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.
I'm currently using the following location directive along with the try_files for a restfull application:
location /branches/branchx/app/api/ {
try_files $uri $uri/ /branches/branchx/app/api/api.php$is_args$args;
}
Where branchx is the name of my Git branch. I may therefore have multiple branches such as: branch1, branch2, etc..
And therefore I need to manually create a directive for each and every branches I will create.
To avoid this issue, I'm looking to use a regular expression to extract the branch name and using it in my try_files directive. So I'd have a dynamic system managed by a single location directive taking care of all branches.
All attempts to use a regex so far in the try_files end up by throwing a 405 or 404 error.
My last attempt (based on #Richard Smith answer):
location ~ ^(/branches/[^/]+/app/api)/ {
try_files $uri $uri/ $1/api.php$is_args$args;
}
Which returned a 405 (Not Allowed) response for matching uri.
If you are using a regular expression location, you can capture the path at the same time. For example:
location ~ ^(/branches/[^/]+/app/api)/ {
try_files $uri $uri/ $1/api.php$is_args$args;
}
Important note: Changing from a prefix location to a regular expression location will change the evaluation order. Regular expression location blocks are evaluated in order, so /branches/branchx/app/api/api.php will match this location block unless it is placed after the location ~ \.php$ location block. See this document for details.
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.
Trying to see if this is possible.
We have an app and a wordpress install.
Is it possible to use 2 locations for the same folder but under different circumstances. Example..
http://domain.com/subfolder/ - This shows the APP
http://domain.com/subfolder/anything - This shows WP permalink
Right now, we have it so
http://domain.com/subfolder (without the /) shows the app
http://domain.com/subfolder/ (witht the /) shows WP.
This does work, but would it be possible to have it so, it will only show WP IF the URL contains text after subfolder/*
Current Nginx conf:
location ^~ /knowledge {
root /opt/domain.com/public/;
try_files $uri #backend;
}
location /knowledge/ {
index index.php index.html index.htm;
root /opt;
include /etc/nginx/php-wpsc.conf;
try_files $uri $uri/ /knowledge/index.php?q=$uri&$args;
}
Obviously it makes sense to keep the location /knowledge/ block for WordPress, as that matches the majority of cases, with just one case that needs to be overridden.
A specific URI can be taken away from that location block by using a location block with a higher precedence. See this document for details.
One possibility would be an exact match location block:
location = /knowledge/ {
rewrite ^ /knowledge last;
}
Or possibly, change your existing location ^~ /knowledge block from a prefix location to a regular expression location, making the trailing / optional.
location ~ ^/knowledge/? { ... }
Note that this changes the order of evaluation of this location block, so there may be side-effects that need to be considered.