Apache to Caddy migration with non-standard subdirectory setup

1. Caddy version (caddy version):

2.2.2

2. How I run Caddy:

As a systemd service

a. System environment:

Debian 10 (Buster)

b. Command:

N/A

c. Service/unit/compose file:

Systems default config

d. My complete Caddyfile or JSON config:

The main Caddyfile only contains import vhosts/*, and my email address for LetsEncrypt, each vhost is inside a single config file.

Here’s the concerned vhost:

jdr.pierstoval.com {
        root * /var/www/jdr.pierstoval.com/www/
        encode gzip
        file_server
        php_fastcgi /esteren/* unix//run/php/php7.74-fpm.sock
        tls pierstoval@gmail.com
        log {
                output file /var/www/jdr.pierstoval.com/access.log {
                        roll_size 512mb
                        roll_keep_for 720h
                }
        }
}

3. The problem I’m having:

I’m migrating a PHP application which web entrypoint is hosted in a subdirectory.
It was previously hosted with Apache2, here’s the vhost:

<IfModule mod_ssl.c>
<VirtualHost *:443>
    ServerAdmin pierstoval+jdr.pierstoval.com@gmail.com
    ServerName jdr.pierstoval.com

    DocumentRoot /var/www/jdr.pierstoval.com/www

    Include /etc/apache2/conf-available/php7.3-fpm.conf

    <Directory /var/www/jdr.pierstoval.com/www>
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog /var/www/jdr.pierstoval.com/error.log
    LogLevel warn
    CustomLog /var/www/jdr.pierstoval.com/access.log combined
    ServerSignature Off
Include /etc/letsencrypt/options-ssl-apache.conf
SSLCertificateFile /etc/letsencrypt/live/pierstoval.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/pierstoval.com/privkey.pem
</VirtualHost>
</IfModule>

It is important to note that the /var/www/jdr.pierstoval.com/www/esteren directory is a symlink to /var/www/jdr.pierstoval/esteren/webroot/.

The project is open-source and its source code is in the links below.

4. Error messages and/or full log output:

When I go to Corahn Rin - Accueil (the home page of the project), it works but there are several PHP errors that indicate that some files have not been loaded (especially the config.php file that is in the project directory).

It seems that when I make a request on a non-existent file (such as Corahn Rin - Accueil, the homepage in French), Caddy doesn’t proxy the entire request to FastCGI, or maybe it’s proxied improperly. I could not find a way to debug this part :confused:

5. What I already tried:

I tried copy/pasting the php_fastcgi expanded form but as I’m a total newcomer to Caddy, I’m not sure of what I should modify, how, where, etc.

I also tried to debug the PHP entrypoint file (the index.php file), but it’s not even called…

6. Links to relevant resources:

Project’s source code: GitHub - StudioAgate/CorahnRinV1: Character generator for Shadows of Esteren / Les Ombres d'Esteren.

By default, Caddy’s fastcgi transport doesn’t follow symlinks, but there is an option to enable it (unfortunately currently undocumented on the docs site, but it will be up along with the v2.3.0 stable release):

php_fastcgi /esteren/* unix//run/php/php7.74-fpm.sock {
	resolve_root_symlink
}

Also, is php7.74.fpm.sock a typo? Shouldn’t it be php7.4-fpm.sock?

Also, is php7.74.fpm.sock a typo? Shouldn’t it be php7.4-fpm.sock?

Yes, it’s a typo right here, but it’s written correctly in the Caddyfile :grin:

I implemented your resolve_root_symlink suggestion and it still does not work :sob:

I just tried this, after re-reading the php_fastcgi documentation:

php_fastcgi /esteren/* unix//run/php/php7.4-fpm.sock {
        root /var/www/jdr.pierstoval.com/esteren/
        index index.php
        resolve_root_symlink
}

But it doesn’t work either.

I think I’m misunderstanding something here, but I can’t figure out what :slight_smile:

Okay - I don’t think I understand the problem, honestly. Which requests (i.e. which paths) do you expect to do what exactly?

I’ve never seen Apache used with php-fpm before (mod_php is much more common) so I don’t know what its default behaviour compares to Caddy’s. The fastcgi behaviour of Caddy is easier to compare to nginx’s (to me at least).

Hmm. Thinking about the paths involved, and I’m making big assumptions here, but I think that the trouble might be with the try_files logic built-in, cause the index.php fallback probably tries to serve /var/www/jdr.pierstoval.com/www/index.php which doesn’t exist, if I understand correctly?

If that’s the case, there’s a couple ways it could be solved. One would be to strip the /esteren part from the path using handle_path, then change the root to /var/www/jdr.pierstoval.com/esteren/webroot so that any try_files rewrite would go to the right place.

handle_path /esteren/* {
	root * /var/www/jdr.pierstoval.com/esteren/webroot
	php_fastcgi unix//run/php/php7.4-fpm.sock
}

Another way might be to add your own try_files first which has “correct” paths on it:

handle /esteren/* {
	try_files {path} {path}/index.php /esteren/index.php
	php_fastcgi unix//run/php/php7.4-fpm.sock
}

There are some fundamental differences with both of these; stripping the path with handle_path will cause the PHP app to not see the /esteren/ part of the path (I think), but the handle + try_files way would preserve that. Not sure if it matters for your app.

All that said, I think this interaction between the path matcher on php_fastcgi and the try_files built into it hasn’t been considered before, so it could be thought of as a bug, but I’m not sure I fully understand the problem right now to make a decision on whether it’s something that should be revised in Caddy or not. TBD. But basically I’m thinking that the path matcher on php_fastcgi should possibly bubble down to the try_files within, similar to that 2nd solution above.

Most users don’t typically need to use a path matcher on php_fastcgi though, they typically just use it without a matcher because their index.php will be in their actual root and any static files will be alongside.

So yeah. Try those out I guess, see where it gets you. Maybe turn on debug logging (put the following at the top of your config) to see some more details on how requests are handled, and use curl -v to make the requests to take the browser out of the equation when coming back with more questions :slight_smile:

{
	debug
}
1 Like

Thank you so much for your very exhaustive answer!

I tested your very first solution and it worked perfectly :grin:

Thanks a lot!

1 Like

Great!

Does the 2nd work as well? Just trying to better understand how this all behaves. Because like I said there might be room for us to improve Caddy’s default behaviour to make this work a bit better.

The second solution does not work, it returns a blank page on any HTTP request

This topic was automatically closed after 30 days. New replies are no longer allowed.