TimReeves
Regular Pleskian
Username: TimReeves
TITLE
Nginx-only php location does not work for WordPress permalinks
PRODUCT, VERSION, OPERATING SYSTEM, ARCHITECTURE
Plesk WebAdmin, 18.0.33, Debian 9, PHP-FPM
PROBLEM DESCRIPTION
I'm using nginx with no proxy mode ("Turn off to stop using Apache") - but the Plesk standard configuration does not work with WordPress, when in WordPress "Settings | Permalinks | Common Settings | Post name" is chosen (as is usual, for SEO reasons), or any other permalink form with a trailing slash. Until now, (for 6 years) I have been using a custom template for nginxDomainVirtualHost.php, which I keep having to update as Plesk nginx config evolves, so now I was looking if I can dispense with the custom template.
Sadly, there is one almost-showstopper and some not-so-optimal stuff in the standard template /usr/local/psa/admin/conf/templates/default/domain/nginxDomainVirtualHost.php.
The (almost) Showstopper
Starting at line 211 two conditions are tested and if true this nginx code is generated:
location ~ /$ { index <?=$VAR->quote($VAR->domain->physicalHosting->directoryIndex)?>; }
This test matches all URLs ending in slash - the assumed intention to provide indexing information for URLs which are directories (otherwise the webserver would refuse to list a directories content). However, some Pseudo-URLs such as WordPress permalinks to articles using the article name also end in slash, and should be passed to WordPress's index.php. But the matched location does not actually DO anything, it just sets the index value. The outcome is, that "index.php" is tacked on to the permalink and WordPress articles are not reachable, the server returns a 404 not found. And since regex location processing in nginx terminates on the first match, and the nginx code which a user can append for a domain is inserted after all the Plesk statements, this situation cannot be remedied with another location statement.
Therefore, it needs to be removed, i.e. the test must be false, but the only thing I could have control over is $VAR->domain->physicalHosting->directoryIndex. This is a (obviously) a string, a string only evaluates to boolean false if it is empty or the string "0". At first I did not see how to set this value for nginx, because when you go to {domain} | Apache & nginx Settings and look at nginx settings, there is no such field offered. So it takes a leap of intuition to realise that you have to set the value in the section "Common Apache settings", although lower down I have selected "Turn off to stop using Apache" - the UI should be improved here. And also, in the Apache field "Index files", emtpy string and "0" are not accepted, and the field value reverts to its standard value. Thus the test on physicalHosting->directoryIndex can never be false, so the test is currently meaningless, and the location statement for trailing slash URLs will always be generated when not nginxProxyMode.
There is a way to get around this in one's own configuration - rewrites trigger a complete re-evaluation:
rewrite ^/.+/$ /index.php?q=$uri&$args last;
But I would prefer not to have to do it - as I'm not sure that it will work in all cases.
Sub-optimal: lack of "try_files" in the .php block
Since nginx is not told to check if a .php file actually exists, it passes the request to php-fpm in any case. In such a case, php-fpm just returns a text "File not found" (not even HTML) and sets a 404 HTTP return code. This is not only a waste of resources - the nginx error log fills up with messages like "/var/www/vhosts/example.com/httpdocs/blog-article/index.php" is not found...", so it becomes easy to oversee 'real' error messages in the log - not to mention what happens when your website is subject to a DDoS attack...
So my custom .php location block looks like this:
location ~ \.php(/.*)?$ {
fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
set $path_info $fastcgi_path_info;
# Requested PHP Scripts MUST exist exactly as named
try_files $fastcgi_script_name =404;
# try_files has emptied $fastcgi_path_info
fastcgi_param PATH_INFO $path_info;
# Tell comet cache that we have cared about the nginx config
fastcgi_param WP_NGINX_CONFIG done;
# You can add any application-special headers to $_SERVER here
fastcgi_param PATH "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
fastcgi_pass "unix:///var/www/vhosts/system/$domain/php-fpm.sock";
include /etc/nginx/fastcgi.conf;
# access_log off;
}
Sub-optimal: lack of any way to add to the Plesk .php block
Looking at the custom location block above, which I use, there are two statements, apart from the try_files, which I added:
1) fastcgi_param WP_NGINX_CONFIG done;
This tells a caching plugin that I've done the neccessary already
2) fastcgi_param PATH "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
This ramps up the otherwise very brief default value of PATH - some php scripts may need it.
So it would be helpful to have a way to add custom statements (per website) to the .php location.
STEPS TO REPRODUCE
Try the standard Plesk handling of PHP-FPM with WordPress configured to use post name as permalink.
ACTUAL RESULT
Articles are not reachable, instead 404.
EXPECTED RESULT
Articles are displayed.
ANY ADDITIONAL INFORMATION
I discoverd the "location ~ /$" problem nearly 6 years ago - would be really great if it could finally be overcome - easiest fix on Plesk's part to allow empty index string in the domain config.
YOUR EXPECTATIONS FROM PLESK SERVICE TEAM
Confirm bug
TITLE
Nginx-only php location does not work for WordPress permalinks
PRODUCT, VERSION, OPERATING SYSTEM, ARCHITECTURE
Plesk WebAdmin, 18.0.33, Debian 9, PHP-FPM
PROBLEM DESCRIPTION
I'm using nginx with no proxy mode ("Turn off to stop using Apache") - but the Plesk standard configuration does not work with WordPress, when in WordPress "Settings | Permalinks | Common Settings | Post name" is chosen (as is usual, for SEO reasons), or any other permalink form with a trailing slash. Until now, (for 6 years) I have been using a custom template for nginxDomainVirtualHost.php, which I keep having to update as Plesk nginx config evolves, so now I was looking if I can dispense with the custom template.
Sadly, there is one almost-showstopper and some not-so-optimal stuff in the standard template /usr/local/psa/admin/conf/templates/default/domain/nginxDomainVirtualHost.php.
The (almost) Showstopper
Starting at line 211 two conditions are tested and if true this nginx code is generated:
location ~ /$ { index <?=$VAR->quote($VAR->domain->physicalHosting->directoryIndex)?>; }
This test matches all URLs ending in slash - the assumed intention to provide indexing information for URLs which are directories (otherwise the webserver would refuse to list a directories content). However, some Pseudo-URLs such as WordPress permalinks to articles using the article name also end in slash, and should be passed to WordPress's index.php. But the matched location does not actually DO anything, it just sets the index value. The outcome is, that "index.php" is tacked on to the permalink and WordPress articles are not reachable, the server returns a 404 not found. And since regex location processing in nginx terminates on the first match, and the nginx code which a user can append for a domain is inserted after all the Plesk statements, this situation cannot be remedied with another location statement.
Therefore, it needs to be removed, i.e. the test must be false, but the only thing I could have control over is $VAR->domain->physicalHosting->directoryIndex. This is a (obviously) a string, a string only evaluates to boolean false if it is empty or the string "0". At first I did not see how to set this value for nginx, because when you go to {domain} | Apache & nginx Settings and look at nginx settings, there is no such field offered. So it takes a leap of intuition to realise that you have to set the value in the section "Common Apache settings", although lower down I have selected "Turn off to stop using Apache" - the UI should be improved here. And also, in the Apache field "Index files", emtpy string and "0" are not accepted, and the field value reverts to its standard value. Thus the test on physicalHosting->directoryIndex can never be false, so the test is currently meaningless, and the location statement for trailing slash URLs will always be generated when not nginxProxyMode.
There is a way to get around this in one's own configuration - rewrites trigger a complete re-evaluation:
rewrite ^/.+/$ /index.php?q=$uri&$args last;
But I would prefer not to have to do it - as I'm not sure that it will work in all cases.
Sub-optimal: lack of "try_files" in the .php block
Since nginx is not told to check if a .php file actually exists, it passes the request to php-fpm in any case. In such a case, php-fpm just returns a text "File not found" (not even HTML) and sets a 404 HTTP return code. This is not only a waste of resources - the nginx error log fills up with messages like "/var/www/vhosts/example.com/httpdocs/blog-article/index.php" is not found...", so it becomes easy to oversee 'real' error messages in the log - not to mention what happens when your website is subject to a DDoS attack...
So my custom .php location block looks like this:
location ~ \.php(/.*)?$ {
fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
set $path_info $fastcgi_path_info;
# Requested PHP Scripts MUST exist exactly as named
try_files $fastcgi_script_name =404;
# try_files has emptied $fastcgi_path_info
fastcgi_param PATH_INFO $path_info;
# Tell comet cache that we have cared about the nginx config
fastcgi_param WP_NGINX_CONFIG done;
# You can add any application-special headers to $_SERVER here
fastcgi_param PATH "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
fastcgi_pass "unix:///var/www/vhosts/system/$domain/php-fpm.sock";
include /etc/nginx/fastcgi.conf;
# access_log off;
}
Sub-optimal: lack of any way to add to the Plesk .php block
Looking at the custom location block above, which I use, there are two statements, apart from the try_files, which I added:
1) fastcgi_param WP_NGINX_CONFIG done;
This tells a caching plugin that I've done the neccessary already
2) fastcgi_param PATH "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
This ramps up the otherwise very brief default value of PATH - some php scripts may need it.
So it would be helpful to have a way to add custom statements (per website) to the .php location.
STEPS TO REPRODUCE
Try the standard Plesk handling of PHP-FPM with WordPress configured to use post name as permalink.
ACTUAL RESULT
Articles are not reachable, instead 404.
EXPECTED RESULT
Articles are displayed.
ANY ADDITIONAL INFORMATION
I discoverd the "location ~ /$" problem nearly 6 years ago - would be really great if it could finally be overcome - easiest fix on Plesk's part to allow empty index string in the domain config.
YOUR EXPECTATIONS FROM PLESK SERVICE TEAM
Confirm bug