Rewrite to another domain using Caddy v2

1. Output of caddy version:

v2.6.1 h1:EDqo59TyYWhXQnfde93Mmv4FJfYe00dO60zMiEt+pzo=

2. How I run Caddy:

I installed Cddy using sudo apt install caddy on my Ubuntu server, and I run it as a systemctl service.

a. System environment:

OS: Ubuntu 22.04 x64

b. Command:

systemctl reload caddy

c. Service/unit/compose file:

Paste full file contents here.
Make sure backticks stay on their own lines,
and the post looks nice in the preview pane. -->

d. My complete Caddy config:

/etc/caddy/example.com

example.com {
	root * /var/www/html/example.com
	file_server

	# 404 errors
	handle_errors {
		@404 {
			expression {http.error.status_code} == 404
		}

		rewrite @404 /404.html
		file_server
	}
}

www.example.com {
	redir https://example.com{uri} permanent
}
/etc/caddy/tools.example.com

tools.example.com {
	reverse_proxy 127.0.0.1:4000
}
/etc/caddy/Caddyfile

:80 {
	# Set this path to your site's directory.
	root * /usr/share/caddy

	# Enable the static file server.
	file_server

	# Another common task is to set up a reverse proxy:
	# reverse_proxy localhost:8080

	# Or serve a PHP site through php-fpm:
	# php_fastcgi localhost:9000
}

# example.com
import ./example.com

# tools.example.com
import ./tools.example.com

3. The problem I’m having:

So I have a static website hosted at example.com and a NodeJS app with a reverse proxy on tools.example.com (see above Caddyfiles).

I want that if someone visits example.com/tools, they are shown the tools.example.com site, without changing the URL (so not a redirect).

4. Error messages and/or full log output:

Paste logs/commands/output here.
USE THE PREVIEW PANE TO MAKE SURE IT LOOKS NICELY FORMATTED.

5. What I already tried:

I’ve tried rewrite /tools to https://tools.example.com and rewrite /tools to 127.0.0.1:4000

6. Links to relevant resources:

2 Likes

Howdy @segbedji, welcome to the Caddy community. Thanks for filling out the help template! Your current config, good description of the problem and your goals, and what you’ve already tried give me a pretty good idea of what you’re after.

So: rewrites are totally agnostic of the domain; to a rewrite, the only thing that really exists is the URI.

You want to silently “rewrite” (kind of) the domain by reverse proxying, and rewrite the URI separately beforehand; just the URI. These are two separate operations for the webserver that can be done at the same time, but they both need to be configured.

There’s a few techniques you can use for this. The first is handle_path, which implicitly strips the path in question before passing it on: handle_path (Caddyfile directive) — Caddy Documentation

example.com {
  handle_path /tools* {
    reverse_proxy tools.example.com
  }
}

If you don’t want handle_path to do the prefix stripping implicitly, there’s the uri directive - which effectively just does rewrite under the hood, but is easy to wield - that you can use to manually specify the URI manipulation for this purpose: uri (Caddyfile directive) — Caddy Documentation

You could use it inside a handle:

example.com {
  handle /tools* {
    uri strip_prefix /tools
    reverse_proxy tools.example.com
  }
}

Or you can use a matcher to catch these requests: Request matchers (Caddyfile) — Caddy Documentation

example.com {
  @tools path /tools*
  uri @tools strip_prefix /tools
  reverse_proxy @tools tools.example.com
}

Finally, I note that you’re hosting tools.example.com on the same web server as example.com, so instead of proxying from the latter to the former, you could reverse proxy directly from example.com/tools to your backend, skipping one request. You might need to set the Host header going upstream so the backend treats this request the same as it would a request to tools.example.com. (See: reverse_proxy (Caddyfile directive) — Caddy Documentation)

example.com {
  handle_path /tools* {
    reverse_proxy 127.0.0.1:4000 {
      header_up Host tools.example.com
    }
  }
}
4 Likes

Hello @Whitestrake. Thank you so much. That’s a very great and helpful explanation. I never got to understand properly how rewrites work, and now I have a brief idea.

I went with the third option with a direct reverse proxy to the backend, and it works seamlessly.

Only thing remaining to figure out is how to properly handle the assets from the backend.

Thanks again :).

3 Likes

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