nginx: PHP-files are being downloaded after rewrite - php

I'm using nginx with PHP-FPM on a ISPConfig3 server.
I put the following rewrite-rule in my nginx-directives (to make prettier links in Pydio):
location ~ \.php$ {
try_files #php;
}
location #php {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
fastcgi_pass 127.0.0.1:9026;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_intercept_errors on;
}
proxy_set_header X-Accel-Mapping /var/www/XXXYYY.com/pydio/data/=/data/;
location /conf/ { deny all; }
location /data/ { internal; }
location /data/public/ {
try_files $uri $uri.php =404 last;
}
I want URLs in pydio.XXXYYY.com/data/public/* to have a .php-extension added.
This rule finds the files without the .php in the address bar but now they are downloaded instead of executed.
Since I use ISPConfig3, the rewrites for .php-files (to have them executed by PHP-FPM) is above the stated part. But I thought adding "last" should take care of that.
What else could I try?
Thank you!

First of all, you misunderstand the try_files directive. There's no "last" argument and it doesn't work like you think. Please, check the documentation: http://nginx.org/r/try_files. It's technical documentation, read it literally, every word has meaning.
To solve your problem you have to remove two last arguments from try_files:
try_files $uri $uri.php =404 last;
should be replaced with:
try_files $uri $uri.php;

Related

Nginx location not behaving as the docs suggest

I have a query about the way nginx handles locations when try_files is invoked. I have a docker stack which serves Wordpress and phpmyadmin applications. My config is below:
server {
listen 80;
index index.php;
server_name test.com; # Just a placeholder
root /code;
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location /pma {
alias /var/www/html;
index index.php;
try_files $uri $uri/ /index.php;
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_pass phpmyadmin:9000;
}
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
My question is this: Why does the above config work when according to the nginx documentation here it should not work.
When a request for 'http://test.com/pma/' is made I understand the following should happen:
Match /pma location
try_files $uri = FALSE
try_files $uri/ = FALSE
Fallback to /index.php
$uri now equal to: 'http://test.com/pma/index.php'
Processing is now restarted.
Match '.php$' location at the bottom.
It breaks because this is the wrong location and the 'php' container doesn't have access to the phpmyadmin files inside the 'phpmyadmin' container.
However, instead what appears to happen is this:
Match /pma location
try_files $uri = FALSE
try_files $uri/ = FALSE
Fallback to /index.php
$uri now equal to: 'http://test.com/pma/index.php'
Processing continues inside the '/pma' context, which now matches the nested '.php$' location block, which passes the request to FPM listening inside the 'phpmyadmin' container.
I know I should be happy that it works, but it's bugging me when everything I understand about nginx says this shouldn't work.
Obviously I have some fundamental misunderstanding about the way this works, and I'd appreciate it if someone could point me in the right direction.
Thank you.
Solution was found here: https://artfulrobot.uk/blog/untangling-nginx-location-block-matching-algorithm
Specifically this part:
Exact string matches location = /foo
The longest of any location ^~ ... matches
The first regex match that is nested within the single longest matching prefix match! See discussion below.
The first other regex match location ~ regex
The longest prefix match location /foo
Point number '3' is the answer here.

Redirect /file-name to file-name.php in Nginx

My structure project
- index.php
- abc.php
- folder/
---- def.php
My nginx.conf
server {
listen 80 default_server;
root /var/www/public;
index index.html index.htm index.php;
server_name _;
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location /index.php {
include snippets/fastcgi-php.conf;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
}
}
How can I change nginx.conf to use domain/abc for href instead of domain/abc.php
Thanks!
This is commonly called "extensionless PHP", there are many solutions, of which this is just one:
location / {
try_files $uri $uri/ #php;
}
location #php {
try_files $uri.php $uri/index.php /index.php =404;
include snippets/fastcgi-php.conf;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
}
If you want URIs ending in .php to work too, add:
location ~* ^(.*)\.php$ { return 301 $1$is_args$args; }
The high-performance solution is simply specifying the desired location, and map it to the corresponding PHP script.
location = /abc {
include snippets/fastcgi-php.conf;
fastcgi_param SCRIPT_FILENAME $document_root/abc.php;
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
}
This will ensure that /abc is processed by script /abc.php.
If you want to also "hide" access to /abc.php, you can add:
location = /abc.php {
return 404;
}
Why this is fast, is because the exact matching (with equals sign) involves no prefix matching and no regular expression processing.
Moreover we don't need to use try_files (it has performance issues). Specifically, if using the config from the answer by #RichardSmith, it may yield up to 5 unnecessary file existence checks for an arbitrary request, and 3 file existence checks for every request to /abc.

Nginx with multiple locations serving some php files as download

I have two directories, /home/php/www/public/ and /home/php/www/private/ and I would like to serve which one depending on the first part of the uri.
I would like /foo/ to act 'normally' in that it should serve which ever file is in the location, for example mysite.com/about would serve /public/about.php. However /private/ would always serve a single file no matter the request, for example mysite.com/private/foo, mysite.com/private/bar and mysite.com/private/foo/test would all serve /private/app.php.
I am probably in the 100s of different variations from what I have seen here and in other googles but being very new to all of this can't seem to piece together exactly what I need. After a few days of trial and error I am close to what I am after, mysite.com/about serves /public/about.php correctly and mysite.com/private/whatever gets /private/app.php but it doesn't execute it, it serves it as a download instead.
Here is what I have so far:
server {
listen 80;
listen [::]:80;
root /home/php/www/public;
index index.php;
server_name mysite.com;
location /private/ {
alias /home/php/www/private;
try_files /app.php =404;
location ~ [^/]\.php(/|$) {
# location ~ \.php$ {
# fastcgi_split_path_info ^(.+\.php)(/.+)$;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.2-fpm.sock;
# fastcgi_index app.php;
# fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# include fastcgi_params;
# return 302 https://google.com;
}
}
location / {
try_files $uri $uri.html $uri/ #extensionless-php;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.2-fpm.sock;
}
location #extensionless-php {
rewrite ^(.*)$ $1.php last;
}
location ~ /\.ht {
deny all;
}
}
If I replace everything inside the location /private/ block with just return 302 https://google.com it redirects successfully to google.com, so I know this location block is being returned but putting the same thing inside the location ~ \.php$ inside location /private/ nothing happens so to me it looks like it is not hitting this block, what am I missing? There aren't any errors in /var/log/nginx/error.log relating to this.
Try:
location /private/ {
root /home/php/www/private;
try_files /app.php =404;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.2-fpm.sock;
}
Use root rather than alias as you are not trying to alias the original request. The file was downloaded because of the unnecessary nested location block.

nginx rewrite to index.php?q=$uri

I have to update a nginx host so all requests to an alias are rewritten to /alias_path/index.php?q=$uri. But now all the assets are no longer available.
This is my current configuration. And I'm getting closer, but when I am uncommenting the last location, the assets are no longer available.
location /csv-import {
alias /var/www/csv-import/public;
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}
try_files $uri $uri/ =404;
#location ~ ^/csv-import/(.*)$ {
#alias /var/www/csv-import/public;
#try_files $uri $uri/ /csv-import/index.php?q=$1;
#}
error_log /var/log/nginx/csv-import.error.log;
access_log /var/log/nginx/csv-import.access.log;
}
The file I wan't to reach is /var/www/csv-import/public/index.php. All Urls like example.com/csv-import/some/url should be rewritten to example.com/csv-import/index.php?q=some/url but assets like example.com/csv-import/css/app.css should be available under /var/www/csv-import/public/css/app.css.
I'm shure there is a solution that works perfect but I couldn't come up with it.
You do not need another location block. The usual method is to change the default action of the try_files statement. But because of this issue, an if block may be simpler:
location ^~ /csv-import {
alias /var/www/csv-import/public;
if (!-e $request_filename) {
rewrite ^/csv-import/(.*)$ /csv-import/index.php?q=$1 last;
}
location ~ \.php$ {
...
}
error_log /var/log/nginx/csv-import.error.log;
access_log /var/log/nginx/csv-import.access.log;
}
The if block replaces the try_files statement. The ^~ operator avoids any ambiguity with other location blocks. See this caution on the use of if.

filesmatch htaccess convert to nginx (on plesk)

my old server had this in the htaccess file:
< FilesMatch "^resort$">
ForceType application/x-httpd-php
< /FilesMatch>
where I had a resort php file (without the php extension)...
so the file was domain.com/resort/param1/param2
I'm struggling to make the equivalent work for nginx...
i've tried these items, but none work:
location = /resort/ {
try_files $uri /resort.php;
try_files $uri $uri/ #extensionless-php;
try_files $uri $uri/ $uri.html $uri.php?$query_string;
try_files $uri $uri/ /resort.php$is_args$args;
rewrite ^(.*)$ /resort.php last;
}
So how do I execute the resort file as php, when this url is in the browser:
domain.com/resort/param1/param2.php
THANKS!
PS. would love some pages/resources/tutorials that explain "apache to nginx" for people who don't understand nginx :)
i've been to nginx site, but IMO, I need to know more than I do to figure it out or understand what the nginx site is saying.
update:
i think this is close, but still not working :(
this is url: domain.com/resort/city/state.php
here is directive:
location ~ /resort/ {
rewrite ^/resort/(.*)/(.*) /resort/$1/$2 break;
}
this is what worked for me:
location ^~ /resort/ {
try_files $uri $uri/ /resort.php; }
To execute a file without a .php extension as though it was a PHP file, you will need to replicate the location block that handles those types of request.
It will look something like this:
location ~ \.php$ {
try_files $uri =404;
include fastcgi_params;
fastcgi_pass ...;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
To process the URI /resort/param1/param2 using the PHP file located at /resort, you might use something like:
location ~ ^/resort/ {
include fastcgi_params;
fastcgi_pass ...;
fastcgi_param SCRIPT_FILENAME $document_root/resort;
}
See this document for location syntax.

Categories