Php_fastcgi + file_server browse

1. Caddy version (caddy version):

v2.3.0

2. How I run Caddy:

systemctl start caddy

a. System environment:

Arch Linux - up-to-date, systemd

b. Command:

N/A

c. Service/unit/compose file:

The default Arch Linux caddy.service file, not edited at all.

[Unit]
Description=Caddy web server
Documentation=https://caddyserver.com/docs/
After=network-online.target
Wants=network-online.target systemd-networkd-wait-online.service
StartLimitIntervalSec=14400
StartLimitBurst=10

[Service]
User=caddy
Group=caddy
Environment=XDG_DATA_HOME=/var/lib
Environment=XDG_CONFIG_HOME=/etc
ExecStartPre=/usr/bin/caddy validate --config /etc/caddy/Caddyfile
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile
ExecStopPost=/usr/bin/rm -f /run/caddy/admin.socket

# Do not allow the process to be restarted in a tight loop. If the
# process fails to start, something critical needs to be fixed.
Restart=on-abnormal

# Use graceful shutdown with a reasonable timeout
TimeoutStopSec=5s

LimitNOFILE=1048576
LimitNPROC=512

# Hardening options
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
DevicePolicy=closed
LockPersonality=true
MemoryAccounting=true
MemoryDenyWriteExecute=true
NoNewPrivileges=true
PrivateDevices=true
PrivateTmp=true
ProcSubset=pid
ProtectClock=true
ProtectControlGroups=true
ProtectHome=true
ProtectHostname=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectProc=invisible
ProtectSystem=strict
RemoveIPC=true
ReadWritePaths=/var/lib/caddy /var/log/caddy /run/caddy
RestrictNamespaces=true
RestrictRealtime=true
RestrictSUIDSGID=true

[Install]
WantedBy=multi-user.target

d. My complete Caddyfile or JSON config:

{
	admin "unix//run/caddy/admin.socket"
}

http://hoefnix.sys1 {
	root * /srv/http
	file_server browse
	php_fastcgi unix//run/php-fpm/php-fpm.sock
	log {
		output file /var/log/caddy/access-hoefnix.log
	}
}

3. The problem I’m having:

I’m running caddy2 on a development machine called hoefnix.sys1 in an internal network.

The document root has some sub directories. If a sub directory doesn’t contain an index file (.php of .html), then I want to get a directory listing. But when a subdirectory does have and index file, that index file should be returned.

My problem is very similar to this topic:
Struggling with php_fastcgi and browsing a subdirectory

Except in my case there isn’t a specific sub directory for directory listing. The solution in that topic (with “route”) works well when there is a specific directory for listing. But I want to get a directory listing in every directory, except if that directory contains an index (index.php or index.html).

The following Caddyfile does what I want, except that php files aren’t parsed.

http://hoefnix.sys1 {
	root * /srv/http
	file_server browse
	log {
		output file /var/log/caddy/access-hoefnix.log
	}
}

So how do I combine “file_server browse” and “php_fastcgi”.

4. Error messages and/or full log output:

N/A

5. What I already tried:

When i tried this:

route {
    file_server /* browse
    php_fastcgi unix//run/php-fpm/php-fpm.sock
    file_server
}

Then, logically, php files aren’t parsed anymore.

6. Links to relevant resources:

Please use ``` on the lines before and after your config and logs to use code formatting, to preserve whitespace. It’s quite hard to read your post without it.

In your case, the php_fastcgi directive will not do what you want, because it has built-in try_files logic which is incompatible with what you’re trying to do. That directive is optimized for modern PHP apps which use a single index.php as an entrypoint for routing. See the expanded form to get an understanding of what it does:

To do what you need, you’ll likely want to use a modified version of the expanded form. I’m a bit unclear on your exact requirements (would need to see a tree view of your site root and some example requests and the expected result for each) so I can’t really suggest an exact config without more details.

My apologies for leaving out the backticks. I’ve added them right now.

The tree of my site root look like:
/somedata/file1.tif
/somedata/file2.tif
/someapp/index.php
/someapp/something.php
/static/index.html
/index.php

Request with expected results:
/ → /index.php
/static → /static/index.html
/somedata → directory listing of /somedata
/someapp/something.php → /someapp/something.php

These names are just some examples, in reality there are many more.

Thanks for your answer, I will try to understand what’s going on in the expanded form and adapt it to my situation.

I think you might do something like this:

route {
	# Add trailing slash for directory requests
	@canonicalPath {
		file {path}/index.php {path}/index.html
		not path */
	}
	redir @canonicalPath {path}/ 308

	# If the requested file does not exist, try index files
	@indexFiles file {
		try_files {path} {path}/index.php {path}/index.html
		split_path .php
	}
	rewrite @indexFiles {http.matchers.file.relative}

	# Proxy PHP files to the FastCGI responder
	@phpFiles path *.php
	reverse_proxy @phpFiles unix//run/php-fpm/php-fpm.sock {
		transport fastcgi {
			split .php
		}
	}
}

The key difference is that {path}/index.html is added to the try_files matcher, and index.php is removed as the fallback (which would prevent browsing from working).

Now that I wrote that out, I wonder if this might work the same (haven’t tested it):

php_fastcgi unix//run/php-fpm/php-fpm.sock {
	index {path}/index.html
}

By default, the “index” is index.php, but I think replacing that {path}/index.html might work :thinking:

Give both of those a shot!


Edit: Actually I don’t think the index override will work because that’ll also affect the redir and the existing {path}/index.php in the try_files.

I think this might do the trick though:

@indexFiles file {
	try_files {path} {path}/index.php {path}/index.html
	split_path .php
}
rewrite @indexFiles {http.matchers.file.relative}

php_fastcgi unix//run/php-fpm/php-fpm.sock {
	index off
}

Setting index to off basically gets rid of the built-in redir and rewrite, so you can do your own instead. (I sorta forgot I implemented that :joy:)

1 Like

Rewiring the route does exactly what I want. Thumbs up!

Like you already though, the second solution didn’t work; overriding the index affects the redir to index.php .

The last solution where the index got rebuild, works perfect as well.

For now I will stick to the first solution, because it is the most educational and the flexibility allows me to reorganise some other things.

I really appreciate you fast and extensive help!

2 Likes

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