Global Options #

When configuring the Caddyfile, most of your time goes into setting site-specific behavior or API routes inside site blocks. However, many operational parameters aren’t meant for a particular site; they apply to the entire running Caddy server instance.

These global settings include setting the email address for SSL certificate registration with Let’s Encrypt globally, changing Admin API behavior, enabling troubleshooting mode (debug logging), defining custom default HTTP/HTTPS ports, and configuring synchronized certificate storage across a server cluster (cluster storage). In the Caddyfile, all these instance settings are collected in a special block at the very top of the file, called the Global Options Block.


On-Demand TLS Certificate Issuance Flow via the Ask API #

One of the global features that most requires external coordination is On-Demand TLS. Here, Caddy interacts with your internal API to check the validity of a new domain before requesting an SSL certificate from the certificate authority (CA).

To visualize how this dynamic certificate coordination process works, look at the sequence diagram below:

sequenceDiagram
    autonumber
    participant Client as "Client (Browser)"
    participant Caddy as "Caddy Web Server"
    participant API as "Ask API (localhost:5000)"
    participant CA as "ACME CA (Let's Encrypt)"

    Client->>Caddy: "SSL Client Hello (domain: app.customer.com)"
    note over Caddy: Caddy detects a new domain & on_demand option is active
    Caddy->>API: "GET /validate-domain?domain=app.customer.com"
    API-->>Caddy: "HTTP 200 OK (Domain Valid & Allowed)"
    Caddy->>CA: "Request dynamic certificate (ACME Challenge)"
    CA-->>Caddy: "Send Validation Challenge (DNS/HTTP-01)"
    Caddy-->>CA: "Complete Challenge & Obtain SSL Certificate"
    note over Caddy: Caddy stores the certificate in Storage & loads it into memory
    Caddy-->>Client: "SSL Server Hello (HTTPS Connection Successful)"

Global Options Block Syntax #

The global options block syntax is marked by a pair of curly braces { } without any domain name or site address in front. This block must be placed at the very top of the Caddyfile, before any site block is declared.

# Global Options Block — Must be on the first line
{
    # Global options go here
    email [email protected]
    debug
}

# Site Block 1
example.com {
    file_server
}

# Site Block 2
api.example.com {
    reverse_proxy localhost:8080
}

If you put the global options block in the middle or lower part of the file after other site blocks, the Caddyfile parser fails (syntax error) because it thinks the block is an invalid site block declaration without an address.


Most Critical Global Options in Production (Analytical Table) #

To make sure your Caddy instance is secure, reliable, and ready for high traffic, several global options must be configured carefully. The table below summarizes the impact of these custom parameters:

Global OptionMain UseImpact if Ignored (Default)Production Recommendation
emailSSL certificate registration with the ACME CA.No notification if SSL renewal fails.Must be filled with the operations/DevOps team email.
trusted_proxiesIdentifies the client’s real IP behind proxies.Client IP reads as the Load Balancer IP.Must be configured with the Load Balancer/CDN IP block.
storageCertificate & private key storage location.Certificates stored on local disk (not synchronized).Use Redis or Consul if you have more than one server.
acme_ca (staging)Opens the testing ACME server.Directly requests real SSL and risks hitting limits.Use Let’s Encrypt Staging during integration/testing phases.
on_demand_tlsVerifies SSL issuance for SaaS domains.Vulnerable to disk exhaustion and SSL limit attacks.Must use a secure ask endpoint.

The Most Commonly Used Global Options Explained #

Here’s a detailed explanation of the instance configuration parameters you can set inside the global options block:

1. email (ACME Certificate Authority Contact) #

Sets the administrative email address registered with Let’s Encrypt or ZeroSSL when Caddy requests SSL certificates.

[!TIP] Always configure the email parameter on production servers. Without an email, Let’s Encrypt has no way to reach you if an automatic renewal failure occurs that could expire your SSL certificate, or to send critical security announcements about certificate infrastructure.

2. admin (Controlling Caddy’s Admin API) #

Caddy has an HTTP-based administrative API (active by default on localhost:2019) that lets you reload configuration without downtime, view server status, or dynamically stop a Caddy instance.

{
    # Option A: Turn off the Admin API entirely for maximum security
    admin off
    
    # Option B: Change the admin API listen port or interface
    admin localhost:2025
    
    # Option C: Detailed security configuration
    admin localhost:2019 {
        # Only allow requests with the following Origin header
        origins localhost:2019 127.0.0.1:2019
        enforce_origin
    }
}

3. debug (Detailed Tracing Mode) #

Enables verbose logs to help you trace HTTP request flows, TLS handshake processes, or connection issues to upstream backend servers.

{
    # Enable debug mode
    debug
}

Don’t leave this mode active on a production server serving heavy traffic, because the log volume generated is enormous and can fill disk storage capacity in a short time.

4. log (Global System Log Configuration) #

Sets the logging of Caddy server startup activity, certification processes, and internal instance failure logs (different from the per-site access log configured inside site blocks).

{
    log {
        # Write global logs to a physical file
        output file /var/log/caddy/system.log {
            roll_size 50mb
            roll_keep 3
        }
        # Use JSON format for easy parsing by log parser tools
        format json
        level WARN # Only record Warning and Error level logs
    }
}

5. http_port & https_port (Custom Default Ports) #

By default, Caddy runs on the standard web ports: 80 for HTTP and 443 for HTTPS. You can change these defaults if Caddy runs in a non-root environment (no bind permission for ports below 1024) or behind an external load balancer.

{
    http_port 8080
    https_port 8443
}

# Now the domains below automatically listen on port 8443 for HTTPS
# and port 8080 for HTTP redirect
myplatform.com {
    file_server
}

Global TLS & ACME Settings #

If you have many site blocks and want to align how TLS certificates are obtained and validated, you can set global TLS rules:

1. acme_ca (Custom Certificate Authority) #

Switches Caddy from the default Let’s Encrypt to another ACME certificate provider.

{
    # Option A: Use Let's Encrypt Staging (Highly recommended for testing)
    # to avoid hitting production Let's Encrypt rate-limit blocks
    acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
    
    # Option B: Use ZeroSSL
    # acme_ca https://acme.zerossl.com/v2/DV90
}

2. acme_dns (Global DNS Provider) #

Sets the DNS challenge module globally for all domains that need certificate validation via DNS (like wildcard domains).

{
    acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}

3. local_certs (Internal CA for All Sites) #

Forces Caddy to use its own internal CA to issue SSL certificates for all domains instead of requesting public certificates from Let’s Encrypt. Very useful for corporate intranets or closed development labs.

{
    local_certs
}

HTTP Server Configuration & Trusted Proxies #

Inside global options, you can set detailed behavior of Caddy’s HTTP server engine through the servers block:

{
    servers {
        # Restrict the active TLS protocols
        protocols h1 h2 h3 # Enable HTTP/1.1, HTTP/2, and HTTP/3
        
        # Set global connection timeouts
        timeouts {
            read_body   30s
            read_header 10s
            write       60s
            idle        120s
        }
        
        # Trusted Proxies configuration (VERY IMPORTANT!)
        # If Caddy sits behind a CDN (Cloudflare) or Load Balancer (AWS ALB, Nginx)
        trusted_proxies static 10.0.0.0/8 192.168.1.0/24
        
        # If you want to change which headers are used to identify the client IP
        client_ip_headers CF-Connecting-IP X-Forwarded-For
    }
}

Why Is trusted_proxies Crucial? #

If your Caddy server sits behind a Load Balancer, every HTTP request arriving at Caddy appears to come from the Load Balancer’s IP address, not the client’s real IP. As a result:

  1. Your traffic logs get filled with the Load Balancer IP.
  2. IP-based access restrictions (IP Whitelisting or Geoblocking) don’t work because the detected IP is wrong.

By defining your Load Balancer’s IP addresses in the trusted_proxies parameter, you tell Caddy to trust the X-Forwarded-For header sent by that Load Balancer. Caddy then extracts the client’s real IP from that header and writes it to the {remote_host} variable.

Here’s an example configuration to trust Cloudflare’s IP ranges manually:

{
    servers {
        # Cloudflare IPv4 IP Ranges (partial, as illustration)
        trusted_proxies static {
            173.245.48.0/20
            103.21.244.0/22
            103.22.200.0/22
            103.31.4.0/22
            141.101.64.0/18
            108.162.192.0/18
            190.93.240.0/20
            188.114.96.0/20
            197.234.240.0/22
            198.41.128.0/17
        }
    }
}

Setting Auto HTTPS Behavior (Disable Options) #

Caddy aggressively triggers automatic TLS handling to ensure a secure web. However, in scenarios where TLS is terminated at the outermost Load Balancer level, your Caddy server only acts as a plain HTTP receiver. Caddy provides fine-grained controls to adjust this:

1. auto_https off #

Disables all of Caddy’s HTTPS automation capabilities, including Let’s Encrypt certificate issuance and the port 80 redirect. Caddy behaves like a traditional HTTP web server.

{
    auto_https off
}

2. auto_https disable_redirects #

TLS certificates are still created and enabled on port 443, but Caddy won’t create an automatic redirect server on port 80. Users typing an http:// address won’t be automatically redirected to https://. Very useful if you want to handle port 80 manually for special routes.

{
    auto_https disable_redirects
}

3. auto_https disable_certs #

Caddy still enables HTTP-to-HTTPS traffic redirects but doesn’t try to create public SSL certificates. This option is used when you want Caddy to serve HTTPS using certificates you load manually in site blocks.

{
    auto_https disable_certs
}

Cluster Storage (Synchronized Storage) #

By default, Caddy stores SSL certificate files and private keys in the server’s local filesystem. The default storage path varies by OS, like /var/lib/caddy/.local/share/caddy on Linux systemd, or ~/.local/share/caddy for standard users.

However, if you run Caddy in a multi-server environment (cluster / load-balanced pool), every Caddy server must have access to the same certificates so they don’t repeatedly request new certificates from Let’s Encrypt, which could trigger a rate-limit block.

Caddy manages cross-server conflicts by implementing distributed locks on the storage in use. You can configure distributed storage using the Redis or Consul storage plugins:

{
    # Option A: Synchronize using Redis Cluster
    storage redis {
        host     "redis-cluster.internal"
        port     6379
        password {env.REDIS_PASSWORD}
        key_prefix "caddy_certs"
    }
    
    # Option B: Synchronize using HashiCorp Consul
    # storage consul {
    #     address "consul.internal:8500"
    #     prefix "caddy"
    # }
}

Complete Configuration Examples #

1. Production Server Template (Production Setup) #

{
    # Main email for SSL notifications
    email [email protected]
    
    # Caddy system global logs
    log {
        output file /var/log/caddy/caddy_system.log {
            roll_size 100mb
            roll_keep 7
        }
        format json
        level WARN # Only record Warning & Error
    }
    
    # HTTP Engine tuning & Load Balancer Trust
    servers {
        protocols h1 h2 h3
        trusted_proxies static 10.0.0.0/8 172.16.0.0/12
        timeouts {
            read_header 5s
            idle 120s
        }
    }
    
    # Cluster Storage configuration using Redis
    storage redis {
        host "redis-prod.company.lan"
        password {env.REDIS_ACCESS_PASSWORD}
    }
}

# Our production site block
myfirm.com {
    reverse_proxy localhost:3000
}

2. Test Machine Template (Development Setup) #

{
    # Run on non-privileged ports (no sudo/root needed)
    http_port 8080
    https_port 8443
    
    # Use the internal CA (free local certificates without internet)
    local_certs
    
    # Enable debug logging to monitor detailed requests in the terminal
    debug
    
    # Keep the admin API configuration on the default port
    admin localhost:2019
}

# Our local development site block
localhost:8443 {
    tls internal
    reverse_proxy localhost:3000
}

Summary #

  • The Global Options Block is written with curly braces {} without a domain name, and must be placed on the first line of the Caddyfile.
  • Configuring email globally is very important so you receive Let’s Encrypt notifications if there’s a failure in automatic SSL certificate renewal.
  • The trusted_proxies parameter under the servers block must be configured if Caddy runs behind an external load balancer or CDN to ensure the client IP is detected accurately.
  • Use the acme_ca Let’s Encrypt Staging endpoint when testing new configurations to avoid hitting production Let’s Encrypt certificate rate limits.
  • Configure storage backed by a distributed database like Redis if your Caddy servers run in a multi-server cluster environment behind a load balancer.

← Previous: Snippet & Import   Next: Automatic HTTPS →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact