Serve file_server on both port 80 and 443

1. Caddy version (caddy version):

2.1.1

2. How I run Caddy:

caddy start

a. System environment:

AWS EC2 Ubuntu 18.04

b. Command:

caddy start

c. Service/unit/compose file:

paste full file contents here

d. My complete Caddyfile or JSON config:

{
  "admin": {
    "disabled": false,
    "enforce_origin": true,
    "listen": ":2020",
    "origins": [
      "localhost:2020",
      "54.190.1.2"
    ]
  },
  "apps": {
    "http": {
      "servers": {
        "scaning": {
          "listen": [
            ":80",
            ":443"
          ],
          "routes": [
            {
              "handle": [
                {
                  "handler": "file_server",
                  "root": "/var/www/html/qcg-scanning-frontend"
                }
              ],
              "match": [
                {
                  "@id": "host",
                  "host": [
                    "caddy.example.com",
                    "caddy2.example.com"
                  ]
                }
              ]
            }
          ]
        }
      }
    }
  }
}

3. The problem I’m having:

I have the following use case:

My application has a feature to let the user point their CNAME to the my server.
When they point their CNAME to the server, I want to serve them on port 80.
Once the domain has been verified, I add their domain to the port 443 hosts list so that they could be served over https now.

I want to set up the server on both ports 80 and 443 which will serve the file_server from the same directory

  1. If user domain is not in the 443 hosts list, it should be served over http
  2. If the user’s domain is in the 443 hosts list, should be served over https
  3. If the user’s domain is in the 443 hosts list, http requests should be redirected to the https

4. Error messages and/or full log output:

When I pointed the domain CNAME to my server, it is default redirecting to https and giving error

ERR_SSL_PROTOCOL_ERROR

5. What I already tried:

6. Links to relevant resources:

That usecase is exactly what On-Demand TLS is meant to solve. I think you’re overcomplicating this.

You can set up an endpoint that Caddy can ask to see if the domain is allowed. If not, the connection is rejected.

I don’t think it’s a good idea to serve content over HTTP here at all. That’s just a needless complication.

Thanks @francislavoie,

So, the below configuration will work?

{
  "admin": {
    "disabled": false,
    "enforce_origin": true,
    "listen": ":2020",
    "origins": [
      "localhost:2020",
      "54.190.1.2"
    ]
  },
  "apps": {
    "tls": {
      "automation": {
        "on_demand": {
          "rate_limit": {
            "interval": 0,
            "burst": 0
          },
          "ask": ""
        }
      }
    },
    "http": {
      "servers": {
        "scaning": {
          "listen": [
            ":443"
          ],
          "routes": [
            {
              "handle": [
                {
                  "handler": "file_server",
                  "root": "/var/www/html/qcg-scanning-frontend"
                }
              ],
              "match": [
                {
                  "@id": "host",
                  "host": [
                    "caddy.example.com",
                    "caddy2.example.com"
                  ]
                }
              ]
            }
          ]
        }
      }
    }
  }
}

You may want to remove the host matcher because it’s not necessary alongside on-demand TLS. The TLS handshake happens before those matchers run, so all that will happen is that domains that you have certificates for will get empty 200 pages because they didn’t pass the matcher.

Also, I highly recommend setting up an endpoint on your backend service for ask to make requests to. This way you can limit the certificates you manage to only ones for registered customers (or whatever your model is). Otherwise, some clever attackers could abuse the fact that you don’t have limits and force your Caddy instance to issue certificates for every combination combination of domains that point to your server (i.e. any subdomain wildcard), essentially DDOSing your server.

Usually I recommend starting from a Caddyfile config and using that as your base for your JSON config. For example:

{
	on_demand_tls {
		ask https://example.com/ask
	}
}

:443 {
	root * /var/www/html/qcg-scanning-frontend
	file_server

	tls {
		on_demand
	}
}

Which looks like this:

{
  "apps": {
    "http": {

      "servers": {
        "srv0": {
          "listen": [
            ":443"
          ],
          "routes": [
            {
              "handle": [
                {
                  "handler": "vars",
                  "root": "/var/www/html/qcg-scanning-frontend"
                },
                {
                  "handler": "file_server",
                  "hide": [
                    "Caddyfile"
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    "tls": {
      "automation": {
        "policies": [
          {
            "on_demand": true
          }
        ],
        "on_demand": {
          "ask": "https://example.com/ask"
        }
      }
    }
  }
}
1 Like

@francislavoie Thank you so much. Every day I’m loving Caddy more than yesterday.

Your solution is working perfectly. But I’m still having one issue. When SSL is not allowed from the ask endpoint, is it possible to serve them on http (port 80)?

1 Like

I would strongly recommend against trying that. It will complicate things significantly for really no gain (mostly harm, i.e. lack of security).

If a client tries to connect over HTTPS, you can’t downgrade them to HTTP. Browsers will just give an error because the TLS handshake will have failed.

If you try to connect to HTTP, they can be redirected to HTTPS.

The ask mechanism happens during the TLS handshake of an HTTPS connection, and only happens if a new domain that doesn’t yet have a certificate is encountered.

Caddy doesn’t support a mechanism for doing an ask type of matching decision on requests. That would be necessary to determine whether to redirect or to serve. Or you could push configuration changes with an updated host matcher as you go (as you had in your original post), but that doesn’t scale well and clashes with the concept of on-demand TLS.

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