0

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)"
Rich Remer
  • 205
  • 1
  • 7
  • I'd like to see a bit more of your config around PHP, as well as access and error logs for the requests that do and don't work. Please edit your question, and correlate requests with log entries, don't just dump everything in. The easier it is to understand the quicker and better the help you'll get. – Tim May 16 '17 at 01:36
  • I've added the whole server block. Can you be more specific on what logs you'd like to see? I tried to add the log for the request to /about, but StackExchange gives me a character limit error. – Rich Remer May 16 '17 at 18:21
  • I've now added links to request logs. – Rich Remer May 16 '17 at 18:35
  • FYI, in general, on SF, you should inline relevant parts of the logs, rather than link to bulk logs on other sites. Two reasons 1) Other sites may lose data 2) If you correlate logs properly with requests it helps people trying to help you, so you'll get more/better/faster replies – Tim May 16 '17 at 18:59
  • I don't disagree, but I'm not sure what part of the log is relevant. As noted, Stack Exchange wouldn't let me inline it, but I noted the corresponding web requests which generate the logs (/about vs /about.php) along with the links. Also, I've added the access logs, though they don't seem very helpful. – Rich Remer May 16 '17 at 21:33

1 Answers1

1

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.

Tero Kilkanen
  • 36,796
  • 3
  • 41
  • 63
  • With this approach, none of the non-PHP (CSS, JS, etc) files are served. – Rich Remer May 16 '17 at 18:24
  • After checking some logs, I also tried checking if $document_root$uri exists, which brings me back to my original behavior of getting downloads for PHP files without extensions. – Rich Remer May 16 '17 at 18:40
  • I think I made a mistake with `rewrite` directive flag. Please try `last` instead of `break` on it. – Tero Kilkanen May 17 '17 at 09:19