DNS srv record support in proxy directive

Looking through the documentation on proxy, it indicates the option to use a SRV record to determine host to proxy to but doesn’t provide any examples. I’ve tried the following:
:443 {
proxy / srv+https://{

with dns containing
_http._tcp.example.com 86400 IN SRV 10 60 80 bigbox.example.com.

and I am getting a 502 error when requesting example.com

I’m not sure how to debug caddy to show what it did to proxy the request. Any insight into what I’ve done wrong or how to troubleshoot it would be appreciated

Hi @Richard_Stupek,

From the docs:

If the endpoint starts with srv:// or srv+https:// it will be considered as a service locator and Caddy will attempt to resolve available services via SRV DNS lookup.


So, you’d still need a regular endpoint (e.g., _http._tcp.example.com), you just add the appropriate prefix to it, which tells Caddy to resolve its SRV records instead of proxying to it directly.

Thanks for the response but I guess I still don’t understand what I missed in setting it up. I’ve spent a little time building and attempting to trace the setup and have only gotten to the point where the dns resolver for srv handling never seems to get called. Maybe I’m misunderstanding what srv:// was supposed to do. I thought a request would come in for example.com, that would cause caddy to request the srv record for _http._tcp.example.com and find a hostname and port to use to proxy the request to. Is that what it was meant to do?

Well, Caddy has no good way to know that you want it to look for _http._tcp.example.com unless you specify that as the endpoint/service locator.

In your given example, your site doesn’t even have a label, it serves all requests on port 443. Even if Caddy were to dynamically look up SRV records for the hostname provided by the client, how would it handle clients that don’t provide a hostname, or connect directly to the IP address?

And how does it know for sure you want it to look for _http._tcp.? Even if it were a fair assumption, it’s not guaranteed.

So unfortunately, Caddy can’t take just srv:// and extrapolate the rest; you’ll need to specify the FQDN of your service record and prefix it as required (srv:// for HTTP, srv+https:// for HTTPS).

Thanks for your reply I appreciate your time on this. I think in the word of https everywhere that we’re moving to, a client using no host name or just an IP address aren’t going to get a website in either case anyways and those cases can easily be handled by using another config block. Also, does the proxy directive handle anything but http at this point? I think it would be a fair assumption to use the host name of the request to lookup the srv record for http (srv://) or https (srv+https://) so (_http._tcp.hostnameofrequest.com or _https…) and the SRV option becomes a much more useful feature than it is presently since it can provide a list of failover hosts and include a priority weighting which isn’t presently available with the current configuration options.

At a minimum, the current feature should probably fail to load if the configuration option for srv:// and srv+https:// does not include a host name since it won’t work in that case.

Your own example has a :443 block that would take no-hostname or IP address requests.

Well, it does websockets, but over HTTP if I’m not mistaken.

Correct me if I’m wrong, but my understanding is that while the format _service._proto is defined in RFC 2782, there are non-standard implementations that don’t use the underscores, or include other information.

Wait, why is failover, weighting etc. not available by using srv://_http._tcp.example.com?

That might not be a bad idea for proxying across the board. We don’t validate the upstream at startup in case it doesn’t come up until after Caddy does, but we could probably check to make sure it’s not just a scheme without a hostname of any kind.

I think its immaterial that there are non-standard implementations since the code uses net LookupSRV to perform the lookup and thus would depend on records being formatted per rfc 2782?

Correct me if I’m wrong but I don’t believe the code uses more than one SRV entry?

I looked through the code and was able to almost get it working but not being an expert in Caddy I couldn’t figure out why the following block of code never got anything for the addr passed to the function but the host from the configed host

func (rp *ReverseProxy) srvDialerFunc(locator string) func(network, addr string) (conn net.Conn, err error) {
service := locator
if strings.HasPrefix(locator, "srv://") {
	service = locator[6:]
} else if strings.HasPrefix(locator, "srv+https://") {
	service = locator[12:]
return func(network, addr string) (conn net.Conn, err error) {
	_, addrs, err := rp.srvResolver.LookupSRV(context.Background(), "", "", service)
	if err != nil {
		return nil, err
	return net.Dial("tcp", fmt.Sprintf("%s:%d", addrs[0].Target, addrs[0].Port))


Hmm, good points all, thanks for going over that.

Given that we’re diving into the code and features, it might be better to discuss this at the Github repository. Could you open an issue there and link this thread for background?


Looking back at this in particular, I noted:

LookupSRV constructs the DNS name to look up following RFC 2782. That is, it looks up _service._proto.name. To accommodate services publishing SRV records under non-standard names, if both service and proto are empty strings, LookupSRV looks up name directly.

net package - net - pkg.go.dev

And looking at the LookupSRV call in the reverse proxy code (click through to see the supplied parameters, of which service and proto are indeed empty):


Since we never pass the service or proto directly to the function, it always looks up the name directly as a standard query; so records definitely do not need to be formatted to any standard whatsoever to be used as a service locator by Caddy.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.