caddy: PATH_INFO is empty with basic fastcgi Caddyfile
Hey everyone, I’m trying to move from Caddy 1 to Caddy 2 and was in the process of installing a Moodle server (PHP) when I encountered the following issue:
moodle.domain {
root * /datapool/www/moodle_base/moodle
php_fastcgi 127.0.0.1:9000 {
env SERVER_SOFTWARE Apache
}
}
When doing a GET request with path https://moodle.domain/lib/javascript.php/1599824490/lib/requirejs/require.min.js the PATH_INFO Variable (should be /1599824490/lib/requirejs/require.min.js) is not set correctly:
{"level":"debug","ts":1599828392.7683938,"logger":"http.reverse_proxy.transport.fastcgi","msg":"roundtrip","request":{"remote_addr":"12.34.56.78:1234","proto":"HTTP/2.0","method":"GET","host":"moodle.domain","uri":"/lib/javascript.php","headers":{"Dnt":["1"],"Upgrade-Insecure-Requests":["1"],"User-Agent":["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36"],"Sec-Fetch-Dest":["document"],"Cookie":["MoodleSession=sfenfg19jlpj4mt1gm9um41jtn"],"Cache-Control":["max-age=0"],"Sec-Fetch-User":["?1"],"X-Forwarded-Proto":["https"],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"],"Accept-Language":["de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7"],"X-Forwarded-For":["12.34.56.78"],"Sec-Fetch-Site":["none"],"Accept-Encoding":["gzip, deflate, br"],"Sec-Fetch-Mode":["navigate"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","proto_mutual":true,"server_name":"moodle.domain"}},"dial":"127.0.0.1:9000","env":{"AUTH_TYPE":"","CONTENT_LENGTH":"","CONTENT_TYPE":"","DOCUMENT_ROOT":"/datapool/www/moodle_base/moodle","DOCUMENT_URI":"/lib/javascript.php","GATEWAY_INTERFACE":"CGI/1.1","HTTPS":"on","HTTP_ACCEPT":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9","HTTP_ACCEPT_ENCODING":"gzip, deflate, br","HTTP_ACCEPT_LANGUAGE":"de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7","HTTP_CACHE_CONTROL":"max-age=0","HTTP_COOKIE":"MoodleSession=sfenfg19jlpj4mt1gm9um41jtn","HTTP_DNT":"1","HTTP_HOST":"moodle.domain","HTTP_SEC_FETCH_DEST":"document","HTTP_SEC_FETCH_MODE":"navigate","HTTP_SEC_FETCH_SITE":"none","HTTP_SEC_FETCH_USER":"?1","HTTP_UPGRADE_INSECURE_REQUESTS":"1","HTTP_USER_AGENT":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36","HTTP_X_FORWARDED_FOR":"12.34.56.78","HTTP_X_FORWARDED_PROTO":"1234","PATH_INFO":"","QUERY_STRING":"","REMOTE_ADDR":"12.34.56.78","REMOTE_HOST":"12.34.56.78","REMOTE_IDENT":"","REMOTE_PORT":"1234","REMOTE_USER":"","REQUEST_METHOD":"GET","REQUEST_SCHEME":"https","REQUEST_URI":"/lib/javascript.php/1599824490/lib/requirejs/require.min.js","SCRIPT_FILENAME":"/datapool/www/moodle_base/moodle/lib/javascript.php","SCRIPT_NAME":"/lib/javascript.php","SERVER_NAME":"moodle.domain","SERVER_PROTOCOL":"HTTP/2.0","SERVER_SOFTWARE":"Apache","SSL_CIPHER":"TLS_AES_128_GCM_SHA256","SSL_PROTOCOL":"TLSv1.3"}}
While debugging I saw that the URL.Path is already just /lib/javascript.php here and the rest is missing:
https://github.com/caddyserver/caddy/blob/master/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go#L197
I temporarily fixed this by using the origRequest, but this causes other issues as the (correct) rewrite of / to /index.php fails now.
origReq, ok := r.Context().Value(caddyhttp.OriginalRequestCtxKey).(http.Request)
if !ok {
// some requests, like active health checks, don't add this to
// the request context, so we can just use the current URL
origReq = *r
}
fpath := origReq.URL.Path
I guess there is something going on with the default rewrites as there already is a split_path option there.
(caddy versions affected: 2.1.1, 2.2.0-rc.1, master)
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 30 (13 by maintainers)
I can confirm that using the binary from 3e577ef works with my Moodle setup using the default
php_fastcgidirective 😃Excellent!
It’s slightly too bad about the potential edge case(s) the fix introduces, but probably a worthwhile trade-off, especially if the edge cases involves the user configuring something weird, right?
Looking forward to getting it in as one of the first commits for the 2.3 tree, after 2.2 is released, hopefully this week.
Edit: Heh, it was the last commit for the 2.3 tree.
Hi @francislavoie, I’ve update the “test suite” by using
index.phpas fallback resource in each web server instance: the only difference now in Caddy is onPATH_INFObeing always defined but empty just for both/fooand/foo.php/foowhich looks like not an issue in PHP: those usingempty()will be fine and those usingisset()will have an empty value.@SteffenDE, would you mind to give
caddy_Linux_go1.14_3e577ef(from https://github.com/caddyserver/caddy/actions/runs/263950314) a try in your setup w/o changing anything of your configuration but thecaddybinary? TIA!HTH, Matteo
Hi @francislavoie, great news!
I did some tests and I can confirm it: I’m preparing a rough test suite and will share via my GH account.
HTH, Matteo
Hi @SteffenDE; thanks for your quick try&reply: that was expected, just a small step ahead.
Next step: create a docker compose with caddy, apache and nginx serving the same sample,
info.phpandindex.php(<?php phpinfo(INFO_VARIABLES);), via the same PHP-FPM engine instance, to look for any difference among the different vendors to help @francislavoie (and @mholt).HTH, Matteo
I greatly appreciate that someone is looking into this, as I sadly don’t have the time to investigate this myself. For the meantime, I’m running Caddy v1 and proxy the Moodle requests from Caddy 2 to the old Caddy instance 😅
In the past, I’ve been running an extra Apache just for Moodle, but after discovering that setting the environment variable to Apache saves me from doing this, Moodle was running perfectly for the last two (or so) years on Caddy. Great work and a huge thank you to both the Moodle and Caddy team!
Okay, I see what’s going on. This is pretty tricky.
So, I’m comparing with the code from Caddy v1 which apparently did work correctly: https://github.com/caddyserver/caddy/blob/v1/caddyhttp/fastcgi/fastcgi.go
The actual fastcgi code is pretty much the same in v2, but the difference comes in how the request prep happens in v2 compared to v1.
In v1, this wasn’t a problem because the
index.phpfallback wasn’t handled via a rewrite internally beforehand, it was just directly handled inServeHTTPwithout updating the requested path.In v2, instead it works by delegating the decision for
index.phpto the built-intry_filesbehaviour of thephp_fastcgidirective. See the expanded form to see the stuff it’s doing: https://caddyserver.com/docs/caddyfile/directives/php_fastcgi#expanded-formAs you said, we could use the original URL to solve this case with
.phpbeing found in the URL, and this does work (tried moving theorigReqblock of code up and using that instead offpathwhen splitting), but I’m pretty worried about edgecases.I tried doing some research for documentation to figure out how
PATH_INFOis meant to be handled depending on the original URL from the browser. E.g. we know that/index.php/fooshould havePATH_INFOset to/foo, but should the request to/fooalso havePATH_INFOset to/foo, since the server rewrites it to be handled byindex.php? I’m leaning towards no, but it’s unclear, and the RFC https://tools.ietf.org/html/rfc3875#section-4.1.5 is not specific about that.Also, with that change, it would also make requests like
/foo.php/foothat get rewritten toindex.phpalso have/fooinPATH_INFOeven though/index.phpis what actually gets run, not/foo.php. I think we might need some extra logic to only split if the index file is found in the original path? 🤔I would have to spin up an environment with nginx and/or apache with mod_cgi to verify how they behave. My env is kinda in a state that would make that tricky right now though, so I haven’t done that yet. I wouldn’t mind help there if you’re capable of testing that our quickly.
There’s also security implications with any change we make here, so I’m extra nervous about it.
Hmm, it’s funky that they use PHP to serve JS, but okay 😛
I’ll need to take a closer look at all this, I’ll come back to this one this weekend. Thanks for the report!