Writing DRY HTTPS upgrade config with strip-www feature

1. Caddy version (caddy version):

caddy:2

2. How I run Caddy:

docker

a. System environment:

Docker on Ubuntu 20.04

d. My complete Caddyfile or JSON config:

(strip-www) {
    @www.{args.0} host www.{args.0}
    redir @www.{args.0} https://{args.0}{uri}
}

(add-www) {
    @{args.0} host {args.0}
    redir @{args.0} https://www.{args.0}{uri}
}

www.example.com:443 {
    import strip-www example.com

    tls let_admin@example.com {
        # on_demand
        protocols tls1.2 tls1.3
        ciphers TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
    }

    # HSTS (63072000 seconds)
    header / Strict-Transport-Security "max-age=63072000"
}

example.com:443 {
    reverse_proxy http://internal_app:65

    @websockets {
      header_regexp Connection Upgrade
      header        Upgrade websocket
    }
    
    reverse_proxy @websockets http://internal_app:65

    tls let_admin@example.com {
        # on_demand
        protocols tls1.2 tls1.3
        ciphers TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
    }

    # HSTS (63072000 seconds)
    header / Strict-Transport-Security "max-age=63072000"
}

3. The problem I’m having:

I have a server running internally at 65 and serve it over example.com:443 to the public internet.

What I would like to do is:

  1. If user arrives at www.example.com:443 (out of habit or browser shortcut), redirect them to example.com:443
  2. If user arrives at (www.)example.com:80, redirect them to example.com:443 (is that called an HTTPS upgrade?)
  3. Have HSTS enabled on example.com:443

Here’s what I have. I think I have done a poor job because it’s not DRY - what are some things I can do better?

1 Like

Which version, exactly? Run caddy version in your container to find out. Make sure you’re running the latest, v2.4.6.

Why are you setting this stuff?

I strongly recommend just leaving the defaults. Don’t change the ciphers unless you have a very specific reason to do it.

And setting protocols doesn’t help in any way, because those are already the defaults that you’re specifying, and it would only limit you from using what would be (an imaginary) tls1.4 if Caddy were to add support for it.

Caddy’s defaults are well-crafted and secure.

FYI, you’re using a / matcher here, which matches only requests to exactly /, and nothing else. Remove the / to match all requests.

You don’t need any of this. You only need a websocket matcher if you’re proxying to a different server. Request matchers are basically just “if conditions” on the requests. All you need is reverse_proxy http://internal_app:65 to proxy all requests to that upstream, including websocket requests.

All you need for www redirects, really, is just this:

www.example.com {
	redir https://example.com{uri}
}

That’s it.

If you have multiple www. domains, then you could do something like this:

www.example.com, www.another.com {
	redir https://{labels.1}{labels.0}{uri}
}

Where {labels.*} is a placeholder for each segment of the domain, starting with 0 as the right-most segment (so 0 is com, 1 is example or another depending).

1 Like
/srv # caddy version
v2.4.6 h1:HGkGICFGvyrodcqOOclHKfvJC0qTU7vny/7FhYp9hNw=

Ok - but if it was on a different port - like 66, then too, I would have to explicitly mention the websocket matcher with 66 there?

Here’s the latest config:

(strip-www) {
    @www.{args.0} host www.{args.0}
    redir @www.{args.0} https://{args.0}{uri}
}

www.example.com {
    import strip-www example.com
}

www.example.com:443 {
    import strip-www example.com

    tls let_admin@example.com

    # HSTS (63072000 seconds)
    header Strict-Transport-Security "max-age=63072000"
}

example.com:443 {
    reverse_proxy http://internal_app:65

    tls let_admin@example.com

    # HSTS (63072000 seconds)
    header Strict-Transport-Security "max-age=63072000"
}

Question:

  1. Can we make it more dry?

I was thinking something like:

(strip-www) {
    @www.{args.0} host www.{args.0}
    redir @www.{args.0} https://{args.0}{uri}
}

(common-tls-example) {
    tls let_admin@example.com

    # HSTS (63072000 seconds)
    header Strict-Transport-Security "max-age=63072000"
}

www.example.com {
    import strip-www example.com
}

www.example.com:443 {
    import strip-www example.com

    import common-tls-example
}

example.com:443 {
    reverse_proxy http://internal_app:65

    import common-tls-example
}

What’s wrong with this?

  1. Would also like to know more why/how Caddy issues a “308 Permanent Redirect” for http://(www.)example.com/ while it issues a “HTTP/2 302” for https://www.example.com/ ?

Yeah, if you were using a different upstream for websockets vs regular HTTP traffic, then that matcher is useful. But if you’re not, it’s just cruft.

Use the email global option instead, so you don’t need to specify that in every site.

Then…

Remove this entire site block because your www.example.com does the exact same thing.

There’s no difference between www.example.com:443 and www.example.com, Caddy is HTTPS by default.

The redir directive uses 302 by default, as per the docs:

If you want 308, just specify 308 at the end.

2 Likes

Right but what I’m noticing is that while Caddy issues a “HTTP/2 302” for https://www.example.com/, Caddy issues a “308 Permanent Redirect” for http://(www.)example.com/, and that config is all I have, so I don’t understand how that 308 is happening for http

Caddy sets up automatic HTTP->HTTPS redirects

2 Likes

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