# Caddy Web Server — User Guide

## 1. Quick Start

Caddy is an extensible server platform written in Go that serves HTTP, manages TLS certificates automatically, and acts as a reverse proxy. It eliminates the complexity of HTTPS configuration by obtaining and renewing certificates from Let's Encrypt and ZeroSSL without any manual setup.

**Key features:**

- **Automatic HTTPS** — TLS certificates obtained and renewed automatically via ACME
- **Caddyfile** — human-readable configuration format that adapts to native JSON
- **Reverse proxy** — built-in load balancing, health checks, retries, and streaming
- **File server** — static file serving with directory browsing
- **HTTP/1.1, HTTP/2, and HTTP/3** (QUIC) support
- **Admin API** — live configuration changes without downtime via REST API on `localhost:2019`
- **Modular architecture** — extend with plugins at compile time
- **Cross-platform** — Linux, macOS, Windows, FreeBSD (amd64, arm64, arm, s390x, ppc64le, riscv64)

**Prerequisites:** None for pre-built binaries. Go 1.25+ for building from source.

**Install and run in 30 seconds:**

```bash
# Install (Debian/Ubuntu)
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install caddy
```

**Minimal Caddyfile (serve current directory over HTTPS):**

```text
localhost {
    file_server browse
}
```

```bash
caddy run
```

```text
... INFO  serving initial configuration
```

Visit `https://localhost` — Caddy automatically provisions a locally-trusted TLS certificate.

> [!TIP]
> For the fastest start, just run `caddy file-server --browse` in any directory — no config file needed. Caddy will serve files from the current directory on port 2015.

**What to try next:**

- Set up a reverse proxy: see "Reverse Proxy" in Core Usage
- Configure automatic HTTPS for a domain: see "Automatic HTTPS" in Core Usage
- Learn the Caddyfile syntax: see Configuration
- See the full Admin Guide for architecture, deployment, and security details

## 2. Installation

### System Requirements

| Requirement | Minimum | Recommended |
|---|---|---|
| OS | Linux, macOS, Windows, FreeBSD | Linux (systemd) |
| Architecture | amd64, arm64, armv5/v6/v7, s390x, ppc64le, riscv64 | amd64 or arm64 |
| Go (source builds) | 1.25.0 | Latest stable |
| Memory | 64 MB | 256 MB+ |
| Ports | 80 (HTTP), 443 (HTTPS) for automatic TLS | Also 443/UDP for HTTP/3 |

### Installation Methods

=== "Debian/Ubuntu (APT)"
    ```bash
    sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
    curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
    curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
    sudo apt update
    sudo apt install caddy
    ```

=== "Fedora/RHEL (DNF)"
    ```bash
    dnf install 'dnf-command(copr)'
    dnf copr enable @caddy/caddy
    dnf install caddy
    ```

=== "macOS (Homebrew)"
    ```bash
    brew install caddy
    ```

=== "Windows (Scoop)"
    ```powershell
    scoop install caddy
    ```

=== "Docker"
    ```bash
    docker run -d -p 80:80 -p 443:443 -p 443:443/udp \
      -v caddy_data:/data -v caddy_config:/config \
      caddy:latest
    ```

=== "From Source"
    ```bash
    git clone https://github.com/caddyserver/caddy.git
    cd caddy/cmd/caddy
    go build
    ```

> [!IMPORTANT]
> On Linux, Caddy needs permission to bind to ports 80 and 443. If not running as root, use: `sudo setcap cap_net_bind_service=+ep $(which caddy)`. Remember to re-run this after upgrading the binary.

### Post-Install Verification

```bash
caddy version
```

```text
v2.11.1 h1:abc123...
```

```bash
caddy environ
```

```text
caddy.HomeDir=/root
caddy.AppDataDir=/root/.local/share/caddy
caddy.AppConfigDir=/root/.config/caddy
caddy.ConfigAutosavePath=/root/.config/caddy/autosave.json
caddy.Version=v2.11.1
runtime.GOOS=linux
runtime.GOARCH=amd64
...
```

> [!TIP]
> The `caddy environ` command shows exactly which directories Caddy uses for data and configuration on your system. This is invaluable for debugging path issues.

### Building with Plugins (xcaddy)

Caddy's standard distribution covers common use cases, but you can add third-party modules using `xcaddy`:

```bash
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
xcaddy build --with github.com/caddy-dns/cloudflare
```

The `caddy add-package` and `caddy remove-package` commands can also add/remove modules to an existing binary without xcaddy:

```bash
caddy add-package github.com/caddy-dns/cloudflare
caddy remove-package github.com/caddy-dns/cloudflare
```

> [!WARNING]
> The `upgrade`, `add-package`, and `remove-package` commands are experimental and replace the running binary. Always keep a backup. Use `--keep-backup` to preserve the old binary automatically.

## 3. Configuration

Caddy accepts configuration in two formats: **Caddyfile** (human-friendly) and **native JSON** (machine-friendly). The Caddyfile is automatically adapted to JSON before being applied.

### Caddyfile Basics

A Caddyfile consists of **site blocks** that define how to handle requests for specific addresses:

```text
# Serve static files for example.com
example.com {
    root * /var/www/html
    file_server
}

# Reverse proxy for api.example.com
api.example.com {
    reverse_proxy localhost:8080
}
```

**Key syntax rules:**

| Element | Syntax | Example |
|---|---|---|
| Site address | Domain, IP, or port | `example.com`, `localhost:8080`, `:443` |
| Directive | Keyword inside site block | `file_server`, `reverse_proxy`, `respond` |
| Matcher | `@name` definition or inline path | `@api path /api/*` |
| Placeholder | `{http.request.*}` or shorthand `{host}` | `respond "Host: {host}"` |
| Global options | Block at the top with `{ }` | `{ admin off }` |
| Snippet | `(name) { ... }` | `(common) { encode gzip }` |
| Import | `import <file_or_snippet>` | `import common` |
| Environment variable | `{env.VAR}` or `{$VAR}` | `{env.SITE_ROOT}` |
| Comment | `#` | `# This is a comment` |
| Heredoc | `<<TAG ... TAG` | Multi-line values |

### Global Options Block

The global options block must appear first in the Caddyfile (before any site blocks) and has no site address:

```text
{
    # TLS email for ACME account
    email [email removed]

    # Admin API settings
    admin localhost:2019

    # Default bind address
    default_bind 0.0.0.0

    # Logging
    debug

    # ACME settings
    acme_dns <provider> ...

    # Skip installing the CA root in system trust store
    skip_install_trust

    # HTTP and HTTPS ports
    http_port 80
    https_port 443

    # Graceful shutdown period
    grace_period 10s

    # Server options for specific listeners
    servers :443 {
        protocols h1 h2 h3
    }
}
```

> [!NOTE]
> Not all options are listed above. See the Admin Guide for a complete configuration reference of all global options and server settings.

### Using JSON Configuration

Caddy's native format is JSON. You can view the JSON equivalent of any Caddyfile with `caddy adapt`:

```bash
caddy adapt --config Caddyfile --pretty
```

You can also configure Caddy entirely via the admin API:

```bash
curl localhost:2019/load \
  -H "Content-Type: application/json" \
  -d @caddy.json
```

### Environment Files

Caddy can load environment variables from `.env` files using the `--envfile` flag:

```bash
caddy run --envfile .env
```

The env file uses `KEY=VALUE` format. Lines starting with `#` are comments. Existing environment variables are not overwritten.

## 4. Core Usage

### Running Caddy

#### Foreground (Recommended)

```bash
caddy run --config Caddyfile
```

Caddy runs in the foreground, blocking the terminal. Use Ctrl+C to stop. This is the recommended way to run Caddy, especially with service managers like systemd.

#### Background

```bash
caddy start --config Caddyfile
```

```text
Successfully started Caddy (pid=12345) - Caddy is running in the background
```

```bash
caddy stop
```

> [!WARNING]
> On Windows, `caddy start` keeps the child process attached to the terminal. Closing the terminal forcefully stops Caddy. Prefer `caddy run` with a service manager instead.

#### Reloading Configuration

```bash
caddy reload --config Caddyfile
```

This sends the new configuration to the running instance via the admin API. The reload is graceful — active connections are not dropped.

Use `--force` to force a reload even if the config hasn't changed:

```bash
caddy reload --config Caddyfile --force
```

#### Resuming Previous Configuration

```bash
caddy run --resume
```

Caddy automatically saves its current configuration to `autosave.json` in the config directory. The `--resume` flag loads this file on startup instead of the specified config file.

#### Watching for Config Changes

```bash
caddy run --config Caddyfile --watch
```

> [!CAUTION]
> The `--watch` flag polls the config file every second and automatically reloads on changes. This is intended for **local development only** — it can cause unintended config changes in production.

### Automatic HTTPS

Caddy automatically obtains and renews TLS certificates for any site address that looks like a hostname:

```text
example.com {
    respond "Hello, HTTPS!"
}
```

When Caddy starts with this config, it will:

1. Obtain a certificate from Let's Encrypt (or ZeroSSL) via ACME
2. Set up HTTP → HTTPS redirects on port 80
3. Enable HTTP/2 and HTTP/3
4. Renew the certificate before it expires

**No extra configuration needed** — this is the default behavior.

> [!NOTE]
> Caddy automatically agrees to the ACME CA's terms of service when using automatic HTTPS. For domains using ACME, ports 80 and 443 must be publicly reachable (or use DNS challenge for private networks).

**Disable automatic HTTPS per-site:**

```text
http://example.com {
    respond "Plain HTTP only"
}
```

Prefixing with `http://` disables automatic HTTPS for that site. You can also disable it globally:

```text
{
    auto_https off
}
```

**Using local certificates for development:**

```text
localhost {
    respond "Hello from localhost!"
}
```

For `localhost` and other local addresses, Caddy generates a self-signed certificate from its own internal CA and installs it in the system trust store (unless `skip_install_trust` is set).

### Reverse Proxy

```text
example.com {
    reverse_proxy localhost:8080
}
```

**Multiple backends with load balancing:**

```text
example.com {
    reverse_proxy node1:8080 node2:8080 node3:8080 {
        lb_policy round_robin
    }
}
```

**With health checks:**

```text
example.com {
    reverse_proxy backend:8080 {
        health_uri /health
        health_interval 30s
        health_timeout 5s
    }
}
```

**Proxying to Unix sockets:**

```text
example.com {
    reverse_proxy unix//var/run/app.sock
}
```

**Header manipulation:**

```text
example.com {
    reverse_proxy backend:8080 {
        header_up Host {upstream_hostport}
        header_up X-Real-IP {remote_host}
        header_down -Server
    }
}
```

> [!TIP]
> Caddy automatically adds `X-Forwarded-For`, `X-Forwarded-Proto`, and `X-Forwarded-Host` headers to proxied requests. You don't need to configure these manually.

### File Server

**Serve static files:**

```text
example.com {
    root * /var/www/html
    file_server
}
```

**Enable directory browsing:**

```text
example.com {
    root * /var/www/files
    file_server browse
}
```

**Quick file server from the command line:**

```bash
caddy file-server --root /var/www/html --listen :8080 --browse
```

> [!NOTE]
> The `caddy file-server` convenience command disables the admin API since it configures entirely from command-line flags.

**Serve with SPA fallback (try_files):**

```text
example.com {
    root * /var/www/app
    try_files {path} /index.html
    file_server
}
```

### Respond (Quick Testing)

For quick responses without a file server or proxy:

```text
example.com {
    respond "Hello, world!" 200
}
```

**From the command line:**

```bash
caddy respond --listen :8080 "Service is up" 200
```

### Request Matchers

Matchers control which requests a directive applies to:

```text
example.com {
    # Named matcher
    @api {
        path /api/*
        method GET POST
    }
    reverse_proxy @api backend:8080

    # Path matcher shorthand
    reverse_proxy /ws/* websocket-server:9090

    # All other requests
    file_server
}
```

**Common matcher types:**

| Matcher | Syntax | Description |
|---|---|---|
| `path` | `path /api/*` | Match request path |
| `method` | `method GET POST` | Match HTTP method |
| `host` | `host example.com` | Match hostname |
| `header` | `header Content-Type application/json` | Match request header |
| `remote_ip` | `remote_ip 192.168.1.0/24` | Match client IP |
| `protocol` | `protocol https` | Match protocol |
| `expression` | `expression {http.request.uri}.contains("/api")` | CEL expression |
| `file` | `file {path}` | Match if file exists |
| `not` | `not path /secret/*` | Negate a matcher |

### Request and Response Headers

```text
example.com {
    header {
        # Add/set headers
        X-Frame-Options DENY
        X-Content-Type-Options nosniff
        Strict-Transport-Security "max-age=31536000; includeSubDomains"

        # Remove headers
        -Server
        -X-Powered-By
    }
    file_server
}
```

### Encoding (Compression)

```text
example.com {
    encode gzip zstd
    file_server
}
```

Caddy supports gzip, zstd (Zstandard), and brotli compression. It also supports serving pre-compressed files (`.gz`, `.br`, `.zst` extensions).

### Rewrite and Redirect

```text
example.com {
    # Internal rewrite (URL changes internally, not visible to client)
    rewrite /old-path /new-path

    # External redirect (client sees 301/302)
    redir /old-page /new-page 301

    # URI manipulation
    uri strip_prefix /api
    uri replace /v1/ /v2/
}
```

### Templates

Caddy can process templates in served files:

```text
example.com {
    templates
    file_server
}
```

Templates use Go's `text/template` syntax and have access to request information, Markdown rendering (via Goldmark), and more.

### Logging

```text
example.com {
    log {
        output file /var/log/caddy/access.log {
            roll_size 100MiB
            roll_keep 5
            roll_keep_for 720h
        }
        format json
        level INFO
    }
    file_server
}
```

### TLS Configuration

For fine-grained TLS control beyond automatic HTTPS:

```text
example.com {
    tls cert.pem key.pem
}
```

**Using internal (self-signed) certificates:**

```text
internal.example.com {
    tls internal
}
```

**Client certificate authentication:**

```text
example.com {
    tls {
        client_auth {
            mode require_and_verify
            trust_pool file {
                pem_file /path/to/client-ca.pem
            }
        }
    }
}
```

### Basic Authentication

```text
example.com {
    basicauth /admin/* {
        admin $2a$14$hashOfPassword
    }
    file_server
}
```

Generate password hashes with:

```bash
caddy hash-password
```

> [!IMPORTANT]
> The `hash-password` command is provided by the `caddyauth` module included in the standard distribution. Passwords are hashed using bcrypt by default.

### PHP with FastCGI

```text
example.com {
    root * /var/www/html
    php_fastcgi localhost:9000
}
```

The `php_fastcgi` directive is a shortcut that configures `try_files`, `reverse_proxy` with FastCGI transport, and appropriate index handling for PHP.

### HTTP/3 (QUIC)

HTTP/3 is enabled by default when HTTPS is active. No additional configuration is needed. Caddy listens on UDP port 443 for QUIC connections and advertises it via the `Alt-Svc` header.

### Metrics (Prometheus)

```text
{
    servers {
        metrics
    }
}
```

Caddy exports Prometheus-compatible metrics at the admin API endpoint `/metrics`.

## 5. Common Tasks

### Task: Serve a Static Website with HTTPS

**Goal:** Serve a static site at `example.com` with automatic HTTPS.

**Prerequisites:** DNS A record pointing `example.com` to your server IP; ports 80 and 443 open.

**Steps:**

1. Create the Caddyfile:
    ```text
    example.com {
        root * /var/www/example.com
        file_server
    }
    ```

2. Start Caddy:
    ```bash
    caddy run --config Caddyfile
    ```

3. Verify:
    ```bash
    curl -I https://example.com
    ```

**If it fails:** Check that DNS resolves correctly and that ports 80/443 are not blocked by a firewall.

### Task: Set Up a Reverse Proxy with Load Balancing

**Goal:** Distribute traffic across multiple backend servers.

**Steps:**

1. Create the Caddyfile:
    ```text
    example.com {
        reverse_proxy node1:3000 node2:3000 {
            lb_policy least_conn
            health_uri /health
            health_interval 10s
            fail_duration 30s
        }
    }
    ```

2. Reload:
    ```bash
    caddy reload --config Caddyfile
    ```

### Task: Redirect www to Non-www

```text
www.example.com {
    redir https://example.com{uri} permanent
}

example.com {
    respond "Hello!"
}
```

### Task: Serve Multiple Sites

```text
site1.example.com {
    root * /var/www/site1
    file_server
}

site2.example.com {
    root * /var/www/site2
    file_server
}
```

Each site automatically gets its own TLS certificate.

### Task: Restrict Access by IP

```text
example.com {
    @blocked remote_ip 10.0.0.0/8
    respond @blocked "Forbidden" 403

    reverse_proxy backend:8080
}
```

### Task: Use Caddy as a Pure HTTPS Terminator

```text
example.com {
    reverse_proxy http://internal-service:8080
}
```

Caddy handles TLS termination and proxies plaintext HTTP to the backend.

### Task: Use the Admin API to Change Config at Runtime

```bash
# View current configuration
curl localhost:2019/config/

# Update a specific path
curl -X PATCH localhost:2019/config/apps/http/servers/srv0/routes \
  -H "Content-Type: application/json" \
  -d '[{"handle":[{"handler":"static_response","body":"Updated!"}]}]'

# Load an entirely new configuration
curl localhost:2019/load \
  -H "Content-Type: application/json" \
  -d @new-config.json

# Stop Caddy
curl -X POST localhost:2019/stop
```

> [!TIP]
> The admin API supports `GET`, `POST`, `PUT`, `PATCH`, and `DELETE` methods on `/config/` paths, allowing fine-grained updates to any part of the running configuration without full reloads.

### Task: Export and Import TLS Storage

```bash
# Export certificates and keys
caddy storage export --config Caddyfile --output backup.tar

# Import to a new instance
caddy storage import --config Caddyfile --input backup.tar

# Pipe between instances
caddy storage export --config old.json --output - | \
  caddy storage import --config new.json --input -
```

> [!WARNING]
> The `storage` command is experimental. When using file system storage, run the command as the user that owns the storage directory.

### Task: Validate Your Configuration

```bash
caddy validate --config Caddyfile
```

```text
Valid configuration
```

### Task: Format Your Caddyfile

```bash
# Preview formatted output
caddy fmt Caddyfile

# Overwrite in place
caddy fmt --overwrite Caddyfile

# Show diff
caddy fmt --diff Caddyfile
```

### Task: Set Up a Development Server with Auto-Reload

```bash
caddy run --config Caddyfile --watch
```

Caddy polls the Caddyfile every second and reloads automatically on changes.

### Task: Run Caddy as a systemd Service

After installing via the package manager, Caddy comes with systemd units:

```bash
sudo systemctl enable caddy
sudo systemctl start caddy
```

Edit your Caddyfile at `/etc/caddy/Caddyfile`, then reload:

```bash
sudo systemctl reload caddy
```

### Task: List Installed Modules

```bash
caddy list-modules
```

```text
admin.api.load
admin.api.metrics
caddy.adapters.caddyfile
...

  Standard modules: 100
```

Use `--json` for machine-readable output, or `--skip-standard` to show only plugins:

```bash
caddy list-modules --skip-standard --versions
```

## 6. Troubleshooting

### Diagnostic Decision Tree

```text
Caddy won't start
├── Port already in use? → Check with `ss -tlnp | grep :443`
├── Permission denied on port 80/443? → Use setcap or run as root
├── Config error? → Run `caddy validate --config Caddyfile`
└── Certificate error? → Check DNS, firewall, and ACME account

HTTPS not working
├── Certificate not issued? → Check ACME logs, DNS, port 80 reachability
├── HTTP redirect not working? → Check `auto_https` setting
└── Mixed content warnings? → Ensure all assets use HTTPS URLs

Reverse proxy errors
├── 502 Bad Gateway? → Backend is down or unreachable
├── Timeout? → Adjust timeout settings in reverse_proxy
└── Wrong headers? → Check header_up/header_down configuration
```

### Error: `permission denied` when binding to port 80 or 443

**Cause:** Linux requires elevated privileges to bind to ports below 1024.

**Solution:**
```bash
sudo setcap cap_net_bind_service=+ep $(which caddy)
```

**Prevention:** Use the package manager installation, which configures this automatically via systemd.

### Error: `config is not valid JSON`

**Cause:** You passed a Caddyfile or other non-JSON config without specifying an adapter.

**Solution:**
```bash
caddy run --config Caddyfile --adapter caddyfile
```

Or simply name your file `Caddyfile` — Caddy detects it automatically.

**Prevention:** Caddy auto-detects Caddyfile format when the file is named `Caddyfile` or has a `.caddyfile` extension.

### Error: TLS certificate not obtained

**Cause:** ACME challenge failed. Usually a DNS or firewall issue.

**Solution:**
1. Ensure ports 80 and 443 are publicly reachable
2. Verify DNS A/AAAA records resolve to your server
3. Check Caddy logs for ACME-specific errors
4. Consider using DNS challenge for private networks

**Prevention:** Test with `caddy run` in foreground to see real-time ACME logs.

### Error: `caddy responded with error: HTTP 400`

**Cause:** Invalid configuration sent to the admin API during reload.

**Solution:**
```bash
caddy validate --config Caddyfile
```

Fix any validation errors, then reload again.

### Error: socket activation problems with ACME (community issue #7525)

**Cause:** Socket activation (systemd fd passing) can conflict with ACME certificate acquisition in certain configurations.

**Solution:** If using socket activation, ensure the ACME challenge ports are not also passed as activated sockets. Consider falling back to direct port binding for ACME.

### Error: ACME broken for `.internal` addresses (community issue #7147)

**Cause:** `.internal` TLDs are not publicly resolvable and cannot use standard ACME HTTP/TLS-ALPN challenges.

**Solution:** Use `tls internal` to generate self-signed certificates for `.internal` addresses, or configure a DNS challenge provider for private ACME servers.

### Error: `{block}` ignored when imported from a separate file (community issue #7557)

**Cause:** Regression in Caddyfile import handling for block arguments in snippets loaded from external files.

**Solution:** If affected, inline the snippet in the main Caddyfile as a workaround, or upgrade to a version with the fix.

### Error: `neither HOME nor USERPROFILE environment variables are set`

**Cause:** Caddy cannot determine where to store data (certificates, config) without a home directory.

**Solution:** Set the `HOME` (Linux/macOS), `USERPROFILE` (Windows), or the XDG variables (`XDG_DATA_HOME`, `XDG_CONFIG_HOME`, `XDG_CACHE_HOME`).

### Error: `auto_https` redirects not working with multiple bind addresses (community issue #6226)

**Cause:** When multiple `bind` addresses are specified, automatic HTTP → HTTPS redirects may not be created for all addresses.

**Solution:** Explicitly configure redirect behavior or ensure bind addresses are consistent across HTTP and HTTPS.

### Error: HTTP/3 connection drops (ERR_QUIC_PROTOCOL_ERROR) in Chrome

**Cause:** Various QUIC protocol edge cases can trigger connection resets in some browsers (community issue #7556).

**Solution:** If persistent, disable HTTP/3 for the affected server:
```text
{
    servers {
        protocols h1 h2
    }
}
```

**Prevention:** Keep Caddy updated — HTTP/3 stability improves with each release.

### Error: Excessive memory growth with reverse_proxy

**Cause:** TLS failures to upstream backends may accumulate state (community issue #7665).

**Solution:** Ensure upstream TLS certificates are valid. Monitor memory usage and restart if needed.

## 7. FAQ

**Q: How does Caddy handle HTTPS automatically?**

Caddy uses the ACME protocol to obtain certificates from Let's Encrypt and ZeroSSL. It handles HTTP-01 and TLS-ALPN-01 challenges automatically, sets up HTTP → HTTPS redirects, and renews certificates before expiry. By using Caddy, you automatically agree to the CA's terms of service — this is intentional since Caddy often runs non-interactively.

**Q: Can I use Caddy without automatic HTTPS?**

Yes. Use `http://` prefix in your site address to disable HTTPS for that site, or set `auto_https off` in the global options block.

**Q: What is the admin API and how do I use it?**

The admin API listens on `localhost:2019` by default and allows you to view, modify, and reload configuration at runtime via REST requests. You can change the listen address with the `CADDY_ADMIN` environment variable or the `admin` global option. To disable it entirely: `{ admin off }`.

**Q: How do I use Caddy with Docker?**

Mount a volume for `/data` (certificates/keys) and `/config` (autosave), expose ports 80, 443, and 443/udp (HTTP/3), and provide your Caddyfile. See the Docker Hub page for official images.

**Q: What are placeholders and how do I use them?**

Placeholders like `{host}`, `{uri}`, `{remote_host}`, `{env.VAR}` are dynamic values that Caddy resolves at request time. Use them in Caddyfile directives, headers, and responses. The full list is available in the `replacer.go` source module.

**Q: How do I add third-party modules/plugins?**

Use `xcaddy build --with <module>` to build a custom binary, or use the experimental `caddy add-package <module>` command to add modules to an existing binary.

**Q: What is the difference between `caddy run` and `caddy start`?**

`caddy run` runs in the foreground and blocks until stopped. `caddy start` spawns a background process and returns immediately. Use `caddy stop` to stop a backgrounded instance.

**Q: How do I generate shell completions?**

```bash
caddy completion bash > /etc/bash_completion.d/caddy   # Bash
caddy completion zsh > "${fpath[1]}/_caddy"             # Zsh
caddy completion fish > ~/.config/fish/completions/caddy.fish  # Fish
caddy completion powershell > caddy.ps1                  # PowerShell
```

**Q: Does Caddy support WebSocket proxying?**

Yes. `reverse_proxy` transparently handles WebSocket upgrade requests. No extra configuration is needed.

**Q: Can Caddy act as its own certificate authority?**

Yes. Caddy has a built-in PKI app that can issue certificates from a local CA. Use `tls internal` to use internally-issued certificates. You can manage the local CA with `caddy trust` (install root CA into system trust store) and `caddy untrust` (remove it).

**Q: How do I use environment variables in my Caddyfile?**

Use `{env.VARIABLE_NAME}` or `{$VARIABLE_NAME}` syntax. Load additional env vars from files with `--envfile .env`.

**Q: What happens to active connections during a config reload?**

Caddy performs graceful reloads. Active HTTP connections complete normally. Active WebSocket connections are maintained across configuration reloads for unrelated routes, though this behavior has known edge cases (community issue #7222).

**Q: Where does Caddy store certificates?**

By default, in `$XDG_DATA_HOME/caddy` (or `~/.local/share/caddy` on Linux, `~/Library/Application Support/Caddy` on macOS, `%AppData%\Caddy` on Windows). You can change this with the `storage` configuration.

**Q: What is the `NO_COLOR` environment variable?**

Setting `NO_COLOR` to any value disables color output in Caddy's console log encoder.

---

_Generated May 2026 from caddyserver/caddy at 41aee97. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
