Changes of behaviour with 2.9.0 when php_fastcgi present

1. The problem I’m having:

After upgrading to 2.9.0, my web site no longer served /index.html by default, but instead gave a page saying “no input file specified” (a simple 404, I guess). Requesting index.html explicitly worked perfectly.

When I commented out the php_fastcgi directive, the site worked as expected.

This site has some PHP pages (hence the php directive), but it is not a PHP site per se, just individual pages, so there is no index.php at the root level (there are in some picture album directories, though).

Adding the directive “rewrite / /index.html” (as in the config below) restored the expected behaviour - but why was that necessary in 2.9.0 when it wasn’t in 2.8.4?

Paul

2. Error messages and/or full log output:

The only error message is as above, and the corresponding log entry is:

2a02:8010:f01e:1:2d3b:7718:2812:73b9 - - [08/Jan/2025:20:36:53 +0000] "GET / HTTP/2.0" 404 25

3. Caddy version:

2.9.0 (previous was 2.8.4)

4. How I installed and ran Caddy:

Caddy runs as a Windows service under the control of nssm

a. System environment:

Windows Server 2016

b. Command:

run by nssm, with no special options.

c. Service/unit/compose file:

d. My complete Caddy config:

{
	#debug
	email pwh@cassland.org
	order replace after encode
	order webdav before file_server
	grace_period 10s
}

####################################################################################

cassland.org,
www.cassland.org {
	root * ..\cassland.org
    rewrite / /index.html         ###################Added to get round issue

#  The following lines cause a randomly chosen image selected by the corresponding .php file to be served.
	uri /images/QCavi.jpg replace .jpg .php
	uri /images/FireDog.jpg replace .jpg .php

	basic_auth /CherwellSingers/* {
		cherwell JDJhJDE0JHBoRjNLQUFJTjNoWTMwSC80eVJBZXVFNHg0cnBRZGowNEZBSTJITFh6eURPLlZxbHVDdzA2
	}
	basic_auth /Nic/* {
		NBH JDJhJDE0JHo5WGN5eGZ6SkNiRlEwYmtaZjlMMWVMeE1rVWIwZjNCNGhDdXJvRGRjVWRxQ1I5V3ozcWpH
	}
	basic_auth /KinoKinder/* {
		Download JDJhJDE0JGhmcGlGTy5oZGd6NTJJYVpVTnBmQy5jM25tbE5SWUVlNFZKN0JxaEFqQno2NnJaODQ0UGRH
	}

	encode gzip

	php_fastcgi {
		to localhost:9001
		to localhost:9002
		to localhost:9003
		to localhost:9004
		to localhost:9005
		to localhost:9006
		to localhost:9007
		to localhost:9008
	}

	@get method GET

	rewrite /webdav/Paul /webdav/Paul/
	route /webdav/Paul/* {
		basic_auth {
			PaulWebDav JDJhJDE0JDQ0Z2dubEgyUS5ZMHllTHp3SkpiWWVuOG9Kd3ZvMFQ2eVUvQnE0NWNBa3BnekFwTlREQ0ky
		}
		file_server @get browse
		webdav
	}

	rewrite /webdav/Viv /webdav/Viv/
	route /webdav/Viv/* {
		basic_auth {
			VivWebDav JDJhJDE0JE1EVmQ5V3cvRlVsRVpUYVdmY0dYaGVneHg2aEhIODFObHZQVnlmSXB2NklnclNURlZ6YjBl
		}
		file_server @get browse
		webdav
	}

	@browsedirs {
		path /Album
		path /Album/*
		path /images
		path /images/*
		path /sounds
		path /sounds/*
		path /videos
		path /videos/*
		path /Nic
		path /Nic/*
		path /scores
		path /scores/*
		path /Varicam
		path /Varicam/*
		path /TascamMod
		path /TascamMod/*
		path /words/Wesley
		path /words/Wesley/*
		path /KinoKinder
		path /KinoKinder/*
		path /Hostel
		path /Hostel/*
		path /CherwellSingers/Programs
		path /CherwellSingers/Programs/*
    	path /CherwellSingers/Posters
    	path /CherwellSingers/Posters/*
        path /Vivien
        path /Vivien/*
        path /Recordings/JamesBrown
        path /Recordings/JamesBrown/*
	}
	file_server @browsedirs browse
	file_server

	handle_errors {
		@not404 expression {http.error.status_code} != "404"
		route {
			respond @not404 {http.error.status_code} ""
			rewrite * /404.html
			file_server
		}
	}

	log {
		output file .\Logs\CLaccess.log
		format transform "{common_log}" # for formatter with Caddy 2.5ff
	}
}

####################################################################################

5. Links to relevant resources:

I have just found another related problem: my custom error handler is no longer working. Ignore the fact that the error handler in the config above is broken (it used to work, but I clearly missed a change in syntax in a past update) - I have replaced it with a working error handler.

In 2.9.0 the error handler is not being invoked; the error is being thrown, but handle_errors is being ignored and the default message sent instead. But in the same way as was the reason for starting this thread, removing the php_fastcgi directive makes it work again as expected.

So I can now point to two things which in my system break in 2.9.0, but are restored by removing the php_fastcgi directive - the defaulting to index.html and invoking the custom error handler.

(LATER)

And more - it seems that any (sub)directory access fails to find index.html, and the browse function of file_server also fails (giving a 404). Again php_fastcgi is the key.

Paul

Can you replace your php_fastcgi with the expanded form from here and share the result?

I’ve tried your suggestion. Once I had made the links to the handler correctly (including my "to"s and removing the handler placeholder in the reverse_proxy directive), it worked, and resolved all the issues I’ve mentioned while retaining PHP functionality.

That’s got me sorted for now; but I guess something’s wonky in how the php_fastcgi directive is doing things, and obviously it would be nice to be able to use the compact directive.

Thanks,

Paul

Thank you for the confirmation. I guess this optimization PR broke your use case.

This area of the code isn’t my strongest. I’m checking with the other maintainers.

This is more likely fileserver: Add `first_exist_fallback` strategy for `try_files` by dunglas · Pull Request #6699 · caddyserver/caddy · GitHub.

Could you try if the following fix the issue please?

php_fastcgi {
    try_files {path} {path}/index.php {path}/index.html index.php index.html
}

My guess is that before fileserver: Add `first_exist_fallback` strategy for `try_files` by dunglas · Pull Request #6699 · caddyserver/caddy · GitHub, as index.php doesn’t exist the try_files directive was setting the file to =404, and file_server was then trying index.html.

Because the policy is now first_exist_fallback, index.php is “hardcoded” even if it doesn’t exist. This doesn’t hurt in most cases, except in this one.

I wonder if this is worth it to fix, if the workaround I suggested works.

1 Like

I tried this; it fixed the index files and browse issues, but did not fix the handle_errors issue.

Paul

Further testing showed that this did not fix the index.html and browse in directories other than the root; as well as leaving the handle_errors issue.

When I tried to browse my /images directory, it served the home page. When I tried a subdirectory with an index.html, it served the correct page, but without checking for basic_auth (I hadn’t typed a trailing slash - if I added a trailing slash, the auth dialogue then appeared).

Paul

The php_fastcgi option is really geared towards how big PHP web applications using various frameworks have been constructed in the last decade or so. Basically, every request that does not match a pre-existing static file gets sent over to a single entry point PHP router script, usually simply called index.php, which then dynamically decides what to do with it, including generating possible 40x error pages should it decide so.

It sounds like you’d rather have a very traditional setup where only disparate *.php get processed by PHP but have everything else still get handled by the web server.

I wonder if you wouldn’t be better served with a config more like this, replacing anything php related with this snippet:

	@phpFiles {
		file
		path *.php
	}
	reverse_proxy @phpFiles {
		to localhost:9001
		to localhost:9002
		to localhost:9003
		to localhost:9004
		to localhost:9005
		to localhost:9006
		to localhost:9007
		to localhost:9008
		transport fastcgi {
			split .php
		}
	}

Not at all disputing of course that you’ve bumped into a genuine change in the new version of Caddy making what used to work no longer do so for you. But it is equally true that the php_fastcgi shorthand was always meant for the router script use case, and it seems like a happy accident things still worked out OK for your purposes previously.