Search code examples
phpherokunginxurl-rewritingfastcgi

How to serve all requests from a subpath through the same PHP file?


I'm pulling out my hair trying to get the following setup to work on my machine and on Heroku:

  • main pages are PHP files residing in the document root
  • there's an API under root/api. All requests to /api/* should be forwarded to its gateway file (api/index.php).

Locally I got this to work with a different conf file (below), but on Heroku nothing is working correctly. The best I could figure out was this:

location / {
    try_files $uri $uri/ /index.php?$query_string;
    index index.php;
}
location ~ ^/api/(.+) {
    try_files /api/index.php /api/index.php;
}
location ~ \.php(/|$) {
    try_files @heroku-fcgi @heroku-fcgi;
}

If I try to use rewrite, it complains of an infinite loop. If I try to set the gateway script as index and use try_files with their FCGI location, I get 404 - as there's nothing under the /api folder besides that script.
Using try_files and pointing the script directly makes Heroku send the .php file directly for download instead of interpreting it. How can I make it be interpreted, and still override all other /api/* requests?


Conf file working on my local machine:

server {
    listen        80;
    server_name   devshop.dev;

    index index.php;
    root  /home/myself/dev/developer-shop/www/;

    location ~ ^/api(/|$) {
        try_files $uri $uri/ /api/index.php;
        include       /etc/nginx/fastcgi_params;
        fastcgi_pass  127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
    }

    location ~ \.php(/|$) {
        include       /etc/nginx/fastcgi_params;
        fastcgi_pass  127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
    }
}

Solution

  • On your local machine PHP scripts are handled within their respective location block. On your target machine the /api/ location performs an internal rewrite which is then expected to be handled by the php block.

    Regex location blocks are ordered so that /api/index.php keeps hitting the /api/ location block - hence the redirection loop.

    Either reverse the order of the regex location blocks or more simply, use a prefix location block with a rewrite:

    location /api {
        rewrite ^ /api/index.php;
    }
    

    See this document for details.