Caddy doesn't log requests missing Host header

1. The problem I’m having:

Caddy does not log requests that do not contain the Host header.

curl -iH "Host:"

Produces the following output:

HTTP/1.1 400 Bad Request: missing required Host header
Content-Type: text/plain; charset=utf-8
Connection: close

400 Bad Request: missing required Host header

However, looking at the caddy logs for this request, it does not seem to exist:

cat /var/log/caddy/access.log | grep -v "host"

Produces no output.

I want the ability to log requests that do not contain the host header for monitoring purposes, as well as the ability to abort the connection due to it not following standards. This is typically done by bots scanning the internet and I’d like to control the action of the webserver more easily.

2. Error messages and/or full log output:


3. Caddy version:


4. How I installed and ran Caddy:

Caddy is installed via APT

a. System environment:

Ubuntu 22.04.4 LTS system via systemd x64 arch

b. Command:

systemctl start caddy

c. Service/unit/compose file:

Please use the preview pane to ensure it looks nice.

d. My complete Caddy config:

# The Caddyfile is an easy way to configure your Caddy web server.
# Unless the file starts with a global options block, the first
# uncommented line is always the address of your site.
# To use your own domain name (with automatic HTTPS), first make
# sure your domain's A/AAAA DNS records are properly pointed to
# this machine's public IP, then replace ":80" below with your
# domain name.

        email {env.CERT_EMAIL}
        auto_https disable_redirects

(enable-logging) {
        log {
                output file /var/log/caddy/access.log {
                        roll_size 10MiB
                        roll_keep_for 4380h

                format filter {
                        wrap json
                        fields {
                                request>headers>Requesttoken replace SUGMA
                                request>headers>Sec-Websocket-Key delete

(security-header) {
        header {
                # Enable HTTP Strict Transport Security (HSTS)
                Strict-Transport-Security "max-age=31536000;"
                # Enable cross-site filter (XSS) and tell browser to block detected attacks
                X-XSS-Protection "1; mode=block"
                # Disallow the site to be rendered within a frame (clickjacking protection)
                X-Frame-Options "SAMEORIGIN"
                # Prevent search engines from indexing (optional)
                X-Robots-Tag "noindex, nofollow"
                # Disallow sniffing of X-Content-Type-Options
                X-Content-Type-Options "nosniff"
                # Server name removing
                # Remove X-Powered-By (Framework name)
                # Remove Last-Modified because etag is the same, but better opsec
                Referrer-Policy "no-referrer"
                X-Permitted-Cross-Domain-Policy "none"

:80 :443 {
        header {

        import security-header
        import enable-logging

        handle_errors {
                @500-error expression {http.error.status_code} >= 500 && {http.error.status_code} < 600
                handle @500-error {
                        import security-header
                        respond "shit ¯\_(ツ)_/¯"

        @400 header !Host

        handle @400 {

        handle {
                header -server

# Refer to the Caddy docs for more information:

5. Links to relevant resources:

I think this is rejected by the Go stdlib before it reaches Caddy’s routing. We don’t really have control over that. See net/http: allow processing requests without Host header · Issue #44388 · golang/go · GitHub Caddy’s not in the business of accepting invalid HTTP requests, anyway.

What you could do if you really need this is to write a listener wrapper plugin (look at the http_redirect module that’s built into Caddy for inspiration) which reads the HTTP bytes and buffers it before passing it on, and if it’s missing the Host you can do whatever you want with it.

1 Like