Combining Apps Example

March 3, 2026 · View on GitHub

Summary

This example shows how to combine the HTTP and Layer 4 apps, i.e. use Caddy’s regular HTTP app for serving a static website while also forwarding traffic to certain backends with or without encrypting it.

Configuration

Caddyfile

{
    layer4 {
        # never try to bind to the ports the HTTP app is bound to,
        # use listener (TCP) and/or packet connection (UDP) wrappers instead;
        # see also: https://github.com/mholt/caddy-l4/blob/master/docs/servers.md

        # the following block will cause issues and won't work correctly,
        # if the HTTP app listens to port 443 which is the default HTTPS port
        # tcp/:443 {
        #     ...
        # }

        # the following block will cause issues and won't work correctly,
        # unless HTTP/3 is disabled for the HTTP app listening to port 443
        # udp/:443 {
        #     ...
        # }
        
        # this block works completely fine, but closes any incoming connections
        tcp/:1234 {
            route {
                close
            }
        }
    }

    # while `:443` after the `servers` directive below is optional, it makes sense
    # if the HTTP app listens to multiple ports and the configuration given below
    # should be applicable to one of them exclusively;
    # see also: https://caddyserver.com/docs/caddyfile/options#server-options
    servers :443 {
        # listener wrappers defined below can only handle TCP traffic, e.g. DNS, HTTP, TLS, etc.
        listener_wrappers {
            layer4 {
                # handle all TLS traffic in a single route block for efficiency
                @tls tls
                route @tls {
                    subroute {
                        @tls-1 tls sni subdomain-1.example.com
                        route @tls-1 {
                            # terminate TLS with previously obtained certificates
                            tls
                            # proxy decrypted traffic in clear text to the backend
                            proxy tcp/backend:80
                        }

                        @tls-2 tls sni subdomain-2.example.com
                        route @tls-2 {
                            # terminate TLS with previously obtained certificates
                            tls
                            # proxy decrypted traffic insecurely to the backend
                            proxy {
                                upstream tcp/backend:443 {
                                    # enable TLS and accept any certificates the backend provides
                                    tls_insecure_skip_verify
                                }
                            }
                        }

                        @tls-3 tls sni subdomain-3.example.com
                        route @tls-3 {
                            # terminate TLS with previously obtained certificates
                            tls
                            # proxy decrypted traffic securely to the backend
                            proxy {
                                upstream tcp/backend:443 {
                                    # enable TLS and verify the backend's certificates
                                    tls
                                }
                            }
                        }

                        @tls-4 tls sni subdomain-4.example.com
                        route @tls-4 {
                            # proxy encrypted traffic to the backend
                            proxy {
                                # forward client information to the backend
                                proxy_protocol v2
                                upstream tcp/backend:443 {
                                    # no need to enable TLS, because the traffic is encrypted
                                }
                            }
                        }
                    }           
                }

                # proxy DNS traffic from local subnets to Cloudflare
                @dns {
                    dns
                    remote_ip 10.0.0.0/8 fd00:1122:3344:5566::/64
                }
                route @dns {
                    proxy tcp/1.1.1.1:53
                }
            }
            tls
        }

        # packet connection wrappers defined below can only handle UDP traffic, e.g. DNS, QUIC, etc.
        packet_conn_wrappers {
            # WIP, to be updated once the Layer 4 app becomes capable of wrapping packet connections;
            # by default any UDP traffic received on port 443 is consumed and handled by the HTTP app
        }
    }
}

# this block makes Caddy obtain a wildcard TLS certificate
*.example.com {
    respond "OK" 200
}

:80 {
    respond "INSECURE" 200
}

JSON

{
    "apps": {
        "http": {
            "servers": {
                "srv0": {
                    "listen": [
                        ":443"
                    ],
                    "listener_wrappers": [
                        {
                            "routes": [
                                {
                                    "handle": [
                                        {
                                            "handler": "subroute",
                                            "routes": [
                                                {
                                                    "handle": [
                                                        {
                                                            "handler": "tls"
                                                        },
                                                        {
                                                            "handler": "proxy",
                                                            "upstreams": [
                                                                {
                                                                    "dial": [
                                                                        "tcp/backend:80"
                                                                    ]
                                                                }
                                                            ]
                                                        }
                                                    ],
                                                    "match": [
                                                        {
                                                            "tls": {
                                                                "sni": [
                                                                    "subdomain-1.example.com"
                                                                ]
                                                            }
                                                        }
                                                    ]
                                                },
                                                {
                                                    "handle": [
                                                        {
                                                            "handler": "tls"
                                                        },
                                                        {
                                                            "handler": "proxy",
                                                            "upstreams": [
                                                                {
                                                                    "dial": [
                                                                        "tcp/backend:443"
                                                                    ],
                                                                    "tls": {
                                                                        "insecure_skip_verify": true
                                                                    }
                                                                }
                                                            ]
                                                        }
                                                    ],
                                                    "match": [
                                                        {
                                                            "tls": {
                                                                "sni": [
                                                                    "subdomain-2.example.com"
                                                                ]
                                                            }
                                                        }
                                                    ]
                                                },
                                                {
                                                    "handle": [
                                                        {
                                                            "handler": "tls"
                                                        },
                                                        {
                                                            "handler": "proxy",
                                                            "upstreams": [
                                                                {
                                                                    "dial": [
                                                                        "tcp/backend:443"
                                                                    ],
                                                                    "tls": {}
                                                                }
                                                            ]
                                                        }
                                                    ],
                                                    "match": [
                                                        {
                                                            "tls": {
                                                                "sni": [
                                                                    "subdomain-3.example.com"
                                                                ]
                                                            }
                                                        }
                                                    ]
                                                },
                                                {
                                                    "handle": [
                                                        {
                                                            "handler": "proxy",
                                                            "proxy_protocol": "v2",
                                                            "upstreams": [
                                                                {
                                                                    "dial": [
                                                                        "tcp/backend:443"
                                                                    ]
                                                                }
                                                            ]
                                                        }
                                                    ],
                                                    "match": [
                                                        {
                                                            "tls": {
                                                                "sni": [
                                                                    "subdomain-4.example.com"
                                                                ]
                                                            }
                                                        }
                                                    ]
                                                }
                                            ]
                                        }
                                    ],
                                    "match": [
                                        {
                                            "tls": {}
                                        }
                                    ]
                                },
                                {
                                    "handle": [
                                        {
                                            "handler": "proxy",
                                            "upstreams": [
                                                {
                                                    "dial": [
                                                        "tcp/1.1.1.1:53"
                                                    ]
                                                }
                                            ]
                                        }
                                    ],
                                    "match": [
                                        {
                                            "dns": {},
                                            "remote_ip": {
                                                "ranges": [
                                                    "10.0.0.0/8",
                                                    "fd00:1122:3344:5566::/64"
                                                ]
                                            }
                                        }
                                    ]
                                }
                            ],
                            "wrapper": "layer4"
                        },
                        {
                            "wrapper": "tls"
                        }
                    ],
                    "routes": [
                        {
                            "match": [
                                {
                                    "host": [
                                        "*.example.com"
                                    ]
                                }
                            ],
                            "handle": [
                                {
                                    "handler": "subroute",
                                    "routes": [
                                        {
                                            "handle": [
                                                {
                                                    "body": "OK",
                                                    "handler": "static_response",
                                                    "status_code": 200
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ],
                            "terminal": true
                        }
                    ]
                },
                "srv1": {
                    "listen": [
                        ":80"
                    ],
                    "routes": [
                        {
                            "handle": [
                                {
                                    "body": "INSECURE",
                                    "handler": "static_response",
                                    "status_code": 200
                                }
                            ]
                        }
                    ]
                }
            }
        },
        "layer4": {
            "servers": {
                "srv0": {
                    "listen": [
                        "tcp/:1234"
                    ],
                    "routes": [
                        {
                            "handle": [
                                {
                                    "handler": "close"
                                }
                            ]
                        }
                    ]
                }
            }
        }
    }
}