Docker - Multiple containers with SSL

For serving static sites, use the file_server directive (paired with the root directive to tell Caddy where to look):

You just need to mount those sites in different volumes in Caddy, and you can serve both with one container.

1 Like

Thanks, so when you said to only use one Caddy container, you did mean absolutely only one, I think I get it.

Spin up a Caddy docker container, and mount the folders on my host system, where I have my websites saved.
Then, inside the Caddy container make a Caddyfile.

The Caddyfile can work as a reverse proxy, and make links to other containers (node containers for example), but also work as a direct server of static content using “file_server” instead of “reverse_proxy”.
Caddyfile can also work as a php webserver by using “php_fastcgi” along with folder path.
All these examples are posted here: Common Caddyfile Patterns — Caddy Documentation

Please correct me if I’m wrong.

I’ll be using the Caddy container to serve a few php sites, so can you elaborate what is meant with “With a PHP FastCGI service running…”
Do I need to configure anything for this to work? Or just copy the code and place in my Caddyfile?

Well it depends. If you need to run other apps that aren’t just static sites, that need proxying to, then you’d run a container for each of those other apps and let Caddy proxy to them. But yeah for simple static sites, Caddy can do it on its own.

To run a PHP app, you’ll need to have a php-fpm container running which actually executes the PHP code. See the wiki for various examples:

Caddy doesn’t run the PHP code itself, all it does is act as a fastcgi client which is the protocol used to communicate with php-fpm. It tells php-fpm what request came in and passes along the relevant details to have it run correctly.

1 Like

First of all, thank you so much for your help so far!
With your help, I managed to solve my initial question, however I’m still stuck at getting the php side of things up and running.

I created my Caddyfile, and it looks like this:

 sub1.domain.com {
         root * /var/www/sub1.domain.com
         file_server
 }
 sub2.domain.com {
         root * /var/www/sub2.domain.com
         file_server
 }
 php.domain.com {
         root * /var/www/php.domain.com
         php_fastcgi * phpfpm:9000
}

I run the caddy container with this command:

docker run -d -p 80:80 -p 443:443  --name caddy \
    -v $PWD/Caddyfile:/etc/caddy/Caddyfile \
    -v /var/www:/var/www \
    -v caddy_data:/data \
    -v /srv/caddy_config:/config \
    caddy

Both sub1.domain.com and sub2.domain.com works perfectly as static sites, with a nice https connection.
However, my php subdomain is still not playing par…

I installed this container from docker hub: Docker
Ran it using:

docker run --name phpfpm bitnami/php-fpm

I assume that’s what you mean when you said this, right?

The installation on their docker page says to pass a volume with the app into the container, but this got me confused. I’m supposed to just have the php-fpm do it’s own thing, and pass any directories to Caddy right? Or no?
I tried passing the php volume into the container like this:

docker run -it --name phpfpm -v /var/www/php.domain.com:/app bitnami/php-fpm

This still did not work…

I had to go with this “bitname” fork, because the official php container (https://hub.docker.com/_/php) had me even more confused.
They do list something with “fpm” in their supported tags, but I’m still not sure. Furthermore they also list passing a volume with the “app” to the container.
Again, I thought we’re only supposed to pass the directory to Caddy?
Because then I can run multiple sites, with different folders, off of the same php-fpm container. Or do I need a new php-fpm container for each project?

I do realize these are a lot of questions, but I’m really new to this, and I hope you wont mind helping me on my way.
Thank you in advance.

If you’re running more than one container on a single machine, you’ll have a much better time using docker-compose. It’s basically a way to turn your docker commands into a configuration file. You can use the example at the bottom of Docker Hub as a starting point. See the official docs for that:

The gist is that you’ll just make a docker-compose.yml then run docker-compose up -d and your whole stack will be spun up. Then you can do docker-compose down to shut everything down all at once. And there’s a bunch of other helpful tools there. Google is your friend.

You’ll definitely want to use the Docker Hub image, specifically the fpm variant. Depends what PHP version you want to target, but you’ll probably use like php:7.4-fpm-alpine.

The important thing is that you use the same directory path inside the container for both, because Caddy will tell the php-fpm container which file path to look for the PHP script to execute. You can mount /var/www in your php-fpm container as well (or just /var/www/php.domain.com, whatever)

Also, you’ll need a file_server directive for your PHP site as well, because Caddy will likely still need to serve static files (JS, CSS, images, etc) as well.

It might be helpful to look for nginx+php docker examples, since the same concepts apply for Caddy, just with all the nginx-specific bits swapped out with Caddy. There’s just more content out there about nginx since it’s older and more popular (even though I think Caddy is much better and easier to use!)

1 Like

Francis… I’m very delighted to not only wish you a Merry Christmas, but also to give my deepest appreciation for your help and consistent efforts to get a newcomer, like me, up and running in the best possible way.

Truth to be told, I’ve been avoiding docker-compose, because I found the yaml files frightening, scary, and not very welcoming for absolute beginners.
This was the same reason why I didn’t get “hands-on” with the Caddyfile initially, and only looked into it after you persuaded me.
Caddyfile wasn’t scary after your example and a link to the documentation, and would you believe, docker-compose was the exact same story - not at all frightening after a few good examples and your comforting “push” in that direction.

Thanks to you, I created my first Caddyfile, and thanks to you I now created my first docker-compose file too.
And you know what? It was exactly what I needed!

I now have all my subdomains running static pages, as well as the php-subdomain running a fastcgi link to my php-fpm docker container.
It all works, and I can make changes to the setup any time, and just do a “docker-compose up -d” and not have to kill/remove all the previous containers!

If it haven’t become clear already, I’m extremely thankful for your help, and the way you help is very good, because it forces people to go out and seek information for themselves, in order to actually learn what they’re doing, instead of you just giving code and ending in a “monkey see, monkey do” kind of situation.

So yeah, thank you for everything, I’ll leave you with these final words:

I couldn’t agree more.
After looking at various nginx examples, which accomplishes the same as I just did, I’m amazed at how efficient Caddy does the work.
So much cleaner, so much less code, and jam-packed with awesome features. Getting an SSL cert shouldn’t be such a hassle, and it’s not with Caddy!

I’m glad I got onboard of the Caddy train, and I can’t wait to see where it goes from here. :caddy: :heart:

4 Likes

:grin:

Thanks you for the kind words, Merry Christmas to you too! :christmas_tree:

1 Like

Hello again Francis,

I might have been a bit too quick on my feet with the last message, as it seems there’s still one last issue, before my php webserver is running correctly.

While the site does indeed work for displaying a php-page, when I copied over my entire site, I noticed that it would serve all requests to the /index.php page!
Here’s what I’m working with:

docker-compose:

version: "3"

services:
  phpfpm:
    image: php:7.4-fpm-alpine
    container_name: phpfpm
    volumes:
      - /var/www:/var/www
    ports:
      - 9000:9000

  caddy:
    image: caddy:latest
    container_name: caddy
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/www:/var/www:ro
      - /srv/caddy_data:/data
      - /srv/caddy_config:/config
      - /home/fox/Caddyfile:/etc/caddy/Caddyfile

Caddyfile:

# Main Site
sub.website.com {
        root * /var/www/sub.website.com
        php_fastcgi phpfpm:9000
        file_server
}

Like I said, the site loads the frontpage, but from the console I can see multiple requests like this:

phpfpm     | 172.24.0.5 -  25/Dec/2020:20:55:12 +0000 "GET /index.php" 200
phpfpm     | 172.24.0.5 -  25/Dec/2020:20:55:12 +0000 "GET /index.php" 200
phpfpm     | 172.24.0.5 -  25/Dec/2020:20:55:13 +0000 "GET /index.php" 200
phpfpm     | 172.24.0.5 -  25/Dec/2020:20:55:13 +0000 "GET /index.php" 200

When I try to go to another page, on the php site, it will print the same stuff in the console.
So it’s always sending a request for the index.php page, no matter what GET request it should actually send.

I assume the issue lies within my Caddyfile.
Specifically in either the root or php_fastcgi directive.
My main suspicion is on the wildcard matcher, but I’m not sure what other matcher I should use…
All examples in the documentation, regarding php directive, utilizes the wildcard matcher, and while it was fine for displaying a simple “phpinfo() page”, it does not seem like this is the way to go for real sites, with different paths folders and links.

Please help me shed some light on it.
As always I appreciate your help a lot.

Yeah, using index.php as the fallback for all requests to unknown files is very typical for modern PHP apps, so that’s the default behaviour of the expanded form of php_fastcgi. You can see this in the expanded form, where it uses try_files to do a redirect if the request path does not map to a file on disk.

Probably not the best resource (I wouldn’t recommend using this code directly) on short notice, but here’s an explanation of how an index router typically works

So if that doesn’t match your expectations, how is your PHP app structured? Are you using a framework? What request paths do you expect to do what?

Awesome info, definitely helped me shed some light on things!

I am indeed using a php framework, and upon further investigation it is structured on index routing.
The framework is originally made for Apache though, but using the tip you gave me earlier (didn’t forget), I went ahead and looked for nginx examples.

Turns out you do in fact need some extra configuration for running this framework.
Here’s the configuration needed.

location / {
  try_files $uri $uri/ =404;

   if (!-e $request_filename)
    { 
        rewrite ^/admin/(.*)?$ /admin/index.php?a=$1 last;
        rewrite ^/(.*)$ /index.php?a=$1 last;
        break; 
    }
}

Unfortunately I’m having a very hard time converting this to Caddyfile markup.
Only having learnt the language a few days ago, and now having to also learn nginx markup to convert, seems like a big chunk to chew… Hoping you can help me out a bit.

Ah interesting, it uses a nonstandard approach for the index.php in that it takes a ?a= param for the request path. Frameworks like Laravel and others just take the request path as a direct suffix to index.php, like /index.php/foo for a request to /foo.

I think you can add this to your Caddyfile to do the rewrites properly:

handle /admin/* {
    rewrite /admin/index.php?a={path}&{query}
}
handle {
    try_files {path} {path}/ /index.php?a={path}&{query}
}

Just put this before your php_fastcgi. I think it should work but we’ll see.

Thanks.

I added the configuration like you said, but unfortunately it did not resolve the issue.
From the console I see the first request returning a http 200, and from there on it’s giving 302 for the next couple of requests (loading assets I presume).

Hmm, well I recommend you make the requests with curl -v to get full detail of what the response looks like.

Also you can enable the debug global option to get more details about the rewrites happening in your logs. You can find the logs with docker-compose logs caddy

I’m afraid I won’t be able to help more without more detail about what’s going on.

Thanks, I enabled the debugging for the domain.
I’m trying to access the URI “/servers”.

phpfpm    | 172.29.0.2 -  27/Dec/2020:15:29:31 +0000 "GET /index.php" 302
caddy     | {"level":"info","ts":1609082971.3080006,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_addr":"162.158.154.108:46350","proto":"HTTP/1.1","method":"GET","host":"php.domain.com","uri":"/servers","headers":{"Accept-Language":["en-GB,en;q=0.5"],"Upgrade-Insecure-Requests":["1"],"Cf-Request-Id":["07466abc7a0000408f7ead2000000001"],"Cdn-Loop":["cloudflare"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0"],"Cf-Ipcountry":["GB"],"Cf-Ray":["608413da5c0d408f-LHR"],"X-Forwarded-Proto":["https"],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"],"Referer":["https://php.domain.com/"],"Accept-Encoding":["gzip"],"X-Forwarded-For":["x.x.x.x"],"Cookie":["_ga=GA1.2.678239338.1604131132; __cfduid=debfee6079a0d559f4c94455c399f01921608738744; cookieconsent_status=dismiss; PHPSESSID=7f388b4af221c6a4b4bffcf910602f61"],"Cf-Visitor":["{\"scheme\":\"https\"}"],"Cf-Connecting-Ip":["x.x.x.x"],"Connection":["Keep-Alive"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"","proto_mutual":true,"server_name":"php.domain.com"}},"common_log":"162.158.154.108 - - [27/Dec/2020:15:29:31 +0000] \"GET /servers HTTP/1.1\" 302 20","duration":0.020263877,"size":20,"status":302,"resp_headers":{"Server":["Caddy"],"Status":["302 Found"],"Expires":["Thu, 19 Nov 1981 08:52:00 GMT"],"Location":["https://php.domain.com"],"X-Powered-By":["PHP/7.4.13"],"Vary":["Accept-Encoding"],"Pragma":["no-cache"],"Content-Encoding":["gzip"],"Cache-Control":["no-store, no-cache, must-revalidate"],"Content-Type":["text/html; charset=UTF-8"]}}

After this request, it immediately sends another for the root with no URI, and returns the correct http response 200.

phpfpm    | 172.29.0.3 -  27/Dec/2020:15:41:03 +0000 "GET /index.php" 200
caddy     | {"level":"info","ts":1609083663.297698,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_addr":"162.158.158.146:35758","proto":"HTTP/1.1","method":"GET","host":"php.domain.com","uri":"/","headers":{"Cf-Visitor":["{\"scheme\":\"https\"}"],"Cf-Connecting-Ip":["x.x.x.x"],"Cdn-Loop":["cloudflare"],"Cookie":["_ga=GA1.2.678239338.1604131132; __cfduid=debfee6079a0d559f4c94455c399f01921608738744; cookieconsent_status=dismiss; PHPSESSID=7f388b4af221c6a4b4bffcf910602f61"],"Accept-Encoding":["gzip"],"Cf-Ipcountry":["GB"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0"],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"],"Referer":["https://php.domain.com/"],"X-Forwarded-For":["x.x.x.x"],"Upgrade-Insecure-Requests":["1"],"Connection":["Keep-Alive"],"Cf-Ray":["608424bf0b660672-LHR"],"X-Forwarded-Proto":["https"],"Accept-Language":["en-GB,en;q=0.5"],"Cf-Request-Id":["0746754b6a00000672223f4000000001"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"","proto_mutual":true,"server_name":"php.domain.com"}},"common_log":"162.158.158.146 - - [27/Dec/2020:15:41:03 +0000] \"GET / HTTP/1.1\" 200 4835","duration":0.016583431,"size":4835,"status":200,"resp_headers":{"Content-Type":["text/html; charset=UTF-8"],"X-Powered-By":["PHP/7.4.13"],"Expires":["Thu, 19 Nov 1981 08:52:00 GMT"],"Cache-Control":["no-store, no-cache, must-revalidate"],"Pragma":["no-cache"],"Content-Encoding":["gzip"],"Vary":["Accept-Encoding"],"Server":["Caddy"]}}

Here’s the requested curl reponse with headers:

For the domain, no URI:

*   Trying 2606:4700:3031::ac43:83e8:443...
* TCP_NODELAY set
* Connected to php.domain.com (2606:4700:3031::ac43:83e8) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=US; ST=CA; L=San Francisco; O=Cloudflare, Inc.; CN=sni.cloudflaressl.com
*  start date: Aug 18 00:00:00 2020 GMT
*  expire date: Aug 18 12:00:00 2021 GMT
*  subjectAltName: host "php.domain.com" matched cert's "*.domain.com"
*  issuer: C=US; O=Cloudflare, Inc.; CN=Cloudflare Inc ECC CA-3
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x560edb507df0)
> GET / HTTP/2
> Host: php.domain.com
> user-agent: curl/7.68.0
> accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
* Connection state changed (MAX_CONCURRENT_STREAMS == 256)!
< HTTP/2 200
< date: Sun, 27 Dec 2020 15:47:27 GMT
< content-type: text/html; charset=UTF-8
< set-cookie: __cfduid=d78bb1eb12a7090d24d4dc3b77bcae9141609084047; expires=Tue, 26-Jan-21 15:47:27 GMT; path=/; domain=.domain.com; HttpOnly; SameSite=Lax
< cache-control: no-store, no-cache, must-revalidate
< expires: Thu, 19 Nov 1981 08:52:00 GMT
< pragma: no-cache
< set-cookie: PHPSESSID=fe25b71ab098f4ccd2182c5ba050572a; path=/
< vary: Accept-Encoding
< x-powered-by: PHP/7.4.13
< cf-cache-status: DYNAMIC
< cf-request-id: 07467b279a0000bf23a836b000000001
< expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
< report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report?s=k0r5CFqKpRKOGKcZQqNz5HFIrSGSEKex46doRjWNfPWAlInIUCrQcGbjvb7dJwaFn8dA3hFJ9BGC9MnbJlcwsbTtLSdja8fsIJPiGt7hL8JATgk%2F1KAfRk3z4Q%3D%3D"}],"group":"cf-nel","max_age":604800}
< nel: {"report_to":"cf-nel","max_age":604800}
< server: cloudflare
< cf-ray: 60842e1f5db8bf23-FRA
<

For the domain with /servers URI:

*   Trying 2606:4700:3035::681c:23f:443...
* TCP_NODELAY set
* Connected to php.domain.com(2606:4700:3035::681c:23f) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=US; ST=CA; L=San Francisco; O=Cloudflare, Inc.; CN=sni.cloudflaressl.com
*  start date: Aug 18 00:00:00 2020 GMT
*  expire date: Aug 18 12:00:00 2021 GMT
*  subjectAltName: host "php.domain.com" matched cert's "*.domain.com"
*  issuer: C=US; O=Cloudflare, Inc.; CN=Cloudflare Inc ECC CA-3
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x56003a962df0)
> GET /servers HTTP/2
> Host: php.domain.com
> user-agent: curl/7.68.0
> accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
* Connection state changed (MAX_CONCURRENT_STREAMS == 256)!
< HTTP/2 302
< date: Sun, 27 Dec 2020 15:51:28 GMT
< content-type: text/html; charset=UTF-8
< set-cookie: __cfduid=ddefb6922a55ec5c171b010bdcd1e77311609084288; expires=Tue, 26-Jan-21 15:51:28 GMT; path=/; domain=.domain.com; HttpOnly; SameSite=Lax
< cache-control: no-store, no-cache, must-revalidate
< expires: Thu, 19 Nov 1981 08:52:00 GMT
< location: https://php.domain.com
< pragma: no-cache
< set-cookie: PHPSESSID=4e9d41e2609d93ae66977c24ee98e2e6; path=/
< status: 302 Found
< vary: Accept-Encoding
< x-powered-by: PHP/7.4.13
< cf-cache-status: DYNAMIC
< cf-request-id: 07467ed75c00000601ab8d2000000001
< expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
< report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report?s=UlUig6A%2Fbrb4Qx%2Fg6h%2BTpLfp0DoTBeeCiGi1un7orTXfc%2BEUy2Gwp6ONc2KrgWke2Lv8ofK8JJJkO9qysLYMFbpowAuTj5g11Qv%2FMOVPcTbPUReT55UD%2FQI5Yg%3D%3D"}],"group":"cf-nel","max_age":604800}
< nel: {"report_to":"cf-nel","max_age":604800}
< server: cloudflare
< cf-ray: 608434055b340601-FRA
<
* Connection #0 to host php.domain.com left intact

I’m afraid I’m in way over my confidence level.
Because of this, I might not be able to conclude anything from these files myself…

I have another copy of the site running on an apache server, so if you need any logs from there, to see an expected behaviour, I can fetch that as well.

As always, thanks for your help.
I’ll be sure to forward any information learned here, to the creator of the framework, and pursuade them to add it to their documentation, so hopefully others can enjoy the site on Caddy as well.

Which framework is it that you’re using? I could take a closer look at its documentation to see what I can figure out.

Premium URL Shortener.

They are very sparse on their documentation for installation though.

For your curl request, did you specify https:// or did you omit that? The 302 redirect there is to send you from HTTP to HTTPS.

Try by either specifying https:// or using curl -vL to follow redirects (L for Location, the redirect header name)

As for the config, I noticed a mistake I make, I think your app is expecting the path for admin to not include /admin in it. To solve that, just use handle_path instead of handle for that one. The difference is handle_path will strip the given path prefix before handing your rewrite, which should do what you expect, I think.

You’re right, that was a silly mistake.

After including the https, requests are no longer being duplicated from a 302 to a 200.
When running curl -v https://sub.domain.com I get a 200 success.
However, I’m afraid it gives the same response as posted earlier. It simply redirects to the default frontpage.

phpfpm        | 172.31.0.4 -  28/Dec/2020:18:49:35 +0000 "GET /index.php" 302
caddy         | {"level":"info","ts":1609181375.0495365,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_addr":"162.158.93.194:53918","proto":"HTTP/1.1","method":"GET","host":"php.domain.com","uri":"/servers","headers":{"Cf-Visitor":["{\"scheme\":\"https\"}"],"User-Agent":["curl/7.68.0"],"Connection":["Keep-Alive"],"Accept-Encoding":["gzip"],"Cf-Ipcountry":["DE"],"X-Forwarded-For":["2a01:4f8:221:2999::2"],"X-Forwarded-Proto":["https"],"Cf-Request-Id":["074c4841f70000c26d0e17b000000001"],"Cdn-Loop":["cloudflare"],"Cf-Ray":["608d76498da2c26d-FRA"],"Accept":["*/*"],"Cf-Connecting-Ip":["2a01:4f8:221:2999::2"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"","proto_mutual":true,"server_name":"php.domain.com"}},"common_log":"162.158.93.194 - - [28/Dec/2020:18:49:35 +0000] \"GET /servers HTTP/1.1\" 302 20","duration":0.022119267,"size":20,"status":302,"resp_headers":{"Pragma":["no-cache"],"Location":["https://php.domain.com"],"Content-Type":["text/html; charset=UTF-8"],"Set-Cookie":["PHPSESSID=0db27f910de917af44e0822210f27ee3; path=/"],"Expires":["Thu, 19 Nov 1981 08:52:00 GMT"],"Server":["Caddy"],"Status":["302 Found"],"Cache-Control":["no-store, no-cache, must-revalidate"],"Vary":["Accept-Encoding"],"X-Powered-By":["PHP/7.4.13"],"Content-Encoding":["gzip"]}}

Now, we’ve been at this for quite a while.
My initial question, in the start of the topic, has long since been answered, and I feel like this topic is being dragged out because of my framework, which is still not relevant to Caddy.

I appreciate your help a lot - it really means a lot to me.
But I also realize that you’re probably a busy person, and can’t dedicate so much of your time to an issue like this.

Therefore I’m hoping we can put this to rest soon, one way or another.
Do you want to continue helping me, the way we’ve been doing so far, back and forth?
Would giving you access to the source code help?
I can even pass on some SSH credentials, if you like to see the configuration for yourself - there’s nothing else running on the server.

If you don’t mind continuing, I’ll of course be happy, but I’m also in complete understandings if you’re not interested.
Like I said, my original question has already been answered, so it feels like we’re just dragging this out, which I’m not interested in doing to a knowledgeable person like you - I’m sure you could spend your time on way more important things, and I don’t want to hold you up.

Please let me know, and thanks for your help so far.

I don’t really understand what the problem is at this point honestly. That seems fine to me? What is it supposed to be doing otherwise? I don’t get it.

Possibly the simplest way to just get it working is to run that app with apache+php in a Docker image (i.e. php:7.4-apache) and use Caddy’s reverse_proxy directive instead of php_fastcgi. Basically Caddy would just terminate TLS then proxy the request to apache to deal with any custom behaviour of your app. Might not perform quite as well, but if this isn’t very high traffic, it’ll be just fine.

Scroll down to the bit about php:<version>-apache for instructions Docker

And if you do it that way, Caddy won’t need the volume mounted for that site cause all it’ll do is proxy every request basically as-is.

:slight_smile:

I’m glad to put a smile on people’s faces. Thanks again for the well-wishes, it put a smile on my face on Christmas eve :smiley: :christmas_tree:

It’s just a few minutes here and there so it’s not much of a bother.

Yep, that’s no problem.

Hello again, and happy new years.

I’ve actually thought about this solution too, and it would obviously work, but as my site does have a pretty high hit-rate, I’m not comfortable doing it this way.
I’d rather do it correctly this time around.

I’ve been going a bit back and forward with the issue though, and in the end I decided to try and install nginx, just to see if it was the site/server/database/anything else that was causing the issue.
After installing nginx, the same error occurred, where all URI’s redirect to 404, but then I added the little code (posted earlier) and suddenly everything was working flawlessly.

This leads me to believe that the root of the issue is with the Caddyfile, and the rewrites going on there.
Is there a chance you could take a look at this again, and make sure it’s correctly “translated”?
I think we might be able to put this to rest if this file is checked.

Here’s the site configuration where the rewrites are working:

server {
        index index.php;
        listen  443 ssl;
        server_tokens off;

        server_name php.domain.com;
        root /var/www/php.domain.com;

        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_pass phpfpm:9000;
                fastcgi_index index.php;
                include fastcgi_params;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_param PATH_INFO $fastcgi_path_info;
        }

        location / {
                try_files $uri $uri/ =404;

                if (!-e $request_filename) {
                        rewrite ^/admin/(.*)?$ /admin/index.php?a=$1 last;
                        rewrite ^/(.*)$ /index.php?a=$1 last;
                        break;
                }
        }

}

As always, thank you in advance.