23

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?

1
  • Isn't it possible in your case to do some URL rewriting but handle the displayed page using PHP all the time ?
    – AntoineB
    Commented May 27, 2016 at 7:09

3 Answers 3

8
+75

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;
    }
}
  1. You don't need the listen directive if it only contains 80 since this is the default.
  2. The server_names should not contain a leading dot.
  3. 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).
  4. You don't need fastcgi_split_path_info because you are not using that feature.
  5. 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.
4
  • Either .example.com or example.com *.example.com; usage of the combination of both makes no sense. Commented May 27, 2016 at 9:35
  • Actually you cannot add the try_files inside of the @php location because no dynamic.html file exists in there and nginx would not forward the request to php-fpm. However, I changed the configuration to account for the issue you mentioned. Commented May 27, 2016 at 9:37
  • Sorry I misunderstood your point 2. I thought you were saying that one should never use a server name with a leading dot. I totally agree with your analysis Commented May 27, 2016 at 9:45
  • Yeah, lets forget I mentioned it :-) Commented May 27, 2016 at 9:57
7

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.

8
  • Thanks for pointing out the mistake I made, but your suggestion is also not working. When attempting to go to /, I am redirected to //static/ (with two forward-slashes) and when attempting to go to /static.html I am redirected to /static/. =/
    – Apollo
    Commented Apr 23, 2016 at 17:42
  • @Apollo I cannot reproduce your failure mode but I did need to add a try_files statement to the first example. Commented Apr 23, 2016 at 18:53
  • @Apollo, which of the two examples in this answer did you use to get the failure described in your comment?
    – russell
    Commented May 17, 2016 at 1:02
  • @Richard, using the first example, foo.bar/ does work, but the URI changes to foo.bar//static/; foo.bar/static.html redirects to index and changes the URI to foo.bar/static/; foo.bar/dynamic.html works; foo.bar/baz.html yields a blank page.
    – Apollo
    Commented May 18, 2016 at 22:04
  • @Richard, using the second example, results are the same. Thanks!
    – Apollo
    Commented May 18, 2016 at 22:06
1

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.