Skip to main contentSkip to navigation
[email protected]
Client AreaSupport
Hosting Mammoth
HostingMammothYour Data, Our Responsibility
Home
Solutions
Hosting Services
Store
Pricing
About
Blog
API
Contact

Stay Ahead of the Curve

Get the latest insights on cybersecurity, AI innovations, and enterprise data solutions delivered to your inbox.

Hosting Mammoth
HostingMammothEnterprise Solutions

Enterprise-grade data solutions. Hosting, recovery, cybersecurity, and AI-powered services for businesses worldwide.

[email protected]
Sun - Fri, 9:00am - 5:00pm

Services

  • Cloud Hosting
  • Data Recovery
  • Cybersecurity
  • Legal Support
  • MSP Services
  • Web Development
  • AI Services
  • Free Server Migration

Hosting

  • VPS Hosting (NVMe SSD)
  • VDS Hosting (NVMe)
  • Storage VPS (High SSD)
  • GPU Servers
  • Managed Services
  • Cloud Firewall
  • Load Balancer
  • One-Click Apps
  • n8n Hosting
  • Object Storage
  • FAQ

Company

  • Store
  • Pricing
  • About Us
  • Locations
  • Blog
  • Testimonials
  • Contact
  • Affiliate Program
  • White-Label
  • Terms of Service
  • Privacy Policy
  • Browser Cookies
  • SLA

Support

  • Client Area
  • Submit Ticket
  • Knowledge Base
  • Server Status
  • API Documentation

© 2026 Hosting Mammoth. All rights reserved.

Knowledge Base
Getting StartedAccount ManagementVPS HostingGPU ServersStorage VPSCloud FirewallLoad BalancerServer ManagementBilling & PaymentsSupport & TicketsAffiliate ProgramReseller ProgramMarketplace & Appsn8n HostingManaged ServicesServer MigrationAPI & DevelopersSecurityTroubleshootingGlossaryInstall Guides
  1. Home
  2. /
  3. Support
  4. /
  5. Install Guides
  6. /
  7. How To Install Caddy Ubuntu
GUIDEInstall Guides

How to Install Caddy on Ubuntu 24.04 — Automatic HTTPS Web Server

26 min read

How to Install Caddy on Ubuntu 24.04 — Automatic HTTPS Web Server

Caddy is the modern web server that treats HTTPS as a first-class, automatic feature rather than an afterthought. Instead of spending an afternoon wrangling Let's Encrypt, Certbot cron jobs, and Nginx server blocks, you write four lines of configuration and Caddy handles certificate provisioning, renewal, HTTP to HTTPS redirects, OCSP stapling, and HTTP/3 for you. This guide walks you through installing Caddy v2 on an Ubuntu 24.04 VPS from the official apt repository, writing your first Caddyfile, setting up reverse proxies, adding custom modules with xcaddy, and running PHP applications — everything you need to go from a blank server to a production web host.

Want to skip the setup? Deploy Caddy on a pre-tuned Ubuntu 24.04 VPS with our Starter plan and be serving HTTPS traffic in under five minutes.

Table of Contents

  • What is Caddy?
  • Why Choose Caddy Over Nginx?
  • Prerequisites
  • Step 1: Update System Packages
  • Step 2: Add the Caddy apt Repository
  • Step 3: Install Caddy
  • Step 4: Verify the Installation
  • Step 5: Write Your First Caddyfile
  • Step 6: Configure a Reverse Proxy
  • Step 7: Serve PHP with PHP-FPM
  • Step 8: Use the Caddy v2 JSON API
  • Step 9: Add Custom Modules with xcaddy
  • Step 10: Cloudflare DNS-01 Challenge
  • Managing the caddy.service systemd Unit
  • HTTP/3 and QUIC
  • Troubleshooting
  • FAQ
  • Next Steps
  • What is Caddy?

    Caddy is an open-source web server written in Go, designed around a single guiding principle: HTTPS should be automatic. When you point a domain at a Caddy server and declare it in the Caddyfile, Caddy will obtain, install, and renew a TLS certificate from Let's Encrypt (or ZeroSSL as a fallback) with zero configuration. It also handles OCSP stapling, HSTS, HTTP to HTTPS redirects, and HTTP/3 transport — all enabled by default, with sensible security headers out of the box.

    Caddy v2 is a complete rewrite of the original Caddy and is structured as a modular platform. The core binary ships with everything you need to serve a static site, proxy to a backend, handle PHP via FastCGI, compress responses, match requests with complex conditions, or transform headers. The same binary can also be extended at build time with community modules — DNS challenge providers for 100+ registrars, authentication backends, storage adapters for distributed certificate management, and protocol handlers like MQTT or gRPC.

    Typical use cases include hosting static sites (Hugo, Jekyll, Astro output), reverse-proxying to Node.js, Python, or Go application servers, serving WordPress, Laravel, and other PHP apps via PHP-FPM, operating as a Kubernetes ingress controller (via the Caddy Ingress project), acting as an edge gateway in front of microservices, and running personal homelabs where you want HTTPS on every internal service without wrestling with a certificate authority. Because Caddy is a single static binary with no runtime dependencies, it deploys cleanly on VPS instances from 1 GB of RAM upward.

    Why Choose Caddy Over Nginx?

    Both Nginx and Caddy are excellent web servers, but they optimize for different priorities. Here is a practical comparison of where each shines:

    FeatureCaddyNginx
    Automatic HTTPSBuilt-in, defaultManual via Certbot + cron
    HTTP/3 (QUIC)Enabled by defaultRequires build from source (1.25+)
    Config syntaxCaddyfile (very short)nginx.conf (verbose)
    Config for a single HTTPS site2 lines20-30 lines + Certbot
    Default security headersSensible defaultsMust configure manually
    Runtime hot reloadYes, zero downtimeYes, but less graceful
    JSON API for dynamic configYes, full RESTful APINo native equivalent
    Third-party module ecosystemGrowing (100+ modules)Very large and mature
    Memory footprint~40-80 MB typical~10-30 MB typical
    Windows supportFirst-classLimited
    If you are standing up a new site today and want HTTPS to "just work," Caddy removes an entire class of operational toil. If you are migrating a high-traffic legacy site with heavy dependencies on specific Nginx modules (for example, RTMP streaming or a custom Lua script), sticking with Nginx is reasonable. For many DevOps teams, the right answer is Caddy for new projects, Nginx for legacy. Teams also compare Caddy against Traefik (a Go-based edge router focused on dynamic Docker/Kubernetes environments) and against managed UIs like Nginx Proxy Manager — Caddy sits between them, offering a cleaner config-file experience than Nginx and simpler semantics than Traefik's label-based discovery.

    Prerequisites

    Before you begin, make sure you have:

    • A VPS running Ubuntu 24.04 LTS with root or sudo access
    • SSH access to your server
    • A domain name with an A record pointing at your server's public IPv4 address (and an AAAA record for IPv6 if available)
    • Ports 80, 443/TCP, and 443/UDP open in any upstream firewall (required for the ACME HTTP-01 challenge and HTTP/3)
    Recommended Plan: Starter
    >
    For most personal sites, small business websites, and reverse-proxy workloads, we recommend the Starter plan:
    >
    - 2 vCPU cores
    - 4 GB RAM
    - 50 GB NVMe SSD
    - Unmetered bandwidth
    >
    This is more than enough to serve millions of static requests per day and host several Node.js or PHP applications behind a single Caddy instance. If you need more parallelism for a busy multi-tenant setup, scale up to a higher-tier plan at any time.

    Connect to your server via SSH:

    bash
    ssh root@your-server-ip

    Step 1: Update System Packages

    Refresh the package index and apply any pending security updates before adding the Caddy repository.

    bash
    sudo apt update && sudo apt upgrade -y

    Expected output (abbreviated):

    text
    Hit:1 http://archive.ubuntu.com/ubuntu noble InRelease
    Hit:2 http://archive.ubuntu.com/ubuntu noble-updates InRelease
    Reading package lists... Done
    Building dependency tree... Done
    0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.

    If the kernel was updated, reboot before continuing:

    bash
    sudo reboot

    Step 2: Add the Caddy apt Repository

    The Caddy project publishes official Debian and Ubuntu packages through Cloudsmith, a hosted package registry. This is the recommended install method because it gives you automatic updates through apt upgrade and ships the binary signed by the Caddy team.

    Install the prerequisite packages:

    bash
    sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl

    Import the Cloudsmith GPG signing key:

    bash
    curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | \
      sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg

    Add the repository to apt's sources:

    bash
    curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | \
      sudo tee /etc/apt/sources.list.d/caddy-stable.list

    Update the package index with the new source:

    bash
    sudo apt update

    Expected output:

    text
    Get:1 https://dl.cloudsmith.io/public/caddy/stable/deb/debian any-version InRelease
    Reading package lists... Done

    Step 3: Install Caddy

    Install the caddy package. This single command pulls down the binary, sets up the systemd service, creates the caddy system user, and lays down a default Caddyfile.

    bash
    sudo apt install -y caddy

    Expected output:

    text
    The following NEW packages will be installed:
      caddy
    ...
    Setting up caddy (2.8.4) ...
    Created symlink /etc/systemd/system/multi-user.target.wants/caddy.service → /lib/systemd/system/caddy.service.

    The install creates several files you should know about:

    • /usr/bin/caddy — the binary itself
    • /etc/caddy/Caddyfile — the main configuration file
    • /etc/systemd/system/caddy.service (symlink) — the systemd unit
    • /var/lib/caddy — home directory for the caddy user, where certificates and state are stored under .local/share/caddy
    • /var/log/caddy — default log directory
    The service starts automatically and is enabled for boot. At this point, if you visit http://your-server-ip, you will see Caddy's default landing page.

    Step 4: Verify the Installation

    Confirm Caddy is running and healthy.

    Check the version:

    bash
    caddy version

    Expected output:

    text
    v2.8.4 h1:q3pe0wpBj1OcHFZ3n/1nl4V4bxBrYoSoab7rL9BMYNk=

    Check the systemd service status:

    bash
    sudo systemctl status caddy

    Expected output:

    text
    ● caddy.service - Caddy
         Loaded: loaded (/lib/systemd/system/caddy.service; enabled; preset: enabled)
         Active: active (running) since Wed 2026-04-16 10:00:00 UTC; 30s ago
           Docs: https://caddyserver.com/docs/
       Main PID: 1234 (caddy)
          Tasks: 8 (limit: 9403)
         Memory: 42.4M
            CPU: 0.235s
         CGroup: /system.slice/caddy.service
                 └─1234 /usr/bin/caddy run --environ --config /etc/caddy/Caddyfile

    Verify it is listening on port 80:

    bash
    curl -I http://localhost

    Expected output:

    text
    HTTP/1.1 200 OK
    Server: Caddy
    Content-Type: text/html; charset=utf-8

    Open firewall ports if you have UFW enabled:

    bash
    sudo ufw allow 80/tcp
    sudo ufw allow 443/tcp
    sudo ufw allow 443/udp
    sudo ufw reload

    The UDP allow is required for HTTP/3, which runs over QUIC.

    Step 5: Write Your First Caddyfile

    The Caddyfile is Caddy's native, human-readable configuration format. It is deliberately terse — the minimal site block is just two lines.

    Open the Caddyfile:

    bash
    sudo nano /etc/caddy/Caddyfile

    Replace its contents with a simple static site block:

    caddy
    example.com {
        root * /var/www/example.com
        file_server
    }

    That is the entire configuration for a TLS-encrypted static site. Let's break down what each line does:

    • example.com { — the site address. Caddy parses this, detects that it is a public domain, and will automatically obtain a certificate from Let's Encrypt on first request.
    • root /var/www/example.com — sets the document root. The is a matcher that applies to all requests.
    • file_server — enables the static file server, which serves files directly from the root with automatic index handling and MIME type detection.
    Create the web root and drop an index file:

    bash
    sudo mkdir -p /var/www/example.com
    echo "<h1>Hello from Caddy on Ubuntu 24.04</h1>" | sudo tee /var/www/example.com/index.html
    sudo chown -R caddy:caddy /var/www/example.com

    Validate the Caddyfile syntax before reloading:

    bash
    sudo caddy validate --config /etc/caddy/Caddyfile

    Expected output:

    text
    2026/04/16 10:15:00.000 INFO    using config from file  {"file": "/etc/caddy/Caddyfile"}
    2026/04/16 10:15:00.000 INFO    adapted config to JSON  {"adapter": "caddyfile"}
    Valid configuration

    Gracefully reload (no dropped connections):

    bash
    sudo systemctl reload caddy

    Visit https://example.com in a browser. Within 5-30 seconds of the first request, Caddy completes the ACME HTTP-01 challenge with Let's Encrypt, installs the certificate, and serves your page over HTTPS. There is no Certbot, no cron job, and no manual renewal step. Certificates renew automatically 30 days before expiry.

    Caddyfile Shorthand Power

    The Caddyfile supports many one-line directives for common patterns:

    caddy
    # Redirect www to apex
    www.example.com {
        redir https://example.com{uri}
    }

    Enable gzip + zstd compression globally

    example.com { encode zstd gzip root * /var/www/example.com file_server }

    Add security headers

    example.com { header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" X-Content-Type-Options "nosniff" Referrer-Policy "strict-origin-when-cross-origin" } root * /var/www/example.com file_server }

    Step 6: Configure a Reverse Proxy

    The most common Caddy use case is sitting in front of an application server (Node.js, Python, Go, Rails, etc.) and handling TLS, compression, and HTTP/3 for it.

    Say you have a Node.js app listening on 127.0.0.1:3000. Edit the Caddyfile:

    caddy
    app.example.com {
        reverse_proxy 127.0.0.1:3000
    }

    That is literally the entire config for an HTTPS reverse proxy with automatic certificates. Reload Caddy:

    bash
    sudo systemctl reload caddy

    Multi-Upstream with Load Balancing

    Point at multiple backends with health checks:

    caddy
    app.example.com {
        reverse_proxy 127.0.0.1:3000 127.0.0.1:3001 127.0.0.1:3002 {
            lb_policy       round_robin
            lb_try_duration 5s
            health_uri      /healthz
            health_interval 10s
            health_timeout  2s
        }
    }

    Available lb_policy values include round_robin, least_conn, random, random_choose 2, first, and ip_hash.

    Path-Based Routing

    Route different URL prefixes to different backends:

    caddy
    example.com {
        handle /api/* {
            reverse_proxy 127.0.0.1:3000
        }

    handle /ws/* { reverse_proxy 127.0.0.1:4000 }

    handle { root * /var/www/example.com file_server } }

    The final handle block with no matcher is the fallback, equivalent to a "default" case.

    WebSockets

    Caddy handles WebSocket upgrades transparently — no extra config needed. The reverse_proxy directive preserves Upgrade and Connection headers automatically.

    Step 7: Serve PHP with PHP-FPM

    Caddy has a built-in FastCGI client. Combined with PHP-FPM, it runs any PHP application — WordPress, Laravel, Drupal, Symfony, NextCloud — with minimal config.

    Install PHP-FPM:

    bash
    sudo apt install -y php-fpm php-mysql php-curl php-gd php-xml php-mbstring php-zip

    Confirm the PHP-FPM socket location (version varies by release):

    bash
    ls /run/php/

    Expected output:

    text
    php8.3-fpm.pid  php8.3-fpm.sock  php-fpm.sock

    Update your Caddyfile to use the php_fastcgi directive:

    caddy
    wordpress.example.com {
        root * /var/www/wordpress
        php_fastcgi unix//run/php/php8.3-fpm.sock
        file_server
    }

    The php_fastcgi directive is a shortcut that expands into:

    • Rewriting requests for missing files to index.php (needed for WordPress pretty permalinks)
    • Forwarding .php requests to PHP-FPM over the Unix socket
    • Setting the correct SCRIPT_FILENAME and PATH_INFO FastCGI parameters
    • Blocking direct access to .php files in certain sensitive locations
    Set the correct permissions:

    bash
    sudo chown -R caddy:caddy /var/www/wordpress

    Reload Caddy:

    bash
    sudo systemctl reload caddy

    For Laravel, replace root with the path ending in /public:

    caddy
    laravel.example.com {
        root * /var/www/laravel/public
        php_fastcgi unix//run/php/php8.3-fpm.sock
        file_server
    }

    Step 8: Use the Caddy v2 JSON API

    Unlike Nginx, Caddy exposes a full RESTful admin API for querying and modifying its configuration at runtime. The Caddyfile is actually parsed into a JSON structure that the API operates on directly. This is invaluable for programmatic deployments, multi-tenant platforms, and dynamic SaaS ingress.

    By default the admin endpoint listens on 127.0.0.1:2019.

    Fetch the current full config:

    bash
    curl http://localhost:2019/config/

    Get just the HTTP apps section:

    bash
    curl http://localhost:2019/config/apps/http

    Load a brand-new config atomically from a JSON file:

    bash
    curl -X POST \
      -H "Content-Type: application/json" \
      -d @config.json \
      http://localhost:2019/load

    Minimal JSON Config Example

    This is equivalent to a two-line Caddyfile but expressed in the raw JSON format:

    json
    {
      "apps": {
        "http": {
          "servers": {
            "srv0": {
              "listen": [":443"],
              "routes": [
                {
                  "match": [{ "host": ["example.com"] }],
                  "handle": [
                    {
                      "handler": "file_server",
                      "root": "/var/www/example.com"
                    }
                  ]
                }
              ]
            }
          }
        }
      }
    }

    You rarely need to write JSON by hand — use the Caddyfile for authoring and the API for orchestration. Multi-tenant platforms can use the JSON API to provision per-tenant HTTPS endpoints on demand without editing text files.

    Restricting the Admin API

    For production, make absolutely sure the admin endpoint is bound to loopback only. Check /etc/caddy/Caddyfile:

    caddy
    {
        admin localhost:2019
    }

    Or disable it entirely if you manage Caddy only through the Caddyfile:

    caddy
    {
        admin off
    }

    Step 9: Add Custom Modules with xcaddy

    Caddy's modular architecture means extra features — DNS challenge providers, authentication backends, storage adapters, extra handlers — are distributed as Go modules compiled into a custom binary. The tool for this is xcaddy.

    Install Go (required by xcaddy):

    bash
    sudo apt install -y golang-go

    Install xcaddy from its official Cloudsmith repository:

    bash
    curl -1sLf 'https://dl.cloudsmith.io/public/caddy/xcaddy/gpg.key' | \
      sudo gpg --dearmor -o /usr/share/keyrings/caddy-xcaddy-archive-keyring.gpg

    curl -1sLf 'https://dl.cloudsmith.io/public/caddy/xcaddy/debian.deb.txt' | \ sudo tee /etc/apt/sources.list.d/caddy-xcaddy.list

    sudo apt update sudo apt install -y xcaddy

    Build a Caddy binary with the Cloudflare DNS module compiled in:

    bash
    xcaddy build --with github.com/caddy-dns/cloudflare

    Expected output:

    text
    2026/04/16 10:30:00 [INFO] absolute output file path: /root/caddy
    2026/04/16 10:30:00 [INFO] Temporary folder: /tmp/buildenv_xxx
    2026/04/16 10:30:00 [INFO] Writing main module: /tmp/buildenv_xxx/main.go
    2026/04/16 10:30:02 [INFO] Build environment ready
    2026/04/16 10:30:15 [INFO] Build complete: ./caddy

    Replace the system binary (stop the service first):

    bash
    sudo systemctl stop caddy
    sudo mv ./caddy /usr/bin/caddy
    sudo systemctl start caddy

    Verify the new binary includes the module:

    bash
    caddy list-modules | grep dns.providers

    Expected output:

    text
    dns.providers.cloudflare

    The Caddy community maintains modules for virtually every major DNS provider (Route 53, DigitalOcean, Gandi, Hetzner, OVH, Namecheap, Porkbun, and many more). Browse the list at caddyserver.com/docs/modules.

    Step 10: Cloudflare DNS-01 Challenge

    If you want wildcard certificates (for example, *.example.com) or cannot expose port 80 to the public internet, use the DNS-01 ACME challenge instead of HTTP-01. This requires a custom Caddy build with a DNS provider module — we built one with the Cloudflare module in the previous step.

    Create a Cloudflare API token at dash.cloudflare.com/profile/api-tokens with the Zone → DNS → Edit permission scoped to the zone you are managing.

    Edit the systemd override to pass the token as an environment variable:

    bash
    sudo systemctl edit caddy

    Add these lines:

    ini
    [Service]
    Environment="CLOUDFLARE_API_TOKEN=your_token_here"

    Update your Caddyfile to use the DNS challenge:

    caddy
    *.example.com, example.com {
        tls {
            dns cloudflare {env.CLOUDFLARE_API_TOKEN}
        }

    @app host app.example.com handle @app { reverse_proxy 127.0.0.1:3000 }

    @api host api.example.com handle @api { reverse_proxy 127.0.0.1:4000 }

    handle { root * /var/www/example.com file_server } }

    Reload the daemon and restart Caddy:

    bash
    sudo systemctl daemon-reload
    sudo systemctl restart caddy

    Caddy now provisions a single wildcard certificate for *.example.com via DNS-01 — no port 80 exposure required, and you can freely add new subdomains to the matchers without new certificates.

    Managing the caddy.service systemd Unit

    The Caddy apt package ships a hardened systemd unit. Here is the day-to-day command surface you will use.

    Start / stop / restart / reload:

    bash
    sudo systemctl start caddy
    sudo systemctl stop caddy
    sudo systemctl restart caddy
    sudo systemctl reload caddy   # graceful, zero-downtime config reload

    Enable or disable auto-start on boot:

    bash
    sudo systemctl enable caddy
    sudo systemctl disable caddy

    View live logs:

    bash
    sudo journalctl -u caddy -f

    View only the last 100 lines:

    bash
    sudo journalctl -u caddy -n 100 --no-pager

    Format logs in the Caddyfile for structured JSON output to a file:

    caddy
    {
        log default {
            output file /var/log/caddy/access.log {
                roll_size     100mb
                roll_keep     10
                roll_keep_for 720h
            }
            format json
        }
    }

    The roll_* parameters configure log rotation natively — no logrotate config needed.

    The caddy reload vs. restart Difference

    • systemctl reload caddy calls caddy reload internally, which loads a new config in place without dropping any in-flight connections. Use this for config changes.
    • systemctl restart caddy fully stops the process and restarts it. Use this only after binary upgrades (including xcaddy rebuilds) or environment variable changes.

    HTTP/3 and QUIC

    HTTP/3 is enabled by default on every HTTPS site in Caddy v2. QUIC runs over UDP port 443, so make sure the port is open in your cloud provider's security group and local UFW rules (we opened it in Step 4).

    Verify HTTP/3 is responding:

    bash
    curl --http3 -I https://example.com

    Expected output:

    text
    HTTP/3 200
    server: Caddy
    alt-svc: h3=":443"; ma=2592000

    The alt-svc header advertises HTTP/3 to clients that connected over HTTP/2 first. On the second request, compatible browsers will switch to QUIC transparently.

    Benefits of HTTP/3 include faster connection setup (0-RTT resumption for returning visitors), improved performance on lossy mobile networks, and better multiplexing without head-of-line blocking. For a modern marketing site or SaaS frontend, leaving it on is purely a win.

    Troubleshooting

    ProblemCauseSolution
    tls: server gave HTTP response to HTTPS clientPort 80 is not open, so ACME HTTP-01 challenge is failingOpen port 80 in UFW and the upstream firewall, or switch to DNS-01 challenge
    unable to get local issuer certificate in curlCaddy hasn't finished provisioning yet, or DNS is wrongWait 30 seconds, verify dig example.com A returns the server IP, then check journalctl -u caddy
    bind: permission denied on port 80Caddy user lacks CAP_NET_BIND_SERVICEThe apt package sets this automatically; reinstall with sudo apt install --reinstall caddy
    too many requests from Let's EncryptYou hit the rate limit (50 certs/week per registered domain)Use the Let's Encrypt staging server during testing: add acme_ca https://acme-staging-v02.api.letsencrypt.org/directory in the global block
    502 Bad Gateway on reverse proxyUpstream not running or wrong portCheck curl 127.0.0.1:3000 directly; verify your backend app is listening on the expected port
    WordPress / Laravel returns PHP source instead of executingphp_fastcgi socket path is wrongRun ls /run/php/ and update the Caddyfile to match the PHP-FPM version on your system
    HTTP/3 doesn't work even with UDP 443 openClient doesn't support it or middle-box blocks QUICTest with curl --http3; some corporate networks block UDP 443. HTTP/2 fallback is automatic.

    Viewing the Current Loaded Config

    If you suspect your Caddyfile isn't being picked up correctly, dump the live JSON config that Caddy is actually running:

    bash
    curl -s http://localhost:2019/config/ | jq .

    Compare this against what you expect your Caddyfile to produce.

    FAQ

    Is Caddy really free to use in production?

    Yes. Caddy is Apache 2.0 licensed and free for any use, commercial or personal. Automatic HTTPS via Let's Encrypt or ZeroSSL is included at no cost. There is a commercial support offering from the maintainers, but the binary and all core features are fully open source. Many commercial SaaS products embed Caddy as their ingress layer without any licensing fees.

    How does Caddy compare to Nginx?

    Caddy prioritizes simplicity and security-by-default — it provisions and renews TLS certificates automatically, ships with HTTP/3 enabled, and uses a much shorter config syntax. Nginx offers broader third-party module ecosystems and more tuning knobs for extreme high-throughput workloads. For most small and medium sites, Caddy achieves the same result in a fraction of the configuration. A site that requires 30 lines of nginx.conf plus a Certbot cron entry typically needs only 2-4 lines of Caddyfile.

    Can Caddy handle millions of requests per day?

    Yes. Caddy is written in Go and handles tens of thousands of concurrent connections comfortably on modest hardware. Netlify, Stripe, and many CDN edges use Caddy or its underlying libraries in production. For very high-traffic sites, tune the Linux ulimits (raise nofile to at least 65535), enable HTTP/3, and front Caddy with a load balancer if you need horizontal scaling beyond one server. The default systemd unit already sets LimitNOFILE=1048576.

    Do I need to open port 80 if I only serve HTTPS?

    Yes, for the HTTP-01 ACME challenge. Let's Encrypt validates domain ownership by calling port 80. If you cannot expose port 80, use the DNS-01 challenge with a custom Caddy build that includes your DNS provider module (for example caddy-dns/cloudflare) — this lets Caddy prove ownership via a DNS TXT record instead. Step 10 of this guide shows the full Cloudflare setup.

    What is HTTP/3 and does Caddy enable it by default?

    HTTP/3 is the latest version of the HTTP protocol, running over QUIC (UDP) instead of TCP. It reduces connection setup latency and handles packet loss more gracefully on mobile networks. Caddy enables HTTP/3 by default on all HTTPS sites — you only need to make sure UDP port 443 is open in your firewall. Browsers automatically discover the HTTP/3 endpoint via the alt-svc header returned on the first HTTP/2 request.

    How do I add Caddy modules like the Cloudflare DNS plugin?

    Use xcaddy, the official custom build tool. Install xcaddy with Go, then run xcaddy build --with github.com/caddy-dns/cloudflare to produce a Caddy binary with the Cloudflare module compiled in. Replace /usr/bin/caddy with the new binary and restart the service. You can chain any number of --with flags in a single build command. A full list of community modules is maintained at caddyserver.com/docs/modules.

    Can I run PHP applications like WordPress on Caddy?

    Yes. Install PHP-FPM (sudo apt install php-fpm) and use the php_fastcgi directive in your Caddyfile pointing at the FPM Unix socket. Caddy will route .php requests to FPM and serve static files directly. This works identically for Laravel, WordPress, Drupal, and any FastCGI-compatible application. The php_fastcgi shortcut handles pretty-permalink rewrites and blocks direct access to sensitive .php files automatically — you do not need to configure try_files rules manually the way you would in Nginx.

    Next Steps

    With Caddy running and serving HTTPS automatically, here are recommended ways to build on this foundation:

    • Set up WordPress or a CMS — Combine the PHP-FPM config from Step 7 with a MariaDB install to host WordPress, Drupal, or Ghost on your VPS.
    • Deploy a Node.js, Python, or Go app — Run your application server behind Caddy as a reverse proxy. Caddy handles TLS, compression, and HTTP/3; your app focuses on business logic.
    • Add basic auth or JWT auth — Use the basicauth directive for simple password protection, or install the caddy-security module for full OAuth2/OIDC flows.
    • Monitor with Uptime Kuma — Add your new HTTPS endpoint to Uptime Kuma to track uptime, response time, and certificate expiry (though Caddy renews automatically, visibility is still useful).
    • Compare with alternatives — If you want a UI-driven proxy experience, read our guide on Nginx Proxy Manager. For dynamic Docker-based routing, see Traefik. For the classic battle-tested choice, our Nginx install guide covers the full setup.
    • Explore the full docs — The official Caddy documentation is excellent and well-indexed. The Caddyfile directives reference, JSON config structure, and module catalog are all worth bookmarking.

    Skip the Manual Install — Start on a Starter VPS
    >
    Our Starter VPS runs Ubuntu 24.04 LTS with fast NVMe storage, making it the ideal base for a Caddy-fronted web stack. Deploy in 60 seconds and follow this guide end-to-end.
    >
    - 2 vCPU, 4 GB RAM, 50 GB NVMe
    - Unmetered bandwidth
    - Root access with SSH keys pre-configured
    - Works out of the box with the steps in this guide
    >
    Launch your Starter VPS and have HTTPS running in five minutes.

    Was this article helpful?

    ← Back to Install GuidesBrowse all categories →

    Still have questions?

    Contact Support →Submit a Ticket