Directive #

Directives are the main building blocks inside a Caddyfile site block. If the site address tells Caddy where it should listen for requests, then directives tell Caddy what to do with those requests.

Without directives, Caddy is just an empty server doing nothing. By using one or more directives, you can turn Caddy into a very fast static file server, a robust reverse proxy in front of your application cluster, a PHP server with FastCGI integration, or a layered security gateway filtering access with authentication. Understanding how each major directive works, its writing anatomy, and its configuration options is the most crucial skill for mastering Caddy in production.


Directive Request Processing Order #

When an HTTP request comes in, Caddy processes it through a series of active directives. This processing order follows Caddy’s internal priority rules.

To visualize how a client request flows through various directives, gets forwarded to the backend, and returns as a response, look at the sequence diagram below:

sequenceDiagram
    autonumber
    participant Client as "Client (Browser)"
    participant Caddy as "Caddy Web Server"
    participant Backend as "Upstream App (Port 3000)"

    Client->>Caddy: "GET /admin/users (HTTP Request)"
    note over Caddy: 1. Evaluate Matcher & Directive Order
    note over Caddy: 2. root /var/www (Set directory)
    note over Caddy: 3. rewrite (Modify internal URI if any)
    Caddy->>Client: "4. basicauth (Send 401 Challenge if Not Authenticated)"
    Client->>Caddy: "GET /admin/users (Send credentials)"
    note over Caddy: 5. basicauth (Bcrypt Hash Validation Successful)
    note over Caddy: 6. encode (Prepare gzip/zstd filter)
    Caddy->>Backend: "7. reverse_proxy (Send request with Updated Headers)"
    Backend-->>Caddy: "8. Response (Send HTML/JSON)"
    note over Caddy: 9. header (Add Security Headers & Remove Server header)
    note over Caddy: 10. Compress response via encode (gzip/zstd)
    Caddy-->>Client: "11. Response (Send to Client with TLS Active)"

Directive Writing Anatomy #

The Caddyfile is designed to be very flexible. Directives can be written in three formats depending on the configuration complexity you need:

1. Inline Format (One Line) #

Used for simple instructions that don’t need many parameters or extra configuration.

example.com {
    # Format: directive_name [arguments...]
    root * /var/www/html
    file_server
    encode gzip
}

2. Block Format #

Used when a directive has many custom parameters (subdirectives) that need detailed setup to stay structured and readable.

example.com {
    # Format:
    # directive_name {
    #     subdirective1 argument
    #     subdirective2 argument
    # }
    log {
        output file /var/log/caddy/access.log
        format json
        level INFO
    }
}

3. Mixed Format (Inline & Block) #

Many Caddy directives support inline writing for standard cases, but also accept curly brace blocks if you want advanced tuning.

example.com {
    # Inline: Fast and uses built-in defaults
    encode gzip zstd
    
    # Block: Customize compression level and minimum file size
    encode {
        gzip 6
        zstd
        minimum_length 1024
    }
}

Explanation of 10+ Core Directives #

Here’s an in-depth discussion of the most frequently used directives for building real-world server configurations:

1. root (Setting the Root Folder) #

Sets the working directory path where Caddy looks for static files to serve via file_server or process via php_fastcgi.

# Format: root [matcher] path
root * /var/www/my-app

The * argument is a wildcard request matcher telling Caddy to use this root folder for all request traffic.

2. file_server (Serving Static Files) #

Enables Caddy’s high-performance static web server capability for serving images, HTML files, CSS, JavaScript, or other documents from local disk.

example.com {
    root * /var/www/static
    
    # Enable the file server service with extra options
    file_server {
        # Enable directory listing display when an index file isn't found
        browse
        
        # Hide sensitive files from directory listings and block direct access
        hide .git .env *.config
        
        # Index file search priority order (default: index.html)
        index index.html index.htm
    }
}

3. reverse_proxy (Proxy to Application Backends) #

One of Caddy’s strongest features for forwarding incoming requests to one or more backend application servers (like Node.js, Go, Python, Java, or Docker containers).

example.com {
    # Forward requests to the backend upstream
    reverse_proxy localhost:3000 {
        # Manipulate request headers before sending to the backend
        header_up Host {upstream_hostport}
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        header_up X-Forwarded-Proto {scheme}
        
        # Load Balancing options (if more than one upstream)
        # Caddy supports round_robin, random, ip_hash, etc.
        lb_policy round_robin
        
        # Health checks to detect dead upstreams
        health_uri /health-check
        health_interval 10s
        health_timeout 5s
        
        # Connection timeout tuning
        transport http {
            dial_timeout 5s
            response_header_timeout 30s
        }
    }
}

4. encode (Content Compression) #

Reduces the size of response data sent to the client to save server bandwidth and speed up web page loading.

example.com {
    # Enable gzip and zstd (Caddy picks the best one based on the user's browser)
    encode gzip zstd {
        # Set gzip compression quality (1-9, default: 4)
        gzip 5
        # Set zstd quality
        zstd
        # Don't compress files under 512 bytes (because it's inefficient)
        minimum_length 512
    }
}

5. header (HTTP Header Manipulation) #

Adds, modifies, or removes HTTP headers in responses sent to the user’s browser. This is the main tool for enforcing security policies (security headers).

example.com {
    header {
        # Add a new header (or replace it if it already exists)
        Strict-Transport-Security "max-age=31536000; includeSubDomains"
        X-Frame-Options "SAMEORIGIN"
        X-Content-Type-Options "nosniff"
        
        # Remove Caddy/backend default headers for server info security
        -Server
        -X-Powered-By
        
        # Add a new value to an existing header without overwriting it (append)
        +Cache-Control "public"
    }
}

6. redir (External URL Redirect) #

Sends an HTTP redirect status (like 301 for permanent or 302 for temporary) back to the client browser so they access a new URL.

# Redirect traffic permanently
redir /site-map /sitemap.xml permanent

# Redirect while preserving query parameters
redir /products/* /new-items/{path} 308

7. rewrite (Internal URI Rewriting) #

Unlike redir, rewrite silently changes the request URI path inside the Caddy engine without telling the client browser. The browser still sees the original URL in its address bar.

example.com {
    # If a user accesses /help, Caddy processes it as /support/faq.html in the background
    rewrite /help /support/faq.html
    
    # Perfect for supporting Single Page Application (SPA) architectures like React/Vue/Angular
    # If the file isn't found on disk, route the request internally to index.html
    @notFile {
        not file {path}
        not path /api/*
    }
    rewrite @notFile /index.html
    
    file_server
}

The Fundamental Difference: redir vs rewrite #

  • redir (External Redirect): Caddy sends an HTTP 301/302/308 response to the client browser. The browser then reconnects to the new URL. This causes two HTTP round-trips over the network. The URL in the browser changes.
  • rewrite (Internal Redirect): Caddy directly routes the request to another handler internally. Only one HTTP round-trip happens. The URL in the client browser stays the same.

8. respond (Sending Direct Responses) #

Sends plain text, HTML, or JSON responses instantly to the client without reading a file from disk or contacting a backend server.

# Send plain text with status code 200
respond "Server OK" 200

# Send JSON data for a health endpoint
@health path /health
respond @health `{"status":"healthy","database":"connected"}` 200 {
    close
}

9. basicauth (HTTP Basic Authentication) #

Secures site paths with a username and password protection layer. Passwords must be stored in bcrypt hash format.

example.com {
    # Restrict access to the /admin/ folder
    basicauth /admin/* {
        # Username: admin, Password: password123 (hash generated using the Caddy CLI command)
        admin $2a$14$yR41x7U715c0mpr3ss10n.hashexamplevalue
    }
    
    reverse_proxy localhost:3000
}

You can generate the password hash with the following terminal command:

caddy hash-password --plaintext "password123"

10. tls (Certificate & Encryption Configuration) #

Used to customize Caddy’s TLS behavior, such as setting the ACME admin email, loading paid SSL certificates manually, or setting the minimum protocol.

example.com {
    # Use a paid or custom SSL certificate you already own
    tls /etc/ssl/certs/site.pem /etc/ssl/private/site.key
    
    # Or configure modern encryption options
    tls {
        # Only allow TLS 1.3 for maximum security
        protocols tls1.3
        
        # Set a custom certificate provider (e.g., ZeroSSL)
        ca https://acme.zerossl.com/v2/DV90
    }
}

11. log (System Access Log) #

Configures incoming request data logging (access logs) for traffic analytics or security audits.

example.com {
    log {
        # Write logs to a physical file on the system
        output file /var/log/caddy/access.log {
            roll_size 100mb    # Rotate the log file after reaching 100MB
            roll_keep 10       # Keep a maximum of 10 old log files
            roll_keep_for 720h # Keep old logs for 30 days (720 hours)
        }
        # Use JSON format so it's easy to read by Elasticsearch or Logstash
        format json
        level INFO
    }
    
    reverse_proxy localhost:3000
}

12. php_fastcgi (PHP Processing) #

A special directive that makes PHP-based web configuration (like WordPress, Laravel, or Drupal) very easy by automatically forwarding .php file requests to a local PHP-FPM service.

example.com {
    root * /var/www/my-laravel-app/public
    
    # Forward PHP requests to the PHP-FPM unix socket
    php_fastcgi unix//run/php/php8.2-fpm.sock {
        # Set internal PHP environment variables if needed
        env PHP_ADMIN_VALUE "expose_php=Off \n memory_limit=256M"
    }
    
    file_server
}

Directive Combination Patterns (Production Use Cases) #

In real production environments, you almost always use combinations of the directives above. Here are ready-to-use configuration templates for common use cases:

1. Single Page Application (SPA) with Maximum Security #

This template suits serving React, Vue, Svelte, or Angular frontends hosted on a local static server.

app.mydomain.com {
    root * /var/www/dist
    
    # Enable modern compression
    encode gzip zstd
    
    # Apply a strict security header set
    header {
        Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
        X-Frame-Options "DENY"
        X-Content-Type-Options "nosniff"
        Referrer-Policy "strict-origin-when-cross-origin"
        Content-Security-Policy "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline';"
        -Server
        -X-Powered-By
    }
    
    # SPA Router fallback: route internal route requests to index.html
    @notFile {
        not file {path}
    }
    rewrite @notFile /index.html
    
    # Enable static file serving
    file_server
}

2. API Gateway with CORS Configuration #

When Caddy acts as the front-line gateway for your microservices API cluster and must serve cross-origin requests (Cross-Origin Resource Sharing).

api.mydomain.com {
    # Set CORS headers globally for this block
    header {
        Access-Control-Allow-Origin "https://app.mydomain.com"
        Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
        Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With"
        Access-Control-Allow-Credentials "true"
        Access-Control-Max-Age "3600"
    }
    
    # Intercept browser pre-flight OPTIONS requests instantly
    @options method OPTIONS
    respond @options "" 204
    
    # Forward the original request to the upstream API server
    reverse_proxy localhost:8080 {
        # Add the client's real IP tracking header
        header_up X-Real-IP {remote_host}
    }
    
    # JSON log configuration
    log {
        output file /var/log/caddy/api_access.log {
            roll_size 50mb
        }
        format json
    }
}

3. WordPress Production Setup #

WordPress needs special handling because it combines static assets (images, CSS, JS), PHP processing, and blocking sensitive configuration files.

blog.mydomain.com {
    root * /var/www/wordpress
    
    # Response compression
    encode gzip zstd
    
    # Block access to sensitive WordPress files for security
    @blockSens {
        path /wp-config.php
        path /xmlrpc.php
        path /.env
        path /.git/*
    }
    respond @blockSens "Access Denied" 403
    
    # FastCGI integration to process PHP files
    php_fastcgi unix//run/php/php8.2-fpm.sock
    
    # Serve static assets, hide the default Apache htaccess file if present
    file_server {
        hide .htaccess
    }
}

Summary #

  • Directives are the Caddyfile’s operational instruction blocks placed inside a site block to manipulate requests and responses.
  • reverse_proxy is the backbone of backend application integration, while file_server is the local static file serving engine.
  • rewrite works internally inside the Caddy server, while redir triggers an external redirect by sending an HTTP 3xx response to the client browser.
  • PHP application integration is easily done with a single php_fastcgi directive line pointed at the PHP-FPM socket.
  • header helps secure your website by removing built-in server info (-Server) and injecting modern security headers.
  • Passwords for basicauth protection must be converted to a bcrypt encryption hash using the caddy hash-password CLI command.

← Previous: Site Address   Next: Matcher →

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