caddyserver/caddy · error

server listening on %v is HTTP, but attempts to configure TL

Error message

server listening on %v is HTTP, but attempts to configure TLS connection policies

What it means

A server classified as HTTP (because at least one of its addresses is http:// or uses http_port) also carries TLS connection policies. Connection policies only make sense where TLS terminates, so applying them to a plaintext server is a logical conflict and adaptation aborts.

Source

Thrown at caddyconfig/httpcaddyfile/httptype.go:1161

	httpsPort := strconv.Itoa(caddyhttp.DefaultHTTPSPort)
	if hsp, ok := options["https_port"].(int); ok {
		httpsPort = strconv.Itoa(hsp)
	}

	var httpOrHTTPS string
	checkAndSetHTTP := func(addr Address) error {
		if httpOrHTTPS == "HTTPS" {
			errMsg := fmt.Errorf("server listening on %v is configured for HTTPS and cannot natively multiplex HTTP and HTTPS: %s",
				srv.Listen, addr.Original)
			if addr.Scheme == "" && addr.Host == "" {
				errMsg = fmt.Errorf("%s (try specifying https:// in the address)", errMsg)
			}
			return errMsg
		}
		if len(srv.TLSConnPolicies) > 0 {
			// any connection policies created for an HTTP server
			// is a logical conflict, as it would enable HTTPS
			return fmt.Errorf("server listening on %v is HTTP, but attempts to configure TLS connection policies", srv.Listen)
		}
		httpOrHTTPS = "HTTP"
		return nil
	}
	checkAndSetHTTPS := func(addr Address) error {
		if httpOrHTTPS == "HTTP" {
			return fmt.Errorf("server listening on %v is configured for HTTP and cannot natively multiplex HTTP and HTTPS: %s",
				srv.Listen, addr.Original)
		}
		httpOrHTTPS = "HTTPS"
		return nil
	}

	for _, sblock := range serverBlocks {
		for _, addr := range sblock.parsedKeys {
			if addr.Scheme == "http" || addr.Port == httpPort {
				if err := checkAndSetHTTP(addr); err != nil {
					return err

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Remove the `tls` directive/policy from the http:// site block
  2. Change the site address to `https://` if TLS should terminate at Caddy
  3. Move the TLS-terminating site to its own port/server

Example fix

# before
http://example.com {
  tls internal
  respond "hi"
}
# after
http://example.com {
  respond "hi"
}
Defensive patterns

Strategy: validation

Validate before calling

# Site blocks with http:// scheme must not contain a tls directive
for block in blocks:
    if block.scheme == 'http' and block.has_directive('tls'):
        raise ConfigError('tls on plaintext site: ' + block.key)

Prevention

When it happens

Trigger: A site block with an `http://` address that also defines `tls` settings (e.g. `http://example.com { tls internal }`), or a global TLS policy attached via global options to a server whose blocks are all HTTP, producing non-empty srv.TLSConnPolicies when checkAndSetHTTP runs.

Common situations: Leftover `tls` directive after switching a site to `http://` (e.g. behind another TLS-terminating proxy), or a `tls` global policy plus `http_port` sites on the same server.

Understand the failure class

Related errors


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/8232d6ed99d527c8. Report an issue: GitHub.