I want to serve static HTML files with NGINX, but if the file is missing, it should load a PHP file instead and PHP should handle the content.
I've been testing several combinations of try_files, but I can't get my head around it. I have a dummy PHP app that looks like this:
./
../
dynamic.php
index.php
static/
static/static.html
Then I have a small PHP code on index like this:
<?php
$path = $_SERVER['REQUEST_URI'];
$pattern = '/^\/(.*)\.html$/';
$matches = [];
$results = preg_match($pattern, $path, $matches);
if (count($matches) > 0) {
if ($matches[1] == "dynamic") {
require 'dynamic.php';
} else {
echo "Not found!";
}
} else {
echo "Index page!";
}
The results of browsing to each page should be:
http://foo.bar/ - Loads index.php
http://foo.bar/static.html - Loads static/static.html
http://foo.bar/dynamic.html - Loads index.php & PHP requires dynamic.php
http://foo.bar/baz.html - Loads index.php with "not found" message
This is what I got in the NGINX config file:
server {
listen 80;
server_name .foo.bar *.foo.bar;
access_log /var/log/nginx/foo.access.log;
error_log /var/log/nginx/foo.error.log;
root /var/www/foo;
index index.php;
location / {
# Trying with 'try_files' here. No success.
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php5-fpm-foo.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
I've been trying repeatedly and evidently utterly failing with this line:
try_files $uri $uri/static /index.php;
I am missing something. Help?
I would use your static directory as document root. This ensures that nobody can execute /dynamic.php directly, however, it will be forwarded to your index.php by the named location block #php.
This configuration example is untested!
server {
index index.php;
root /var/www/foo/static;
server_name foo.bar *.foo.bar;
location / {
try_files $uri #php;
}
location #php {
include fastcgi_params;
fastcgi_pass unix:/var/run/php5-fpm-foo.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/foo/index.php;
}
}
You don't need the listen directive if it only contains 80 since this is the default.
The server_names should not contain a leading dot.
The $uri always contains the requested URI including the leading slash (e.g. /static.html) and nginx will prefix them with the document root upon invocation of try_files (e.g. /var/www/foo/static.html). Hence, you need to set your static directory before the $uri (e.g. /static$uri becomes /var/www/foo/static/static.html).
You don't need fastcgi_split_path_info because you are not using that feature.
Your try_files in your PHP location makes it impossible for nginx to properly forward things. A request for /dynamic.html does not end on .php, hence, try_files always fails.
There are a number of ways of hiding the static directory from the URL. For example, manipulating root, clever use of try_files or a rewrite.
Possibly the most obvious is this:
root /var/www/foo;
location / {
root /var/www/foo/static;
try_files $uri /index.php;
}
location ~ \.php$ { ... }
so that nginx looks in the static folder for normal files, but the parent folder for .php files.
What you were trying to achieve was something like this:
root /var/www/foo;
location / {
try_files /static$uri /index.php;
}
location ~ \.php$ { ... }
which will prefix /static to any URI before testing for existence. The /index.php must be the last element as it required processing in a different location. See this document for more.
Based on the specific example case you have given, the configuration below will return the results you listed.
server {
listen 80;
server_name .foo.bar *.foo.bar;
access_log /var/log/nginx/foo.access.log;
error_log /var/log/nginx/foo.error.log;
root /var/www/foo;
index index.php;
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php5-fpm-foo.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /static {
rewrite ^/static\.html$ /static/ last;
index static.html;
}
location ~ / {
rewrite ^ /index.php last;
}
That is ...
http://foo.bar/ - Loads index.php
http://foo.bar/static.html - Loads static/static.html
http://foo.bar/dynamic.html - Loads index.php & PHP requires dynamic.php
http://foo.bar/baz.html - Loads index.php with "not found" message
Related
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.
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.
Some details about my setup first:
I am serving a static webapp (HTML + JS) from default Nginx webroot
I have a PHP-FPM server running on localhost:9000
The destination file should be /api/webroot/index.php for FPM (always, no need to try_files etc.)
I need to forward all /api and /api-debug calls to arrive at localhost:9000, and the /app/webroot/index.php should handle all these requests.
I have the following working Nginx configuration:
upstream fastcgi_backend {
server localhost:9000;
keepalive 30;
}
server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
location ~ ^/(api|api-debug)/ {
root /app/webroot;
index index.php;
try_files $uri /api/index.php$is_args$args;
location ~ \.php$ {
fastcgi_pass fastcgi_backend;
fastcgi_split_path_info ^(?:\/api\/)(.+\.php)(.*)$;
fastcgi_param SCRIPT_FILENAME /app/webroot/$fastcgi_script_name;
include fastcgi_params;
}
}
}
}
I just want to make it more simple and efficient, because as I see it now it's a mess.
I tried to adjust for example
try_files $uri /api/index.php$is_args$args;
to
try_files $uri /api/webroot/index.php$is_args$args;
and it failed... The only reason that it works is that /api/index.php includes /api/webroot/index.php, but I see it's inefficient.
I found debugging nginx config hard, because it's not easy to test.
Thank you very much for your help in advance!
The simplest solution would be to hardwire SCRIPT_FILENAME with a value of /app/webroot/index.php and remove one of your location blocks altogether.
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location ~ ^/(api|api-debug)/ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /app/webroot/index.php;
fastcgi_pass fastcgi_backend;
}
Alternatively, to keep the flexibility of specifying a URI with a .php extension, you could simplify the configuration with:
location / {
root /usr/share/nginx/html;
index index.html index.htm;
rewrite ^/(api|api-debug)/ /index.php last;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /app/webroot$uri;
fastcgi_pass fastcgi_backend;
}
I have a nginx configuration for a react app. I however would also like to include a sitemap.php that I build dynamically with php.
So here is my nginx config:
server {
listen 80;
listen [::]:80;
server_name mysite.com;
index index.html index.htm;
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
}
location /sitemap.xml {
alias /var/www/web-app/public/sitemap.php;
}
location / {
root /var/www/web-app/public;
try_files $uri $uri/ /index.html;
default_type "text/html";
}
}
The snippets file consist of this:
# regex to split $uri to $fastcgi_script_name and $fastcgi_path
fastcgi_split_path_info ^(.+\.php)(/.+)$;
# Check that the PHP script exists before passing it
try_files $fastcgi_script_name =404;
# Bypass the fact that try_files resets $fastcgi_path_info
# see: http://trac.nginx.org/nginx/ticket/321
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;
fastcgi_index index.php;
include fastcgi.conf;
Also, this is hosted on an Ubuntu 16.04 digitalocean VPS.
My react app still loads fine. It is based on the index.html in my site root (/var/www/web-app/public). If I put test.php in the public folder, I get a 404 error. For my sitemap.xml alias, it forwards correctly to sitemap.php (also in public) but the php does not render.
So my two biggest issues here:
1. Why am I getting a 404 on /mysite.com/test.php?
2. And why is my php not rendering when it does work? (i.e. sitemap.php)
You are missing a root statement for your location ~ \.php$ block, so your PHP files will not be found. As this seems to be a common root with the location / block, simply move the statement up to server block scope:
root /var/www/web-app/public;
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
}
location / {
try_files $uri $uri/ /index.html;
default_type "text/html";
}
There are a number of ways to redirect /sitemap.xml to /sitemap.php, but a rewrite...last will be simplest and invisible to users:
location = /sitemap.xml {
rewrite ^ /sitemap.php last;
}
See this document for location syntax, and this one for the rewrite directive.
I'm trying to rewriting /assets/* -> to /theme/theme_1/*.
The rewrite url works with all files except .php-files.
Example file structure:
/theme/theme_1/images/image.jpg
/theme/theme_1/images/user.jpg
/theme/theme_1/ajax/register.php
/theme/theme_1/ajax/read.php
The problem is the PHP-files, I get a 404 with this url:
wget http://example.com/assets/ajax/read.php.
File is found (200) using full path http://example.com/theme/theme_1/ajax/read.php
All other file works fine (200):
wget http://example.com/assets/images/image.jpg
nginx config:
server {
listen 80 default_server;
root /var/www/html;
index index.php index.html
server_name mysite.com;
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location /assets {
rewrite ^/assets/(.*) /theme/theme_1/$1 break;
}
location / {
try_files $uri $uri/ /index.php?$args;
}
}
Okie you should try this
location /assets/ {
alias /var/www/html/theme/theme_1/;
}
If that doesn't work then try
location /assets/ {
alias /var/www/html/theme/theme_1/;
try_files $uri $uri/ /index.php?$args;
}
Edit-1
On second look I realize the previous answer won't work as ~ \.php { block will catch everything with php extension and the other assets block can never get called. So the solution is to nest the rewrite inside the php block. So use
location ~ \.php$ {
rewrite ^/assets/(.*)$ /theme/theme_1/$1;
include snippets/fastcgi-php.conf;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}