Caddy repeatedly serving index.php even when requestedfile.php exists

1. Output of caddy version:

v2.5.2 h1:eCJdLyEyAGzuQTa5Mh3gETnYWDClo1LjtQm2q9RNZrs=

2. How I run Caddy:

a. System environment:

Ubuntu 20.04.4 LTS (GNU/Linux 5.4.0-122-generic x86_64)

b. Command:

systemctl start caddy

c. Service/unit/compose file:

[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target
OnFailure=crashmailserv@%n.service

StartLimitIntervalSec=10
StartLimitBurst=5

[Service]
User=caddy
Group=caddy
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile
ExecStartPost=+/bin/systemctl start upmailserv@%n.service
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE
Environment=DO_AUTH_TOKEN=redacted
Restart=always

[Install]
WantedBy=multi-user.target

d. My complete Caddy config:

(gen) {
        encode gzip
        try_files {path} {path}.html
        php_fastcgi unix//var/run/php/php8.1-fpm.sock
        file_server
        handle_errors {
                rewrite * /{http.error.status_code}
                reverse_proxy https://http.cat {
                        header_up Host http.cat
                }
        }
        log {
                format console
                output file /var/log/caddy.log {
                        roll_size 25mb
                        roll_keep 20
                        roll_keep_for 720h
                }
        }
}

(e-gen) {
        encode gzip
        try_files {path} {path}.html
        php_fastcgi unix//var/run/php/php8.1-fpm.sock
        file_server
        respond /seed/* "Gone" 410 {
        close
        }
        respond /nh/* "Gone" 410 {
        close
        }
        handle_errors {
                rewrite * /{http.error.status_code}
                reverse_proxy https://http.cat {
                        header_up Host http.cat
                }
        }
        log {
                format console
                output file /var/log/caddy.log {
                        roll_size 25mb
                        roll_keep 20
                        roll_keep_for 720h
                }
        }
}

(dns) {
        tls {
                dns digitalocean redacted
        }
}

(header-gen) {
        header {
                Strict-Transport-Security "max-age=31536000; includeSubdomains; preload"
                X-XSS-Protection "1; mode=block"
                X-Content-Type-Options "nosniff"
                X-Frame-Options "DENY"
        }
}

(bfm-header) {
        header {
                Strict-Transport-Security "max-age=31536000; includeSubdomains; preload"
                X-XSS-Protection "1; mode=block"
                X-Content-Type-Options "nosniff"
                X-Frame-Options "DENY"
                Cache-Control: no-cache, must-revalidate
        }
}
eiphax.tech {
        header {
                Strict-Transport-Security "max-age=31536000; preload"
                X-XSS-Protection "1; mode=block"
                X-Content-Type-Options "nosniff"
                X-Frame-Options "DENY"
        }
        root * /var/www/eipmain/webroot
        import e-gen
}

facts.eiphax.tech {
        import header-gen
        root * /var/www/eipmain/facts
        import gen
}

laundry.eiphax.tech {
        import header-gen
        root * /var/www/eipmain/webroot/laundry
        import gen
}

soultrader.net.au {
        import header-gen
        root * /var/www/st
        import gen
}

blog.eiphax.tech {
        import header-gen
        root * /var/www/blog
        import gen
}

bytes.eiphax.tech {
        import header-gen
        root * /var/www/bytes
        import gen
}

album.eiphax.tech {
        import header-gen
        root * /var/www/lychee/public
        import gen
}

bin.eiphax.tech {
        import header-gen
        root * /var/www/eipbin
        import gen
}

3ds.eiphax.tech {
@ytbad {
    header Referer *youtube.com*
}
@ytbad2 {
    header Referer *youtu.be*
}
    rewrite @ytbad /youtube.html
    rewrite @ytbad2 /youtube.html
        import header-gen
        root * /var/www/eipmain/3ds
        import gen
}

http://wiiu.eiphax.tech {
@ytbad {
    header Referer *youtube.com*
}
@ytbad2 {
    header Referer *youtu.be*
}
    rewrite @ytbad /youtube.html
    rewrite @ytbad2 /youtube.html
        root * /var/www/eipmain/wiiu
        import gen
}

https://wiiu.eiphax.tech {
@ytbad {
    header Referer *youtube.com*
}
@ytbad2 {
    header Referer *youtu.be*
}
    rewrite @ytbad /youtube.html
    rewrite @ytbad2 /youtube.html
        root * /var/www/eipmain/wiiu/resources
        import gen
        import header-gen
}

nx.eiphax.tech {
@ytbad {
    header Referer *youtube.com*
}
@ytbad2 {
    header Referer *youtu.be*
}
    rewrite @ytbad /youtube.html
    rewrite @ytbad2 /youtube.html
        import header-gen
        root * /var/www/eipmain/nx
        import gen
}

nintendohomebrew.com {
        header {
                Strict-Transport-Security "max-age=31536000; preload"
                X-XSS-Protection "1; mode=block"
                X-Content-Type-Options "nosniff"
                X-Frame-Options "DENY"
        }
        handle_errors {
                rewrite * /{http.error.status_code}
                reverse_proxy https://http.cat {
                        header_up Host http.cat
                }
        }
        root * /var/www/eipmain/nh
        respond /seed/* "Gone" 410 {
        close
        }
        import gen
}

http://bhax.nintendohomebrew.com {
        root * /var/www/bhax/web/nbhax
        try_files {path} {path}.html
        encode gzip
}

bfm.nintendohomebrew.com, seedminer.hacks.guide {
        import bfm-header
        root * /var/www/eipmain/nh/seed
        reverse_proxy localhost:8082
        encode gzip
        file_server
        handle_errors {
                rewrite * /{http.error.status_code}
                reverse_proxy https://http.cat {
                        header_up Host http.cat
                }
        }
        log {
                level error
                format console
                output file /var/log/bfm_error.log {
                        roll_size 25mb
                        roll_keep 20
                        roll_keep_for 720h
                }
        }
}

http://part1dumper.nintendohomebrew.com https://part1dumper.nintendohomebrew.com {
        reverse_proxy localhost:8081
}

shitpost.lol {
        import header-gen
        root * /var/www/sp
        import gen
}

hacc.me please.hacc.me {
        import header-gen
        root * /var/www/hacc
        import gen
}
friigaemsworld.com {
        import header-gen
        root * /var/www/frigam
        import gen
}

uwu.tax {
        import header-gen
        root * /var/www/uwu
        import gen
}

conversation.id {
        import header-gen
        root * /var/www/conv
        import gen
}

puebes.com {
        import header-gen
        root * /var/www/puebes
        import gen
}

four.family {
    import header-gen
    root * /var/www/four
    import gen
}

230421.wedding {
    import header-gen
    root * /var/www/wedding
    import gen
}

photos.230421.wedding {
        import header-gen
        root * /mnt/wedding/lychee/public
        import gen
}

easymail.sydney {
    import header-gen
    root * /var/www/easymail
    import gen
}

durriesberg.biz {
    import header-gen
    root * /var/www/dberg
    import gen
}

news.eiphax.tech {
    import header-gen
    root * /var/www/news
    import gen
}

blep.co {
    import header-gen
    root * /var/www/blep
    import gen
}

deadletters.club {
    import header-gen
    root * /var/www/deadletters
    import gen
}

photos.four.family {
        import header-gen
        root * /mnt/charlie/public
        import gen
}

photos.moonaglio.wedding photos.agliomoon.wedding moonaglio.wedding agliomoon.wedding {
        import header-gen
        root * /var/www/moonaglio/public
        import gen
}

3. The problem I’m having:

I’m trying to serve/access PHP files which are really just HTML, but using PHP for things like static headers/footers. PHP FastCGI is enabled for all configs, as far as I am aware, and seems to be working (see https://bfm.nintendohomebrew.com for an example). However, when a ‘simple’ HTML file is asked to use ‘simple’ PHP (example <?php include "file.php" ?> inside an otherwise valid HTML file), it just serves the index.php page repeatedly even when requestedfile.php does exist in the root directory.

4. Error messages and/or full log output:

ex/\\think\\app/invokefunction&function=call_user_func_array&vars[0]=md5&vars[1][]=HelloThinkPHP21"],"Content-Type":[],"Server":["Caddy"],"Connection":["close"]}}
Aug 25 11:10:51 eiphax.tech caddy[893607]: {"level":"info","ts":1661389851.3388712,"logger":"http.log.access","msg":"handled request","request":{"remote_ip":"66.249.66.152","remote_port":"61587","proto":"HTTP/1.1","method":"GET","host":"seedminer.hacks.guide","uri":"/","headers":{"Connection":["keep-alive"],"Accept":["*/*"],"From":["googlebot(at)googlebot.com"],"User-Agent":["Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"],"Accept-Encoding":["gzip, deflate, br"]}},"user_id":"","duration":0.000088925,"size":0,"status":308,"resp_headers":{"Server":["Caddy"],"Connection":["close"],"Location":["https://seedminer.hacks.guide/"],"Content-Type":[]}}
Aug 25 11:17:03 eiphax.tech caddy[893607]: {"level":"info","ts":1661390223.8720078,"logger":"http.log.access","msg":"handled request","request":{"remote_ip":"185.7.214.117","remote_port":"48406","proto":"HTTP/1.1","method":"GET","host":"157.230.80.146:80","uri":"/?a=fetch&content=<php>die(@md5(HelloThinkCMF))</php>","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"],"Content-Type":["application/json"],"Accept-Encoding":["gzip"],"Connection":["close"]}},"user_id":"","duration":0.001043372,"size":0,"status":308,"resp_headers":{"Content-Type":[],"Server":["Caddy"],"Connection":["close"],"Location":["https://157.230.80.146/?a=fetch&content=<php>die(@md5(HelloThinkCMF))</php>"]}}
Aug 25 11:18:46 eiphax.tech caddy[893607]: {"level":"info","ts":1661390326.342574,"logger":"http.log.access","msg":"handled request","request":{"remote_ip":"185.21.216.169","remote_port":"51106","proto":"HTTP/1.1","method":"GET","host":"157.230.80.146","uri":"/","headers":{"User-Agent":["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36"],"Accept":["*/*"],"Accept-Encoding":["gzip"]}},"user_id":"","duration":0.000075451,"size":0,"status":308,"resp_headers":{"Server":["Caddy"],"Connection":["close"],"Location":["https://157.230.80.146/"],"Content-Type":[]}}
Aug 25 11:18:57 eiphax.tech caddy[893607]: {"level":"info","ts":1661390337.2292178,"logger":"http.log.access","msg":"handled request","request":{"remote_ip":"40.77.167.101","remote_port":"51840","proto":"HTTP/1.1","method":"GET","host":"230421.wedding","uri":"/robots.txt","headers":{"Connection":["Keep-Alive"],"Accept":["*/*"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Accept-Encoding":["gzip, deflate"],"From":["bingbot(at)microsoft.com"],"User-Agent":["Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)"]}},"user_id":"","duration":0.000067766,"size":0,"status":308,"resp_headers":{"Location":["https://230421.wedding/robots.txt"],"Content-Type":[],"Server":["Caddy"],"Connection":["close"]}}
Aug 25 11:19:00 eiphax.tech caddy[893607]: {"level":"info","ts":1661390340.6366103,"logger":"http.log.access","msg":"handled request","request":{"remote_ip":"157.55.39.59","remote_port":"12096","proto":"HTTP/1.1","method":"GET","host":"230421.wedding","uri":"/","headers":{"From":["bingbot(at)microsoft.com"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Accept-Encoding":["gzip, deflate"],"User-Agent":["Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)"],"Connection":["Keep-Alive"],"Accept":["*/*"]}},"user_id":"","duration":0.000062659,"size":0,"status":308,"resp_headers":{"Server":["Caddy"],"Connection":["close"],"Location":["https://230421.wedding/"],"Content-Type":[]}}
Aug 25 11:24:30 eiphax.tech caddy[893607]: {"level":"info","ts":1661390670.4957132,"logger":"http.log.access","msg":"handled request","request":{"remote_ip":"66.249.66.157","remote_port":"59258","proto":"HTTP/1.1","method":"GET","host":"four.family","uri":"/","headers":{"If-Modified-Since":["Sat, 20 Aug 2022 03:52:12 GMT"],"Connection":["keep-alive"],"Accept":["text/html,application/xhtml+xml,application/signed-exchange;v=b3,application/xml;q=0.9,*/*;q=0.8"],"From":["googlebot(at)googlebot.com"],"User-Agent":["Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.79 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"],"Amp-Cache-Transform":["google;v=\"1..8\""],"Accept-Encoding":["gzip, deflate, br"]}},"user_id":"","duration":0.000298645,"size":0,"status":308,"resp_headers":{"Server":["Caddy"],"Connection":["close"],"Location":["https://four.family/"],"Content-Type":[]}}

5. What I already tried:

Not much. I’m a little out of my depth in regards to what PHP is actually doing, beyond that I’m aware most PHP apps serve everything from a single index.php but that’s not what my site’s doing. I assume I’ve screwed up some part of the config.
Looking into the FastCGI option, it seems like requestedfile.php should be served when it exists, and falling back to serving index.php at /requestedfile.php should only happen if requestedfile.php doesn’t exist.

6. Links to relevant resources:

Actually, you don’t have php_fastcgi at all for that site, you only have a reverse_proxy. Also please keep in mind that reverse_proxy will be handling all requests for that site, and the file_server will never be reached.

The difference is that php_fastcgi under the hood has a *.php path matcher, so it will only handle PHP requests, and it will let anything else fall through to file_server. The reverse_proxy directive has no such logic, it will proxy every request unless you apply a matcher to it.

Caddy sorts directives according to a predetermined directive order and reverse_proxy is higher on the list than file_server, so it will always handle requests first (unless configured with a matcher).

Those are just your access logs. We need to also see your runtime logs, which Caddy logs to stderr by default. Please also turn on the debug global option by adding this at the top of your Caddyfile:

{
	debug
}

Take a look at this page in the docs to know how to see Caddy’s logs:

The try_files here will probably mess things up, actually.

I think what you want instead is to manipulate the try_files built into php_fastcgi instead. Try this:

php_fastcgi unix//var/run/php/php8.1-fpm.sock {
	try_files {path} {path}.html {path}/index.php /index.php
}
1 Like

Edit: Sorry, was so caught up in replying, I forgot to thank you. Thank you for your advice, you’ve saved me a lot of trouble a few times now.

You’re right, sorry. I had a lot of help setting up BFM, all I remembered is that it’s using PHP.

journalctl -u caddy --no-pager | less +G just seems to provide the access logs, even though the docs say

When running with our official service file, Caddy’s output will be redirected to journalctl

Okay, but not all of my files are php… so I still need some kind of try_files or file matcher outside of the FastCGI try_files…
Having said that, I’ve made some changes:

(gen) {
        encode gzip
        uri strip_suffix .html
        try_files {path} {path}.php {path}.html
        php_fastcgi unix//var/run/php/php8.1-fpm.sock {
        try_files {path} {path}.php index.php
        }
        file_server
        handle_errors {
                rewrite * /{http.error.status_code}
                reverse_proxy https://http.cat {
                        header_up Host http.cat
                }
        }
        log {
                format console
                output file /var/log/caddy.log {
                        roll_size 25mb
                        roll_keep 20
                        roll_keep_for 720h
                }
        }
}

(e-gen) {
        encode gzip
        uri strip_suffix .html
        try_files {path} {path}.php {path}.html
        php_fastcgi unix//var/run/php/php8.1-fpm.sock {
        try_files {path} {path}.php index.php
        }
        file_server
        respond /seed/* "Gone" 410 {
        close
        }
        respond /nh/* "Gone" 410 {
        close
        }
        handle_errors {
                rewrite * /{http.error.status_code}
                reverse_proxy https://http.cat {
                        header_up Host http.cat
                }
        }
        log {
                format console
                output file /var/log/caddy.log {
                        roll_size 25mb
                        roll_keep 20
                        roll_keep_for 720h
                }
        }
}

This SEEMS to work, and I’m basing it off Caddy handling directives and matchers sequentially, as in - if it finds {path}.php it’ll serve that, otherwise it’ll serve {path}.html, and when it serves a php file it uses the try_files logic in the FastCGI handler. Is that right?
Also:

How many sequential arguments can try_files take? I thought it was only 2.

1 Like

It’s variadic - any number of arguments should be valid. See the syntax and particular the examples from the documentation, by way of demonstration: try_files (Caddyfile directive) — Caddy Documentation

1 Like

This doesn’t really make sense – only use try_files once, and best to use the one built into php_fastcgi since you need it anyways. So remove that first line there, and modify the one within php_fastcgi to do what you need.

1 Like

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