Reverse Proxy into subfolder by HTML filtering/header rewriting

1. Output of caddy version:

v2.6.2 h1:wKoFIxpmOJLGl3QXoo6PNbYvGW4xLEgo32GPBEjWL8o=

2. How I run Caddy:

In a docker container on ubuntu 22.04

a. System environment:

Docker:

Client: Docker Engine - Community
 Version:           20.10.22
 API version:       1.41
 Go version:        go1.18.9
 Git commit:        3a2c30b
 Built:             Thu Dec 15 22:28:04 2022
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.22
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.18.9
  Git commit:       42c8b31
  Built:            Thu Dec 15 22:25:49 2022
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.15
  GitCommit:        5b842e528e99d4d4c1686467debf2bd4b88ecd86
 nvidia:
  Version:          1.1.4
  GitCommit:        v1.1.4-0-g5fd4c4d
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

OS:

Distributor ID: Ubuntu
Description:    Ubuntu 22.04.1 LTS
Release:        22.04
Codename:       jammy

b. Command:

docker-compose up -d

If I need to modify the Caddy file I do it in VS-Code and then run

docker exec -it caddy caddy reload --config /etc/caddy/Caddyfile

c. Service/unit/compose file:

version: "3.7"

networks:
  # network created for reverse proxy such that all other
  # containers are also on it can communicate with each other
  revProxy-net:
    name: revProxy-net
    driver: bridge

services:
  caddy:
    image: caddy:latest
    restart: unless-stopped
    container_name: caddy
    hostname: caddy
    networks:
      # caddy is in the network with the other containers
      - revProxy-net
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - /home/sagnik/Projects/docker/caddy-reverse-proxy/Caddyfile:/etc/caddy/Caddyfile
      - /home/sagnik/Projects/docker/caddy-reverse-proxy/data:/data
      - /home/sagnik/Projects/docker/caddy-reverse-proxy/config:/config
        # usings hosts tailscale socket
      - /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock

d. My complete Caddy config:

(network_paths) {
	redir /dozzle /dozzle/
	redir /doz /dozzle/
	handle_path /dozzle/* {
		reverse_proxy /* dozzle:8080
	}

	redir /apaxy /apaxy/
	redir /apax /apaxy/
	handle_path /apaxy/* {
		reverse_proxy /* apaxy:7469
	}

	redir /glances /glances/
	redir /galnce /glances/
	redir /gc /glances/
	handle_path /glances/* {
		reverse_proxy /* glances:61208
	}

	redir /radarr /radarr/
	handle_path /radarr/* {
		reverse_proxy /* radarr:7878
	}

	redir /jackett /jackett/
	handle_path /jackett/* {
		reverse_proxy /* jackett:9117
	}

	redir /sonarr /sonarr/
	handle_path /sonarr/* {
		reverse_proxy /* sonarr:8989
	}

	redir /qbittorrent /qbittorrent/
	redir /qbt /qbittorrent/
	handle_path /qbittorrent/* {
		reverse_proxy /* qbittorrent-nox:9696
	}

	redir /jellyfin /jellyfin/
	redir /jelly /jellyfin/
	handle_path /jellyfin/* {
		reverse_proxy /* jellyfin:8920
	}

	redir /flaresolverr /flaresolverr/
	handle_path /flaresolverr/* {
		reverse_proxy /* flaresolverr:8191
	}

	redir /bazarr /bazarr/
	handle_path /bazarr/* {
		reverse_proxy /* bazarr:6767
	}

	redir /guacamole /guacamole/
	redir /guac /guacamole/
	handle_path /guacamole/* {
		reverse_proxy /* guac-guacamole:8080
	}
	reverse_proxy /* dashy
}

lenovo-ideapad-320-15ikb.tail9ece4.ts.net {
	import network_paths
}

3. The problem I’m having:

The documentation doesn’t explain how to do HTML filtering and or header rewriting. As someone with only the basic understanding of networking, I just don’t get who to rewrite the headers.

Take for example:

handle_path /dozzle/* {
	reverse_proxy /* dozzle:8080
}

this app doesn’t have a URL Base setting so I can’t even modify in app, I just want an example showing how I can make a request to lenovo-ideapad-320-15ikb.tail9ece4.ts.net/dozzle/ and get the css and js files to get loaded form the same path and not be sent as a request to to lenovo-ideapad-320-15ikb.tail9ece4.ts.net/.

4. Error messages and/or full log output:

No errors on caddy side.

Here is a curl -v

$ curl -v https://lenovo-ideapad-320-15ikb.tail9ece4.ts.net/dozzle/
*   Trying ip:443...
* Connected to lenovo-ideapad-320-15ikb.tail9ece4.ts.net (100.100.149.9) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS header, Finished (20):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.2 (OUT), TLS header, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=lenovo-ideapad-320-15ikb.tail9ece4.ts.net
*  start date: Jan  6 16:07:50 2023 GMT
*  expire date: Apr  6 16:07:49 2023 GMT
*  subjectAltName: host "lenovo-ideapad-320-15ikb.tail9ece4.ts.net" matched cert's "lenovo-ideapad-320-15ikb.tail9ece4.ts.net"
*  issuer: C=US; O=Let's Encrypt; CN=R3
*  SSL certificate verify ok.
* Using HTTP2, server supports multiplexing
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* Using Stream ID: 1 (easy handle 0x560305101e80)
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
> GET /dozzle/ HTTP/2
> Host: lenovo-ideapad-320-15ikb.tail9ece4.ts.net
> user-agent: curl/7.81.0
> accept: */*
>
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
< HTTP/2 200
< alt-svc: h3=":443"; ma=2592000
< content-security-policy: default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; manifest-src 'self'; connect-src 'self' api.github.com;
< content-type: text/html; charset=utf-8
< date: Sat, 14 Jan 2023 15:57:54 GMT
< server: Caddy
<
* TLSv1.2 (IN), TLS header, Supplemental data (23):
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Dozzle</title>
    <script type="application/json" id="config__json">
      {
        "base": "",
        "version": "v4.6.2",
        "authorizationNeeded": "false",
        "secured": "false",
        "hostname": ""
      }
    </script>
    <link
      rel="icon"
      href="data:image/svg+xml,%3Csvg%20width%3D%22128%22%20height%3D%22128%22%20viewBox%3D%220%200%20128%20128%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%3Crect%20width%3D%22128%22%20height%3D%22128%22%20rx%3D%226%22%20fill%3D%22%23222222%22%2F%3E%0A%3Cpath%20d%3D%22M82.3248%2094.3863H123V104.093H67.8025V95.3506L106.164%2044.3736H68.3808V34.5382H121.072V42.9594L82.3248%2094.3863Z%22%20fill%3D%22%23FFDD57%22%2F%3E%0A%3Cpath%20d%3D%22M8%20107.107L17.5656%2014L43.8372%2016.7013C51.9339%2017.5338%2058.9091%2020.0604%2064.7629%2024.2812C70.6166%2028.5019%2074.8873%2034.0893%2077.5749%2041.0432C80.3052%2048.0016%2081.2514%2055.7674%2080.4137%2064.3407L79.8027%2070.2877C78.9005%2079.0698%2076.4053%2086.5894%2072.3173%2092.8468C68.2719%2099.1084%2062.914%20103.684%2056.2436%20106.574C49.6158%20109.468%2042.1213%20110.529%2033.7602%20109.755L8%20107.107ZM28.8005%2025.3655L21.3043%2098.3288L34.2164%2099.6565C43.6767%20100.629%2051.3299%2098.4435%2057.1758%2093.0993C63.0644%2087.7595%2066.5671%2079.6542%2067.684%2068.7832L68.2424%2063.3477C69.3286%2052.7752%2067.6788%2044.3123%2063.293%2037.9592C58.9542%2031.5678%2052.2295%2027.8607%2043.1188%2026.8377L28.8005%2025.3655Z%22%20fill%3D%22%23FFDD57%22%2F%3E%0A%3C%2Fsvg%3E%0A"
      type="image/svg+xml"
    />

    <script type="module" crossorigin src="/assets/index-7733dcbb.js"></script>
    <link rel="stylesheet" href="/assets/index-9e893dcd.css">
  </head>
  <body>
    <svg
      aria-hidden="true"
      class="is-hidden"
      version="1.1"
      xmlns="http://www.w3.org/2000/svg"
      xmlns:xlink="http://www.w3.org/1999/xlink"
    >
      <defs>
        <symbol id="logo" viewBox="0 0 145 64">
          <path
            d="M15.12 18.016C20.848 18.016 25.104 19.456 27.888 22.336C30.672 25.184 32.064 29.28 32.064 34.624C32.064 38.112 31.376 41.168 30 43.792C28.656 46.384 26.688 48.4 24.096 49.84C21.504 51.28 18.368 52 14.688 52C13.984 52 12.944 51.984 11.568 51.952C10.192 51.888 8.992 51.856 7.968 51.856C6.848 51.856 5.744 51.872 4.656 51.904C3.568 51.904 2.688 51.936 2.016 52V51.04C3.104 50.976 3.92 50.848 4.464 50.656C5.04 50.464 5.424 50.08 5.616 49.504C5.808 48.928 5.904 48.064 5.904 46.912V23.104C5.904 21.92 5.808 21.056 5.616 20.512C5.424 19.936 5.04 19.552 4.464 19.36C3.92 19.136 3.104 19.008 2.016 18.976V18.016C2.688 18.048 3.568 18.096 4.656 18.16C5.744 18.192 6.816 18.192 7.872 18.16C8.992 18.128 10.256 18.096 11.664 18.064C13.072 18.032 14.224 18.016 15.12 18.016ZM13.92 18.88C12.448 18.88 11.488 19.152 11.04 19.696C10.592 20.24 10.368 21.344 10.368 23.008V47.008C10.368 48.672 10.592 49.776 11.04 50.32C11.52 50.864 12.496 51.136 13.968 51.136C17.456 51.136 20.16 50.512 22.08 49.264C24 47.984 25.344 46.128 26.112 43.696C26.88 41.264 27.264 38.304 27.264 34.816C27.264 29.44 26.272 25.44 24.288 22.816C22.336 20.192 18.88 18.88 13.92 18.88ZM47.8395 26.608C49.8875 26.608 51.7275 27.056 53.3595 27.952C54.9915 28.848 56.2875 30.256 57.2475 32.176C58.2395 34.096 58.7355 36.592 58.7355 39.664C58.7355 42.736 58.2395 45.232 57.2475 47.152C56.2875 49.04 54.9915 50.432 53.3595 51.328C51.7275 52.224 49.8875 52.672 47.8395 52.672C45.8235 52.672 43.9835 52.224 42.3195 51.328C40.6875 50.432 39.3755 49.04 38.3835 47.152C37.4235 45.232 36.9435 42.736 36.9435 39.664C36.9435 36.592 37.4235 34.096 38.3835 32.176C39.3755 30.256 40.6875 28.848 42.3195 27.952C43.9835 27.056 45.8235 26.608 47.8395 26.608ZM47.8395 27.568C46.0155 27.568 44.5115 28.512 43.3275 30.4C42.1755 32.288 41.5995 35.376 41.5995 39.664C41.5995 43.952 42.1755 47.04 43.3275 48.928C44.5115 50.784 46.0155 51.712 47.8395 51.712C49.6635 51.712 51.1515 50.784 52.3035 48.928C53.4875 47.04 54.0795 43.952 54.0795 39.664C54.0795 35.376 53.4875 32.288 52.3035 30.4C51.1515 28.512 49.6635 27.568 47.8395 27.568ZM82.0564 27.04L68.1844 51.28H73.4644C74.7444 51.28 75.8484 51.024 76.7764 50.512C77.7364 50 78.5044 49.184 79.0804 48.064C79.6564 46.912 80.0084 45.408 80.1364 43.552H81.2404C81.2404 45.6 81.2564 47.328 81.2884 48.736C81.3204 50.144 81.3684 51.296 81.4324 52.192C80.0244 52.128 78.5524 52.08 77.0164 52.048C75.5124 52.016 74.0084 52 72.5044 52C70.9044 52 69.2724 52.016 67.6084 52.048C65.9444 52.112 64.3444 52.176 62.8084 52.24L76.6804 28H71.4004C69.9924 28 68.8084 28.256 67.8484 28.768C66.9204 29.28 66.2004 30.112 65.6884 31.264C65.2084 32.384 64.8884 33.872 64.7284 35.728H63.6244C63.6244 33.648 63.6084 31.92 63.5764 30.544C63.5444 29.136 63.4964 27.984 63.4324 27.088C64.8404 27.152 66.2964 27.2 67.8004 27.232C69.3364 27.264 70.8564 27.28 72.3604 27.28C73.9604 27.28 75.5924 27.264 77.2564 27.232C78.9204 27.168 80.5204 27.104 82.0564 27.04ZM104.885 27.04L91.0125 51.28H96.2925C97.5725 51.28 9* TLSv1.2 (IN), TLS header, Supplemental data (23):
8.6765 51.024 99.6045 50.512C100.565 50 101.333 49.184 101.909 48.064C102.485 46.912 102.837 45.408 102.965 43.552H104.069C104.069 45.6 104.085 47.328 104.117 48.736C104.149 50.144 104.197 51.296 104.261 52.192C102.853 52.128 101.381 52.08 99.8445 52.048C98.3405 52.016 96.8365 52 95.3325 52C93.7325 52 92.1005 52.016 90.4365 52.048C88.7725 52.112 87.1725 52.176 85.6365 52.24L99.5085 28H94.2285C92.8205 28 91.6365 28.256 90.6765 28.768C89.7485 29.28 89.0285 30.112 88.5165 31.264C88.0365 32.384 87.7165 33.872 87.5565 35.728H86.4525C86.4525 33.648 86.4365 31.92 86.4045 30.544C86.3725 29.136 86.3245 27.984 86.2605 27.088C87.6685 27.152 89.1245 27.2 90.6285 27.232C92.1645 27.264 93.6845 27.28 95.1885 27.28C96.7885 27.28 98.4205 27.264 100.085 27.232C101.749 27.168 103.349 27.104 104.885 27.04ZM115.665 14.368V47.536C115.665 48.88 115.937 49.792 116.481 50.272C117.057 50.752 118.001 50.992 119.313 50.992V52C118.769 51.968 117.937 51.936 116.817 51.904C115.729 51.84 114.625 51.808 113.505 51.808C112.417 51.808 111.313 51.84 110.193 51.904C109.073 51.936 108.241 51.968 107.697 52V50.992C109.009 50.992 109.937 50.752 110.481 50.272C111.057 49.792 111.345 48.88 111.345 47.536V20.128C111.345 18.688 111.089 17.632 110.577 16.96C110.065 16.256 109.105 15.904 107.697 15.904V14.896C108.721 14.992 109.713 15.04 110.673 15.04C111.601 15.04 112.481 14.992 113.313 14.896C114.177 14.768 114.961 14.592 115.665 14.368ZM132.962 26.608C135.778 26.608 137.97 27.472 139.538 29.2C141.138 30.896 141.938 33.552 141.938 37.168H124.994L124.946 36.256H137.33C137.394 34.688 137.266 33.248 136.946 31.936C136.626 30.592 136.114 29.52 135.41 28.72C134.738 27.92 133.858 27.52 132.77 27.52C131.298 27.52 129.986 28.256 128.834 29.728C127.714 31.2 127.042 33.536 126.818 36.736L126.962 36.928C126.898 37.408 126.85 37.936 126.818 38.512C126.786 39.088 126.77 39.664 126.77 40.24C126.77 42.416 127.122 44.288 127.826 45.856C128.53 47.424 129.442 48.624 130.562 49.456C131.714 50.256 132.898 50.656 134.114 50.656C135.554 50.656 136.882 50.304 138.098 49.6C139.314 48.896 140.338 47.648 141.17 45.856L142.13 46.24C141.778 47.296 141.202 48.32 140.402 49.312C139.602 50.304 138.594 51.12 137.378 51.76C136.162 52.368 134.754 52.672 133.154 52.672C130.85 52.672 128.866 52.144 127.202 51.088C125.57 50.032 124.306 48.576 123.41 46.72C122.546 44.832 122.114 42.672 122.114 40.24C122.114 37.424 122.562 35.008 123.458 32.992C124.354 30.944 125.618 29.376 127.25 28.288C128.882 27.168 130.786 26.608 132.962 26.608Z"
          />
        </symbol>
      </defs>
    </svg>
    <div id="app"></div>
  </body>
</html>
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* Connection #0 to host lenovo-ideapad-320-15ikb.tail9ece4.ts.net left intact

5. What I already tried:

Read documentation and wiki

6. Links to relevant resources:

  1. The "subfolder problem", OR, "why can't I reverse proxy my app into a subfolder?"
  2. reverse_proxy (Caddyfile directive) — Caddy Documentation
  3. rewrite (Caddyfile directive) — Caddy Documentation
  4. uri (Caddyfile directive) — Caddy Documentation
  5. GitHub - caddyserver/replace-response: Caddy module that performs replacements in response bodies

To use the replace-response plugin, you need to make a custom build Caddy with the plugin added. See the docs on Docker Hub for how to do this.

But that said, replacing responses is non-trivial and quite error-prone. It requires a good understanding of the underlying application to know which headers need replacing and how paths are returned in response body.

As the article The "subfolder problem", OR, "why can't I reverse proxy my app into a subfolder?" explains, it’s much easier to use subdomains instead. I strongly recommend you take that approach.

That said, it does seem like Dozzle does support changing the base URL. See Base URL · Issue #4 · amir20/dozzle · GitHub

I get what you are saying but how do I make subdomains for my tailscale domain? I have 11 docker containers that will all need a sub-domain.

Well anyways thanks for the reply, I will look into it myself.

Sorry, I can’t answer that question regarding Tailscale, I don’t use it. You should ask for help in their community forums.

1 Like