Subdirectory Rewriting w/ FastCGI + PHP-FPM

Hello,

I am attempting to fastcgi to php-fpm on a separate Docker container. The path of the PHP application (Lumen 5.4) is /api/v3.

I have tried the below configuration:

example.com {
    rewrite /api/v3/ {
        r (.*)
        to {path} {path}/ /api/v3/index.php
    }

    fastcgi /api/v3/ localhost:9000 php {
        root /var/www/backend/public
    }
}

However that results in a 404 from FPM. Below are some relevant logs.

access.log
127.0.0.1 - - [27/Feb/2018:12:34:08 +1100] "GET /api/v3/example HTTP/1.1" 404 16

error.log
27/Feb/2018:12:34:08 +1100 [ERROR 0 /api/v3/index.php] Primary script unknown

php-fpm.log
172.18.0.1 - 27/Feb/2018:01:34:09 +0000 "GET /api/v3/index.php" 404

There might be some issue with the way the volume is mounted to the PHP container.

Can you share details of your Docker configuration?

Thank you for your quick reply Matthew.

I can confirm that the php container itself is ok. If i change my Caddy configuration to the following:

example.com {
    rewrite {
        r (.*)
        to {path} {path}/ /index.php
    }

    fastcgi / localhost:9000 php {
        root /var/www/backend/public
    }
}

everything works fine. Logs for a request on that configuration are as follows:

access.log
127.0.0.1 - - [27/Feb/2018:12:52:06 +1100] "GET /api/v3/example HTTP/1.1" 200 30

error.log

php-fpm.log
172.18.0.1 - 27/Feb/2018:01:52:07 +0000 "GET /index.php" 200

My understanding is that the first example, you’re attempting to serve /var/www/backend/public/api/v3/index.php and in the second example you’re serving /var/www/backend/public/index.php (both as absolute file paths within the PHP container).

My next point to troubleshoot would be to determine whether both exist and have the same file ownership and permissions inside the container, since the latter is proven to work.

That looks to be the case, as deducible from the php-fpm logs.

/var/www/backend/public/api/v3/index.php is not valid, it is indeed supposed to be /var/www/backend/public/index.php. Below are a couple more configurations and accompanying logs that i have tried.


Results in a Caddy 404, never reaches fpm container.

example.com {
    rewrite /api/v3/ {
        r (.*)
        to {path} {path}/ /index.php
    }

    fastcgi /api/v3/ localhost:9000 php {
        root /var/www/backend/public
    }
}

access.log
127.0.0.1 - - [27/Feb/2018:13:20:08 +1100] "GET /api/v3/example HTTP/1.1" 404 14

error.log

php-fpm.log


Reaches fpm container, results in a NotFoundHttpException (Lumen / Symfony)

example.com/api/v3 {
    rewrite {
        r (.*)
        to {path} {path}/ /index.php
    }

    fastcgi / localhost:9000 php {
        root /var/www/backend/public
    }
}

access.log
127.0.0.1 - - [27/Feb/2018:13:24:10 +1100] "GET /api/v3/example HTTP/1.1" 200 2408

error.log

php-fpm.log
172.18.0.1 - 27/Feb/2018:02:24:12 +0000 "GET /api/v3/index.php" 200

This is expected behaviour. You rewrite to fall back on /index.php, but your fastcgi is configured only to proxy paths beginning with /api/v3/, so the request is handled by the default Caddy static fileserver and 404’d (no index.php in your web root, I expect).

Unsure on your NotFoundHttpException, but Caddy is acting exactly as you’ve configured here. Your host is example.com/api/v3 and a request to /example on this host results a rewrite to /index.php which is appended to the host to result in example.com/api/v3/index.php. The URI is then passed on to FastCGI, resulting in a request for /api/v3/index.php, correlating with the root subdirective to the absolute file path /var/www/backend/public/api/v3/index.php on disk within the PHP container.

Thank you for the detailed response. It is very much appreciated.

The application residing on the fpm container is expecting to see the route /api/v3/example. By using the below Caddy config, and removing the /api/v3 route prefix expectation from the application…

example.com/api/v3 {
    rewrite {
        to {path} {path}/ /index.php
    }

    fastcgi / localhost:9000 php {
        root /var/www/backend/public
    }
}

…everything works.

I guess the question is how would i go about configuring Caddy to also pass through the /api/v3 prefix? Ideally i would have the single example.com label as i have quite a bit going on in that definition, e.g. the landing page, proxying of subdirectories to other services, etc. - or is doing the above the only solution?

example.com/api/v3 {
    rewrite {
        to {path} {path}/ /index.php
    }

    fastcgi / localhost:9000 php {
        root /var/www/backend/public
    }
}

unless I’m much mistaken, should be functionally equivalent to:

example.com {
  rewrite /api/v3 {
    to {path} {path}/ /api/v3/index.php
  }
  fastcgi /api/v3 localhost:9000 php {
    root /var/www/backend/public
  }
}

which is remarkably similar to what you put in the initial paragraph.

Both versions of this configuration send the request /api/v3/index.php to FastCGI with a root of /var/www/backend/public, so theoretically, neither should work unless /var/www/backend/public/api/v3/index.php exists within the PHP container.

Below are the outcomes of the two configurations:


Caddyfile

example.com {
    header / x-path {path}
    header / x-rewrite-path {rewrite_path}

    rewrite /api/v3 {
        to {path} {path}/ /api/v3/index.php
    }

    fastcgi /api/v3 localhost:9000 php {
        root /var/www/backend/public
    }
}

GET /api/v3/example

Content-Type: text/html; charset=UTF-8
Server: Caddy
Status: 404 Not Found
X-Path: /api/v3/example
X-Powered-By: PHP/7.1.14
X-Rewrite-Path: /api/v3/index.php
Date: Tue, 27 Feb 2018 04:06:58 GMT
Content-Length: 16

access.log
127.0.0.1 - - [27/Feb/2018:15:06:58 +1100] "GET /api/v3/example HTTP/1.1" 404 16

error.log
27/Feb/2018:15:06:58 +1100 [ERROR 0 /api/v3/index.php] Primary script unknown

php-fpm.log
172.18.0.1 - 27/Feb/2018:04:07:02 +0000 "GET /api/v3/index.php" 404


Caddyfile

example.com/api/v3 {
    header / x-path {path}
    header / x-rewrite-path {rewrite_path}

    rewrite {
        to {path} {path}/ /index.php
    }

    fastcgi / localhost:9000 php {
        root /var/www/backend/public
    }
}

GET /api/v3/example

Cache-Control: no-cache, private
Content-Type: application/json
Date: Tue, 27 Feb 2018 04:14:43 GMT
Server: Caddy
Status: 200 OK
X-Path: /api/v3/example
X-Powered-By: PHP/7.1.14
X-Rewrite-Path: /index.php
Content-Length: 30

access.log
127.0.0.1 - - [27/Feb/2018:15:14:39 +1100] "GET /api/v3/example HTTP/1.1" 200 30

error.log

php-fpm.log
172.18.0.1 - 27/Feb/2018:04:14:42 +0000 "GET /api/v3/index.php" 200

Well, I’m stumped. Unless there’s something else changing between tests, I’m not sure how PHP can be giving two different results for the same file here…

Can you try moving the root keyword out of fastcgi? This has caused issues before. Just put it in the main body.

What is your log statement

I think what you want is to rewrite to this: index.php?q=/api/v3. I haven’t tested, but hopefully that pushes you in the right direction. Lumen/Laravel check the q url param for the path.

I could be way off here, but having a quick look at the fastcgi code, it looks like the SCRIPT_FILENAME cgi variable is possibly being passed as /var/www/backend/public/api/v3/index.php in the below configuration

causing the

whereas this configuration

passes it as /var/www/backend/public/index.php.

In terms of CGI variables, here’s where the absolute path is stitched together. Later on the result is used to set the scriptFilename.

fpath there comes from its caller, which would be the ServeHTTP function. It’s set here, to r.URL.Path.

I find myself wondering whether or not, for site label example.com/foo/bar and a request to /foo/bar/test, the r.URL.Path is actually /foo/bar/test or just /test, which might explain the discrepancy?

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.