Nginx try_files not performing internal redirect

fastcginginxphp-fpm

I'm trying to configure nginx so it will load PHP files without an extension, but still pass along requests for missing files to a front controller.

I started out with a mostly working config like the following that properly loads URLs like "about.php" from webroot/about.php and properly sends all requests for missing files to webroot/index.php:

server {
    listen 80;
    listen 443 ssl http2;
    server_name *.example.com;
    root /srv/example/$host;

    client_max_body_size    8M;
    fastcgi_buffers         16 16k;
    fastcgi_buffer_size     32k;
    large_client_header_buffers 8 32k;

    ssl_certificate         /etc/ssl/certs/example+ca.pem;
    ssl_certificate_key     /etc/ssl/private/example.key;
    ssl_ciphers             '...';

    location / {
        try_files           $uri
                            /index.php$is_args$args;
    }

    location /blog {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://127.0.0.1:2368;
    }

    location ~ [^/]\.php(/|$) {
        fastcgi_split_path_info ^(.+\.php\b)(.*)$;
        fastcgi_param       SERVER_NAME $host;
        fastcgi_param       PATH_INFO $fastcgi_path_info;
        fastcgi_param       SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param       SCRIPT_NAME /index.php;
        fastcgi_index       index.php;
        include             fastcgi.conf;
        fcgi_pass php-fpm;
    }
}

I attempt to add a .php arg to the try_files like the following so I can load URLs like /about from webroot/about.php:

location / {
    try_files           $uri
                        $uri.php
                        /index.php$is_args$args;
}

Now, /about.php loads as expected, but /about downloads the file. I can't figure out how to force an internal redirect while maintaining the catch-all for the front controller. I've seen several people suggest removing =404, but that's not the issue here.

error log (/about): https://pastebin.com/XbdjLri4

173.196.243.178 - - [16/May/2017:21:35:03 +0000] "GET /about HTTP/1.1" 200 6176 "-" "Wget/1.18 (linux-gnu)"

error log (/about.php): https://pastebin.com/ay32GZ0m

173.196.243.178 - - [16/May/2017:21:35:10 +0000] "GET /about.php HTTP/1.1" 200 25848 "-" "Wget/1.18 (linux-gnu)"

Best Answer

The issue here is that the value of request URI nginx sees here after try_files is still about. Since there is no other location for that, it simply sends the file to browser.

I think this approach would work:

location / {
    if (!-e $uri) {
        rewrite (.+)(?!\.php)$ $1.php last;
    }
    try_files $uri /index.php$is_args$args;
}

location ~ [^/]\.php(/|$) {
    ...
    fcgi_pass php-fpm;
}

Here we check separately if the requested file exists on the server. If it does not, then we rewrite the URL if it does not have the .php extension, and add the PHP extension to it.

Then we execute the try_files.