Symfony with URI prefix

1. Caddy version (caddy version): v2.1

2. How I run Caddy: Windows Service

a. System environment: Windows Server 2019

d. My complete Caddyfile or JSON config:

localhost {
    root * C:/demo

    @api {
        path_regexp api ^/(env_[\w]*)/api/(.*)$
    }

    handle @api {
        root * C:/demo/env_01/api/public
        php_fastcgi 127.0.0.1:9008

        file_server
    }

    php_fastcgi 127.0.0.1:9000

    file_server
}

3. The problem I’m having:

I’m trying to serve a Symfony website ontop of an existing backend. They require different versions of PHP to run that is why I’ve put the symfony (api) part inside a handle.

Example:

4. Error messages and/or full log output:

domain.com/env_01/api/home does redirect to Symfony however Symfony is looking for route /env_01/api/home which doesn’t exist.
It should be looking for route /home

How can I “strip” the beggining of the URI so that symfony tries to find correct routes ?

5. What I already tried:

I’ve trid to use the URI directive with strip_prefix but symfony still tries to find the full URI (/env_01/api/home)

Thanks !

Hmm. uri strip_prefix is definitely the right tool for this.

localhost {

	@api path_regexp api ^/(env_[\w]*)/api/(.*)$
	handle @api {
		root * C:/demo/env_01/api/public
		uri strip_prefix /{re.api.1}/api
		php_fastcgi 127.0.0.1:9008
		file_server
	}

	handle {
		root * C:/demo
		php_fastcgi 127.0.0.1:9000
		file_server
	}
}

Give that a shot.

If that doesn’t seem to work, add this to the top of your Caddyfile:

{
	debug
}

That’ll enable some extra logging to stdout so we can see what’s going on with the path rewrites.

1 Like

Thanks for the quick reply !
However this doesn’t seem to work.

Here is the complete stdout log:

{"level":"debug","ts":1603043422.3141768,"logger":"http.handlers.rewrite","msg":"rewrote request","request":{"method":"GET","uri":"/env_01/api/dashboard/","proto":"HTTP/2.0","remote_addr":"0.0.0.0:49632","host":"11.1.1.1:4433","headers":{"User-Agent":["insomnia/2020.4.1"],"Accept":["*/*"]},"tls":{"resumed":false,"version":772,"ciphersuite":4865,"proto":"h2","proto_mutual":true,"server_name":""}},"method":"GET","uri":"/dashboard/"}
{"level":"debug","ts":1603043422.3141768,"logger":"http.handlers.rewrite","msg":"rewrote request","request":{"method":"GET","uri":"/dashboard/","proto":"HTTP/2.0","remote_addr":"0.0.0.0:49632","host":"11.1.1.1:4433","headers":{"User-Agent":["insomnia/2020.4.1"],"Accept":["*/*"]},"tls":{"resumed":false,"version":772,"ciphersuite":4865,"proto":"h2","proto_mutual":true,"server_name":""}},"method":"GET","uri":"index.php"}
{"level":"debug","ts":1603043422.3141768,"logger":"http.reverse_proxy.transport.fastcgi","msg":"roundtrip","request":{"method":"GET","uri":"index.php","proto":"HTTP/2.0","remote_addr":"0.0.0.0:49632","host":"11.1.1.1:4433","headers":{"User-Agent":["insomnia/2020.4.1"],"Accept":["*/*"],"X-Forwarded-For":["0.0.0.0"],"X-Forwarded-Proto":["https"]},"tls":{"resumed":false,"version":772,"ciphersuite":4865,"proto":"h2","proto_mutual":true,"server_name":""}},"dial":"127.0.0.1:9008","env":{"AUTH_TYPE":"","CONTENT_LENGTH":"","CONTENT_TYPE":"","DOCUMENT_ROOT":"C:\\demo\\env_01\\api\\public","DOCUMENT_URI":"index.php","GATEWAY_INTERFACE":"CGI/1.1","HTTPS":"on","HTTP_ACCEPT":"*/*","HTTP_HOST":"11.1.1.1:4433","HTTP_USER_AGENT":"insomnia/2020.4.1","HTTP_X_FORWARDED_FOR":"0.0.0.0","HTTP_X_FORWARDED_PROTO":"https","PATH_INFO":"","QUERY_STRING":"","REMOTE_ADDR":"0.0.0.0","REMOTE_HOST":"0.0.0.0","REMOTE_IDENT":"","REMOTE_PORT":"49632","REMOTE_USER":"","REQUEST_METHOD":"GET","REQUEST_SCHEME":"https","REQUEST_URI":"/env_01/api/dashboard/","SCRIPT_FILENAME":"C:\\demo\\env_01\\api\\public\\index.php","SCRIPT_NAME":"index.php","SERVER_NAME":"11.1.1.1","SERVER_PORT":"4433","SERVER_PROTOCOL":"HTTP/2.0","SERVER_SOFTWARE":"Caddy/v2.1.1","SSL_CIPHER":"TLS_AES_128_GCM_SHA256","SSL_PROTOCOL":"TLSv1.3"}}
{"level":"debug","ts":1603043422.8061702,"logger":"http.handlers.reverse_proxy","msg":"upstream roundtrip","upstream":"127.0.0.1:9008","request":{"method":"GET","uri":"index.php","proto":"HTTP/2.0","remote_addr":"0.0.0.0:49632","host":"11.1.1.1:4433","headers":{"Accept":["*/*"],"X-Forwarded-For":["0.0.0.0"],"X-Forwarded-Proto":["https"],"User-Agent":["insomnia/2020.4.1"]},"tls":{"resumed":false,"version":772,"ciphersuite":4865,"proto":"h2","proto_mutual":true,"server_name":""}},"duration":0.4919961,"headers":{"X-Debug-Token-Link":["https://11.1.1.1:4433/_profiler/7899b0"],"X-Robots-Tag":["noindex"],"Status":["404 Not Found"],"Content-Type":["text/html; charset=UTF-8"],"Cache-Control":["no-cache, private"],"X-Debug-Token":["7899b0"],"X-Previous-Debug-Token":["2f1e83"],"X-Powered-By":["PHP/8.0.0rc1"],"X-Debug-Exception":["No%20route%20found%20for%20%22GET%20%2Fenv_01%2Fapi%2Fdashboard%2F%22"],"X-Debug-Exception-File":["C%3A%5Cdemo%5Cenv_01%5Capi%5Cvendor%5Csymfony%5Chttp-kernel%5CEventListener%5CRouterListener.php:136"],"Date":["Sun, 18 Oct 2020 17:50:22 GMT"]},"status":404}

The uri directive seems to work correctly as in the first two lines the uri is striped down to /dashboard.
However line 3 is more interesting as PHP receives “REQUEST_URI”:"/env_01/api/dashboard/".

Thanks for the help. Cheers !

Ah, right. By design, the fastcgi transport uses the original request path (before any rewrites) for the REQUEST_URI env.

This is because we very often need to rewrite to index.php, which drops the remainder. The rewrite module and the proxy module are separate, and they don’t know about eachother. The problem is that we need to draw the line somewhere for the value of that env. We can’t really know whether a specific rewrite was intended to also apply to the path being sent or not. Caddy leans towards the “apps usually want the URL the client sent, untouched”.

Anyways I can offer you three options:

  1. You can override the env, which is an easy fix but is a funky workaround so it’s my least favorite:
php_fastcgi 127.0.0.1:9008 {
    env REQUEST_URI /{re.api.2}
}

This uses the final matching group in your existing regexp to set the value.

  1. Use a subdomain instead which doesn’t require changing the path at all, i.e. env_01.api.example.com. For testing locally, you can update your hosts file or run a local DNS server like CoreDNS to make whatever domain resolve to 127.0.0.1.

  2. Implement a change in your app that allows for a configurable base path in your router. Pretty sure Symfony allows for this. I write Laravel more often though. A quick Google seems to suggest you can use framework.router.request_context.base_url or the newer framework.router.default_uri added in I think Symfony 5.1.

1 Like

Great thanks for the excellent answer !
Unfortunately while option 2 is our favourite we will have to wait a little before using it as it would require quite a few changes on existing servers.
So we are going to go for one of the other two options until we can go back to 2.

Thanks for the help and the great work on Caddy !

1 Like

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