I have a location block setup to catch all file requests and send them to PHP-FPM
:
location / {
try_files $uri /routing.php?$args;
fastcgi_pass unix:/opt/local/var/run/php54/php-fpm-www.sock;
include /documents/projects/intahwebz/intahwebz/conf/fastcgi.conf;
}
This works and correctly passes the request to PHP-FPM
to either the exact existing php file that was requested or with routing.php
set as the script to run.
I tried to add an error page, so that if routing file was ever removed or otherwise not available, the error page would be shown rather than Nginx's default error page:
location / {
try_files $uri /routing.php?$args /50x_static.html;
fastcgi_pass unix:/opt/local/var/run/php54/php-fpm-www.sock;
include /documents/projects/intahwebz/intahwebz/conf/fastcgi.conf;
}
This stops the routing.php
file from being served, and the 50x_static.html page is shown instead. Requests to an existing PHP file still work i.e. going to the URL /dynamic.php
I realise that the last parameter in a try_files
command is slightly magical:
In the event that no file is found, an internal redirect to the last parameter is invoked. Do note that only the last parameter causes an internal redirect, former ones just sets the internal URI pointer. The last parameter is the fallback URI and must exist, or else an internal error will be raised.
While investigating why the error_page had broken the config, I realised that for the config that works (without the static error page), Nginx does appear to be matching the request twice according to the Nginx rewrite log when trying to get the root URL "/":
"^/proxy/(\d+)/(\w+)/(.+)\.(gif|png|jpg|jpeg|GIF|PNG|JPG|JPEG)$" does not match "/", client: 127.0.0.1, server: basereality.com, request: "GET / HTTP/1.1", host: "basereality.test"
"^/proxy/(\d+)/(.+)\.(gif|png|jpg|jpeg|GIF|PNG|JPG|JPEG)$" does not match "/", client: 127.0.0.1, server: basereality.com, request: "GET / HTTP/1.1", host: "basereality.test"
"^/staticImage/(\w+)/(.+)\.([^\.]*)$" does not match "/", client: 127.0.0.1, server: basereality.com, request: "GET / HTTP/1.1", host: "basereality.test"
"^/proxy/(\d+)/(\w+)/(.+)\.(gif|png|jpg|jpeg|GIF|PNG|JPG|JPEG)$" does not match "/routing.php", client: 127.0.0.1, server: basereality.com, request: "GET / HTTP/1.1", host: "basereality.test"
"^/proxy/(\d+)/(.+)\.(gif|png|jpg|jpeg|GIF|PNG|JPG|JPEG)$" does not match "/routing.php", client: 127.0.0.1, server: basereality.com, request: "GET / HTTP/1.1", host: "basereality.test"
"^/staticImage/(\w+)/(.+)\.([^\.]*)$" does not match "/routing.php", client: 127.0.0.1, server: basereality.com, request: "GET / HTTP/1.1", host: "basereality.test"
i.e. the request comes in as /
, the try_files
fails to serve the file and so rewrites the request from /
to /routing.php
and then reprocesses the request.
Why is try files not serving the routing.php
file on the first pass? It exists and is accessible, otherwise it wouldn't be served on the second time round.
EDIT
Removed unrelated config.
The documentation you quoted explicitly says “an internal redirect to the last parameter is invoked”. The internal redirect is handled in the same way as the initial request coming from the client — this includes processing the
rewrite
statements at the server level, which you see in the log. However, if any othertry_files
parameter except the last one matches an existing file, the request is handled using thelocation
configuration where thetry_files
statement is located, and there will be no second matching.As for your rules, did you try just omitting
$args
intry_files
?Note that
$uri
does not contain$args
too; query parameters will still be passed to the FastCGI backend through theQUERY_STRING
parameter, which is presumably set in yourfastcgi.conf
:And if neither
$uri
nor/routing.php
are present as files, the request will be redirected to/50x_static.html
and handled according to thelocation = /50x_static.html
section in your config (but a second iteration of rewrite attempts will still be performed, because your rewrite rules are placed at the server level).One very suspicious detail of your configuration is that you are passing all files through PHP irrespective of the file extension — this is very unusual, and may cause security issues due to PHP code execution in a file where it was not expected.
Sergey had it correct; adding
$args
to a try_files command stops the file from being matched and so sends Nginx round in another processing loop, which is odd, as that is what the Nginx example says to do.If you follow the example exactly with:
If
$uri
and$uri/
don't exist, Nginx will move onto the magic last parameter. It doesn't even try to match the file and instead reprocesses the request setting the new path to/index.php
and the$args
to now beq=$uri&$args
.On the other hand, if you have another value in
try_files
after index.php e.g.Nginx looks for a file called
/index.php?q=$uri&$args
and it obviously doesn't exist, and so it moves onto the/50x_static.html
and reprocesses that.Because
$uri
is rewritten when Nginx reprocesses the request, avoiding this needs some subtle changes. If you set up your config to be like this:The file Nginx looks for is
/index.php
which is the correct name, so nginx will stop processing there. We have to copy the$uri
variable as thetry_files
modifies it and so it would be just/index.php
if we just used it after the try_files.