Rewrite /someroute to /

1. Output of caddy version:

v2.6.2 h1:wKoFIxpmOJLGl3QXoo6PNbYvGW4xLEgo32GPBEjWL8o=

2. How I run Caddy:

Official Docker image

a. System environment:

Docker Stand Alone CE 20.10.21
Debian 11.5

b. Command:

Docker compose - see below

c. Service/unit/compose file:

version: "3.6"
services:
  Caddy:
    container_name: Caddy
    hostname: caddy
    image: caddy:latest
    networks:
      - Private
    ports:
      - 443:443/tcp
      - 80:80/tcp
    restart: unless-stopped
    volumes:
      - /root/caddy:/etc/caddy:ro
    working_dir: /srv
    logging:
      driver: "json-file"
      options:
        max-size: "100m"

d. My complete Caddy config:

https://test1.camdenacres.stream {
        tls /etc/caddy/certs/live/camdenacres.stream/fullchain.pem /etc/caddy/certs/live/camdenacres.stream/privkey.pem


        rewrite /api/* /index.php?{query}

        reverse_proxy 192.168.30.20 {
        }
}



3. The problem I’m having:

I am trying to figure out how to take a service on the backend that requires /api/index.php?some=query and make that work on the front end at /index.php?some=query. I can’t find anywhere in Google or the docs how to do this - or at least don’t know what to search for.

4. Error messages and/or full log output:

INFO    using adjacent Caddyfile
Error: adapting config using caddyfile: parsing caddyfile tokens for 'rewrite': Caddyfile:56 - Error during parsing: Wrong argument count or unexpected line ending after '^/api/([^/]*)$'

5. What I already tried:

rewrite /api/* /index.php?{query}

6. Links to relevant resources:

I think you’re looking for handle_path, which has prefix stripping behaviour.

1 Like

I seem to have no idea how to get this to work.

https://test1.camdenacres.stream {
        tls /etc/caddy/certs/live/camdenacres.stream/fullchain.pem /etc/caddy/certs/live/camdenacres.stream/privkey.pem
	
	handle_path /api/* {
	
        reverse_proxy 192.168.30.20 {
        }
	}
}

This gives me no output but at least I am not getting the default Apache page (404) from the upstream. When I explicitly enter https://test1.camdenacres.stream/index.php I get a download dialog and a zero byte file.

https://test1.camdenacres.stream {
        tls /etc/caddy/certs/live/camdenacres.stream/fullchain.pem /etc/caddy/certs/live/camdenacres.stream/privkey.pem
	
	handle_path /api/* 
	
        reverse_proxy 192.168.30.20 {
        }

}

Shows the upstream Apache test page (404) regardless of path, explicit index.php etc.

In the upstream Apache logs it does not appear that in any case Caddy is requesting /api behind the scenes.

To be clear, the browser URL should look like https://test1.camdenacres.stream/index.php?some=thing where as the upstream is receiving http://192.168.30.20/api/index.php?some=thing .

Oh I understood that the other way around. You want to add a prefix before proxing instead of removing one.

In that case, do this:

rewrite * /api{uri}
1 Like

Thank you… that moves the ball forward a tad. The site loads index.php from /. Now the issue is the upstream wants to call /api from the index.php so the upstream calls are failing with /api/api/index.php?some=thing as /api is duplicated. Is there any intelligence on the Caddy side to only add /api in the upstream request when /api is missing?

The expanded php_fastcgi route example seems like a hint, but everything references PHP having file locally vs upstream - and the upstream Apache is dping that sort of work already.

This sounds like an XY problem - Wikipedia situation.

Are you sure you want to use reverse_proxy here? If it’s a PHP app you’re proxing to, why not run php-fpm and use Caddy’s php_fastcgi? Then you could add /api to the webroot and it should do what you expect.

That said, you can use a path matcher with the not matcher to only rewrite when the path doesn’t match:

@notApi not path /api*
rewrite @notApi /api{uri}
1 Like

I understand XY problems. reverse_proxy is the hammer I have so to speak. I thought this would be simple enough, but seemingly not. The app expects the route /api and sends everything through index.php. So for what that is worth.

I tried to get php_fastcgi working. I tried:

php_fastcgi  /api/* 192.168.30.20 {
                root  /api
                split .php
                index index.php
        }
php_fastcgi  /api/* 192.168.30.20:80 {
                root  /api
                split .php
                index index.php
        }
php_fastcgi   192.168.30.20:80 {
                root  /api
                split .php
                index index.php
        }

And in all of these cases I either got a 404 or mostly no request shows up on Apache at all.

Thanks for sticking with me on this.

1 Like

The root option of php_fastcgi controls the filesystem path being passed to the php-fpm service.

If you don’t actually have an api directory in the root of your filesystem, then that will of course not work. Where are your PHP files actually stored on your system?

You don’t need to specify split and index, those are already the defaults.

Also, you should use the root directive instead of the root option of php_fastcgi so that file_server knows the location to find static files to serve, and to avoid repeating the root in php_fastcgi. The root option of php_fastcgi is useful though if your php-fpm service has a different view of the filesystem than Caddy (i.e. is running in a container where the PHP source files are in a different path than what Caddy has access to).

1 Like

The PHP files and the entire app all live on an upstream Apache server. This is where my brain hurts and I thought I could get away with a simple reverse_proxy. The Apache server has all the directives to deal with static files vs routes back to the index. Caddy is a server in front of all of that acting as a gateway where we land TLS. So truly the only job I need Caddy to do is TLS and to make /api appear at / for clients - though as we see that’s no that easy with how PHP works.

I should add I tried

@notApi not path /api*
rewrite @notApi /api{uri}

This seems close… however the upstream is still seeing / rather than /api on all subsequent requests. That is, on the first request index.php loads from /api (and looks like / to the browser). Then a POST is sent and arrives at the upstream correctly as /api/index.php. Then the next GET to /index.php?some=thing shows to the upstream as /index.php?some=thing instead of /api/index.php?some=thing and thus 404s.

Fair enough. If you’re doing a lot of legacy rewrites and such, it can be hard to clean up.

Hmm, that shouldn’t happen. What’s in your logs? What’s your whole config at this point? Turn on the debug global option to increase the amount of details in the logs and show us what happens on that subsequent request. I don’t understand why the rewrite wouldn’t happen.

1 Like

I am back to this. It seems I missed a very important detail embarrassingly enough.

Using this:

@notApi not path /api*
rewrite @notApi /api{uri}

This is what I get in Apache.

192.168.30.14 - - [16/Jan/2023:11:07:08 -0500] "GET /api/ HTTP/1.1" 200 1987 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:108.0) Gecko/20100101 Firefox/108.0"
192.168.30.14 - - [16/Jan/2023:11:07:27 -0500] "POST /api/index.php HTTP/1.1" 302 576 "https://api.internal.dev/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:108.0) Gecko/20100101 Firefox/108.0"
192.168.30.14 - - [16/Jan/2023:11:07:27 -0500] "GET /api.php?action=getall.view HTTP/1.1" 404 444 "https://api.internal.dev/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:108.0) Gecko/20100101 Firefox/108.0"

The actual backend index is api.php and the POST to authenticate is going to index.php. So I am not sure what to do about that. Why would the POST go to an index that doesn’t exist?

Again, what’s your entire config at this point? Did you turn on the debug global option? What’s in your Caddy logs after doing that and making some requests?

We need a better picture of your setup to move forwards.

Config:

{
debug
}

http://*.camdenacres.stream {
        redir https://{host}{uri}
}



https://test1.camdenacres.stream  {
        tls /etc/caddy/certs/live/camdenacres.stream/fullchain.pem /etc/caddy/certs/live/camdenacres.stream/privkey.pem


@notapi not path /api*
rewrite @notapi /api{uri}


reverse_proxy 192.168.30.20 {
        }


}

Logs are here

What Apache is showing

192.168.30.14 - - [17/Jan/2023:11:34:44 -0500] "GET /api/ HTTP/1.1" 200 1986 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:108.0) Gecko/20100101 Firefox/108.0"
192.168.30.14 - - [17/Jan/2023:11:34:48 -0500] "POST /api/index.php HTTP/1.1" 302 576 "https://test1.camdenacres.stream/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:108.0) Gecko/20100101 Firefox/108.0"
192.168.30.14 - - [17/Jan/2023:11:34:48 -0500] "GET /api.php?action=getall.view HTTP/1.1" 404 444 "https://test1.camdenacres.stream/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:108.0) Gecko/20100101 Firefox/108.0"

Those logs are unreadable with the spam of INF and DBG all over.

How are you grabbing the logs? Caddy doesn’t log stuff like that. Whatever software you’re using to read the logs is really messing it up.

Use the command line to run docker-compose logs caddy instead.

Sure, but what were the requests you made, exactly? Use curl -v and show the requests being made.

I convinced the upstream admins to create a new internally accessible vhost that puts the API at the root of the site so a vanilla reverse_proxy would work.

1 Like

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