Want caddy redirection rule

I have a project with urls like

localhost:3000/alpha/admin
localhost:3000/beta/admin
localhost:3000/charlie/admin

Where {alpha,beta,charlie} are the username identifier, as after that all functionality is same

I want a single rule that conveys

localhost:3000/{identifier} ~ {identifier}.example.com

something in order of -

subdomain.example.com {
reverse_proxy localhost:3000/subdomain
}

where subdomain is variable

or to simplify it even more

a rule that conveys

localhost:3000/first_subdirectory_before_slash is same as first_subdirectory_before_slash.example.com

Do you mean to perform a path rewrite, or do you want a redirect? Those are two distinct things in the context of HTTP.

A redirect is when the server responds with the Location header, telling the client “try again, but at this URL instead”.

A rewrite is an internal manipulation of the request path, before continuing to handle the request.

Caddy’s reverse_proxy doesn’t perform rewrites itself, if that’s what you’re trying to do. You need to use the rewrite directive instead.

If you need to redirect, then you can use the redir directive.

2 Likes

I believe he wants to rewrite without having to repeatedly type out the rewrite rule.

So, for example for each of:

localhost:3000/alpha/admin
localhost:3000/beta/admin
localhost:3000/charlie/admin

he wants

<each>.example.com {
    reverse_proxy localhost:3000/<each>
}

Such that

alpha.example.com {
    reverse_proxy localhost:3000/alpha/
}

without having to actually write that down explicitly

2 Likes

yes I want a rule as you described

In that case, that would be a rewrite, not a redirect. But again, it needs to be done with the rewrite directive.

You could try this:

*.example.com {
	rewrite {labels.2}{uri}
	reverse_proxy localhost:3000
}

Where the {labels.2} placeholder is the subdomain (index starting at 0 from the right-most label).

This uses a wildcard label though, which means Caddy would attempt to get a wildcard cert. That requires setting up the DNS challenge to prove to ACME issuers that you own the entire zone. You’d need a build of Caddy with the appropriate DNS plugin for your DNS provider:

Or you could set up On-Demand TLS, which doesn’t require building Caddy with a plugin, but you need to set up an ask endpoint to prevent abuse.

3 Likes

If we know apriori that only the following subdomains are allowed:

alpha
beta
charlie
  1. Would it be possible to serve the “ask” endpoint, say as a static file server, from within caddy itself, to satisfy the ask endpoint?
  2. Failing which, is it possible to have an “ask” endpoint, that always returns true (for development only - while the actual “ask” endpoint is being built)?
  3. Is there an example of how this “ask” endpoint should behave? (On-Demand TLS questions)

Yes, there’s one company I know of that does this. Just create another route in your config and set the ask endpoint to hit it. :+1:

1 Like

Exercise for the OP :smiley:

Some form of:

respond /ask 200
1 Like

Yep. A matcher can help. You can also be clever with the map directive. There are several ways to do it, likely involving placeholders since you have to read the query string’s domain key.

1 Like

Or you could just do this:

alpha.example.com, beta.example.com, charlie.example.com {
	rewrite {labels.2}{uri}
	reverse_proxy localhost:3000
}

If the domains are directly listed in your config, Caddy will manage certificates for those.

Simpler and safer than On-Demand TLS, if you know the exact domains you need up-front.

2 Likes

@francislavoie thank you for your suggestion

*.example.com {
    rewrite {labels.2}{uri}
    reverse_proxy localhost:3000
}

— end of suggestion —

does this still work if localhost:3000 has sub-directory depth like

<each>.example.com {
    localhost:3000/<each>/a/b/c
}
  • where <each> are the user identifiers, so a and b and c are kind of common pages but different data for them

  • such as if I visit alpha.example.com/a/b/c, behind the scenes it just means

localhost:3000/alpha/a/b/c
localhost:3000/caddy/caddy-is-awesome?try=it
  • static files are always served from localhost:3000 nonetheless

Yes, {uri} is the entire original URI of the incoming request, including the path and query.

Btw for correctness, it should be:

rewrite /{labels.2}{uri}

With the leading slash since the label doesn’t have one, but {uri} includes a leading slash.

No, in this case all requests would be rewritten and proxied, with that config.

1 Like

Here is my Caddyfile as suggested by @francislavoie

xamyr.com {
  reverse_proxy localhost:3000
}

*.xamyr.com {
  rewrite {label.2}{uri}
  reverse_proxy localhost:3000
}

You can try and visit (everything works here) -

  1. https://xamyr.com/alpha/admin ~ responds with alpha
  2. https://xamyr.com/beta/admin ~ responds with beta
  3. https://xamyr.com/charlie/admin ~ responts with charlie

But visiting it like https://{alpha|beta|charlie}.xamyr.com/admin is not working via provided config, one can try via (nothing works here) -

  1. https://alpha.xamyr.com/admin
  2. https://beta.xamyr.com/admin
  3. https://charlie.xamyr.com/admin

*Note it is a Next.js app, and for demo purposes, structure is

/pages/alpha/admin.js
/pages/beta/admin.js
/pages/charlie/admin.js

with a leading slash on config -

*.xamyr.com {
  rewrite /{labels.2}{uri}
  reverse_proxy localhost:3000
}

yeilds -

$ systemctl reload caddy

Job for caddy.service failed.
See "systemctl status caddy.service" and "journalctl -xe" for details.

Like I said, this on its own will not work unless you enable the DNS challenge to get a wildcard certificate.

What’s in your logs? See here in the docs for the command to run to see your logs:

1 Like

for a single domain -

xamyr.com {
  reverse_proxy localhost:3000
}

alpha.xamyr.com {
  rewrite {label.2}{uri}
  reverse_proxy localhost:3000
}

# rewrite /{label.2}{uri} or /{labels.2}{uri) doesn't work

now site xamyr.com emits same behaviour as alpha.xamyr.com, aka -
https://xamyr.com == https://alpha.xamyr.com
https://xamyr.com/alpha/admin == https://alpha.xamyr.com/alpha/admin

desired case, doesn’t work, i.e. -
https://xamyr.com/alpha/admin != https://alpha.xamyr.com/admin

Again, what’s in your logs?

You can turn on the debug global option to get more details in your logs. Add this at the top of your Caddyfile:

{
	debug
}
1 Like

@francislavoie you have been very helpful, just one thing, leaving above issue aside, here is one nginx config, please provide me its caddy equivalent if you can

# nginx.conf

{
  listen 80;
  server_name blog.example.com;

  location / {
    proxy_pass http://localhost:3000/blog$uri;
    proxy_redirect off;
  }
}
# caddyfile

{
  ... ?
}

What I gave you is the equivalent. Caddy’s reverse_proxy does not perform rewrites itself, you need to do a separate rewrite before proxying.

1 Like

@francislavoie you were right, just a little correction

*.example.com {
	rewrite /{labels.2}{uri}
	reverse_proxy localhost:3000
}

# yeilds ~ systemctl reload caddy - Job for caddy.service failed.

solution -

*.example.com {
	rewrite * /{labels.2}{uri}
	reverse_proxy localhost:3000
}

# an asterisk was required after rewrite

let me know if this isn’t the practice, as I’m new to caddy