How can I rewrite requests to a subfolder to a dedicated index.php

1. The problem I’m having:

Hello.

I tried to solve my issue for hours without success and I searched the web a lot not finding any working solution for me.

I use devenv for development and devenv uses Caddy as a webserver.
My project has the following folder structure:

my-app/
├─ admin/
│  ├─ index.php
├─ index.php

I need all frontend requests that are no static resources like CSS, JS or images to be rewritten to index.php
Example: https://localhost:8000/account/orders hits my-app/index.php

All requests to the administration should be rewritten to my-app/admin/index.php
Example: https://localhost:8000/admin/configuration hits my-app/admin/index.php

I tried a couple of configurations and while requests to http://localhost:8000/admin do hit admin/index.php requests like http://localhost:8000/admin/configuration hit /index.php and cause a 404 error in the application frontend router.

2. Error messages and/or full log output:

currently, the setup does not log anything. I will add logging if you show me how.

3. Caddy version:

2.7.6

4. How I installed and ran Caddy:

a. System environment:

Ubuntu 24.04 with Nix and devenv

b. Command:

#!/nix/store/a1s263pmsci9zykm5xcdf7x9rv26w6d5-bash-5.2p26/bin/bash
XDG_DATA_HOME=/media/juergen/ssd-ext/devenv/my-app/.devenv/state/caddy/data XDG_CONFIG_HOME=/media/juergen/ssd-ext/devenv/my-app/.devenv/state/caddy/config /nix/store/7ad29pl6dsmpjkphkv4q7n3pp63g3v7w-caddy-2.7.6/bin/caddy run  --config /nix/store/p5jk90mkvbm3ykh7f7gai7l6wnlxmkcm-caddy-config.json

c. Service/unit/compose file:

sorry devenv starts caddy for me, not sure what info you need here.

d. My complete Caddy config:

my config section of caddy in my devenv.nix

:8000 {
	encode zstd gzip
	root * shop
	file_server

	handle /admin/* {
		php_fastcgi unix//media/juergen/ssd-ext/devenv/my-app/.devenv/state/php-fpm/web.sock {
			trusted_proxies private_ranges
		}
	}

	@default {
		not path /admin/* /theme/* /media/* /thumbnail/* /bundles/* /css/* /fonts/* /js/* /sitemap/*
	}

	php_fastcgi @default unix//media/juergen/ssd-ext/devenv/my-app/.devenv/state/php-fpm/web.sock {
		trusted_proxies private_ranges
	}
}

devenv generated config file:

{
  "apps": {
    "http": {
      "servers": {
        "srv0": {
          "listen": [
            ":8000"
          ],
          "routes": [
            {
              "handle": [
                {
                  "handler": "vars",
                  "root": "shop"
                },
                {
                  "encodings": {
                    "gzip": {},
                    "zstd": {}
                  },
                  "handler": "encode",
                  "prefer": [
                    "zstd",
                    "gzip"
                  ]
                }
              ]
            },
            {
              "match": [
                {
                  "path": [
                    "/admin/*"
                  ]
                }
              ],
              "handle": [
                {
                  "handler": "subroute",
                  "routes": [
                    {
                      "handle": [
                        {
                          "handler": "static_response",
                          "headers": {
                            "Location": [
                              "{http.request.orig_uri.path}/"
                            ]
                          },
                          "status_code": 308
                        }
                      ],
                      "match": [
                        {
                          "file": {
                            "try_files": [
                              "{http.request.uri.path}/index.php"
                            ]
                          },
                          "not": [
                            {
                              "path": [
                                "*/"
                              ]
                            }
                          ]
                        }
                      ]
                    },
                    {
                      "handle": [
                        {
                          "handler": "rewrite",
                          "uri": "{http.matchers.file.relative}"
                        }
                      ],
                      "match": [
                        {
                          "file": {
                            "split_path": [
                              ".php"
                            ],
                            "try_files": [
                              "{http.request.uri.path}",
                              "{http.request.uri.path}/index.php",
                              "index.php"
                            ]
                          }
                        }
                      ]
                    },
                    {
                      "handle": [
                        {
                          "handler": "reverse_proxy",
                          "transport": {
                            "protocol": "fastcgi",
                            "split_path": [
                              ".php"
                            ]
                          },
                          "trusted_proxies": [
                            "192.168.0.0/16",
                            "172.16.0.0/12",
                            "10.0.0.0/8",
                            "127.0.0.1/8",
                            "fd00::/8",
                            "::1"
                          ],
                          "upstreams": [
                            {
                              "dial": "unix//media/juergen/ssd-ext/devenv/my-app/.devenv/state/php-fpm/web.sock"
                            }
                          ]
                        }
                      ],
                      "match": [
                        {
                          "path": [
                            "*.php"
                          ]
                        }
                      ]
                    }
                  ]
                }
              ]
            },
            {
              "match": [
                {
                  "not": [
                    {
                      "path": [
                        "/admin/*",
                        "/theme/*",
                        "/media/*",
                        "/thumbnail/*",
                        "/bundles/*",
                        "/css/*",
                        "/fonts/*",
                        "/js/*",
                        "/sitemap/*"
                      ]
                    }
                  ]
                }
              ],
              "handle": [
                {
                  "handler": "subroute",
                  "routes": [
                    {
                      "handle": [
                        {
                          "handler": "static_response",
                          "headers": {
                            "Location": [
                              "{http.request.orig_uri.path}/"
                            ]
                          },
                          "status_code": 308
                        }
                      ],
                      "match": [
                        {
                          "file": {
                            "try_files": [
                              "{http.request.uri.path}/index.php"
                            ]
                          },
                          "not": [
                            {
                              "path": [
                                "*/"
                              ]
                            }
                          ]
                        }
                      ]
                    },
                    {
                      "handle": [
                        {
                          "handler": "rewrite",
                          "uri": "{http.matchers.file.relative}"
                        }
                      ],
                      "match": [
                        {
                          "file": {
                            "split_path": [
                              ".php"
                            ],
                            "try_files": [
                              "{http.request.uri.path}",
                              "{http.request.uri.path}/index.php",
                              "index.php"
                            ]
                          }
                        }
                      ]
                    },
                    {
                      "handle": [
                        {
                          "handler": "reverse_proxy",
                          "transport": {
                            "protocol": "fastcgi",
                            "split_path": [
                              ".php"
                            ]
                          },
                          "trusted_proxies": [
                            "192.168.0.0/16",
                            "172.16.0.0/12",
                            "10.0.0.0/8",
                            "127.0.0.1/8",
                            "fd00::/8",
                            "::1"
                          ],
                          "upstreams": [
                            {
                              "dial": "unix//media/juergen/ssd-ext/devenv/my-app/.devenv/state/php-fpm/web.sock"
                            }
                          ]
                        }
                      ],
                      "match": [
                        {
                          "path": [
                            "*.php"
                          ]
                        }
                      ]
                    }
                  ]
                }
              ]
            },
            {
              "handle": [
                {
                  "handler": "file_server",
                  "hide": [
                    "/nix/store/c40lz32kx8v9wnf75r0wyl6hpcxjpkjv-formattedCaddyFile"
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    "tls": {
      "automation": {
        "policies": [
          {
            "issuers": [
              {
                "ca": "https://acme-v02.api.letsencrypt.org/directory",
                "email": "",
                "module": "acme"
              }
            ]
          }
        ]
      }
    }
  }
}

5. Links to relevant resources:

You should configure trusted_proxies in global options, not inside of php_fastcgi anymore. It has some major advantages, like having the client_ip in your access logs, and not having to repeat the config more than once.

Unfortunately we don’t do the same as Apache would normally do, i.e. check each path segment recursively up to the root to find an index file to use. So you’d have to override the index in your admin php_fastcgi to /admin/index.php so that it falls back to that instead of /index.php. I think that would work.

You probably really need this. Caddy will automatically serve files from those other directories with file_server (unless the file ends in .php)

2 Likes

Thank you very much @francislavoie. The site now behaves like I need it.

I tried to move the trusted_proxies directive out of php_fastcgi, but then devenv could not parse the configuration any more. I guess their parser is not up to the latest Caddy features.

I checked if I can remove the @default exclusion for the folders. A quick test showed no issues. I adopted this rule from Shopwares devenv.nix but I might not need it here as you suggest.

Again. Thank you very much. As I’m very new to Caddy I could not figure it out myself.

I’m not sure what devenv is, but why would it care what your Caddyfile looks like? :thinking: I don’t understand.

You’d just put this at the top of your Caddyfile:

{
	servers {
		trusted_proxies static private_ranges
	}
}

See Global options (Caddyfile) — Caddy Documentation

1 Like

devenv uses NIX to build recomposable environments. My Caddy config is only a small part in a larger devenv config. I do not configure Caddy directly only the part inside the vhost, im my case :8000 { ....

devenv then creates a complete Caddy config file. It is a little bit like docker-compose but more complex.

Unfortunately I have another issue:
I need requests to files like http://localhost:8000/asset/custom.css,plugin_css?v=1.0.0 to end up on a specific compression script.
My naive first guess was:

handle_path /asset/* {
    php_fastcgi unix/${config.languages.php.fpm.pools.web.socket} {
        index /includes/libs/minify/index.php?g={path}
        trusted_proxies private_ranges
   }
}

I use handle_path because I want “assets” to get stripped from the {path}.

Unfortuenately the requests end up on the index.php file of the frontend.
why is this not working?

index takes a single filename. If you need to use placeholders and such, you probably want configure try_files instead.

You definitely should set that up to be able to configure global options. Lots of things are only configurable in global options.

1 Like

I tried

        handle_path /asset/* {
          php_fastcgi unix/${config.languages.php.fpm.pools.web.socket} {
          	try_files {path} {path}/ /includes/libs/minify/index.php?g={path}&{query}
          	trusted_proxies private_ranges
          }
        }

and

        handle_path /asset/* {
          php_fastcgi unix/${config.languages.php.fpm.pools.web.socket} {
            root includes/libs/minify
          	try_files {path} {path}/ /index.php?g={path}&{query}
          	trusted_proxies private_ranges
          }
        }

Both do not work and requests still end up on the global index.php with a 404.

Is there any way to get info why the rules do not match?

If you enable the debug global option, you should see more detail in the logs about the rewrites and the exact paths sent to PHP.

But to do that, obviously you’ll need to resolve your issue about not being able to configure global options due to your devenv thing. It’s quite important that you’re able to configure global options, because a lot of Caddy’s features are only configurable that way.

1 Like

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