TL;DR: Caddy is a modern web server — like a smarter, friendlier version of nginx. Its killer feature: it automatically gets and renews HTTPS certificates for your domain with zero configuration. No certbot. No cron jobs. No multi-step SSL tutorials. You tell it your domain name and it handles everything. Its config file (the Caddyfile) is so simple that AI can generate a working one in four lines.

Why AI Coders Need to Know This

If you have ever deployed an app to a real server — a VPS on DigitalOcean, a cloud instance on AWS, a Linux box anywhere — you have hit the wall. Your app runs fine on your laptop. But on the server, you need a web server sitting in front of it to handle incoming traffic. And you need HTTPS, because browsers warn users away from HTTP sites. And you need to renew those HTTPS certificates every 90 days, or your site breaks.

The standard advice is: install nginx, then install certbot, then follow a 10-step tutorial to get a Let's Encrypt certificate, then set up a cron job to renew it. For someone who just wants to share their project with the world, that is a lot of ceremony around a problem that should be solved automatically.

Think about it like building a house. You put up the walls, wired the electricity, put in the plumbing — and now the final step before anyone can move in is getting an occupancy permit. With nginx and certbot, the "permit office" (Let's Encrypt) requires you to fill out multiple forms, schedule inspections, and set a calendar reminder to renew the permit every 90 days or the house becomes illegal to occupy. With Caddy, you hand the process over to a property manager who handles all of that on your behalf, automatically, every time.

AI coding assistants know about both nginx and Caddy — but because nginx has been the industry standard for 20 years, AI defaults to nginx. When you ask for a web server setup, you often get 60 lines of nginx config and a separate certbot tutorial. If you know about Caddy, you can ask for a simpler solution and get there in minutes instead of hours.

Real Scenario: What AI Gives You vs What You Actually Need

You have a Node.js app running on port 3000 on your Linux server. You want it accessible at myapp.com with HTTPS. You ask AI:

Prompt I Would Type

I have a Node.js app running on port 3000 on my Ubuntu server.
I want to access it at myapp.com with HTTPS.
Set up nginx as a reverse proxy with Let's Encrypt SSL.
Give me the full setup instructions.

Here is what a typical AI response looks like for nginx:

# Install nginx
sudo apt install nginx -y

# Install certbot
sudo apt install certbot python3-certbot-nginx -y

# Create nginx config at /etc/nginx/sites-available/myapp
server {
    listen 80;
    server_name myapp.com www.myapp.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}

# Enable the config
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

# Get SSL certificate
sudo certbot --nginx -d myapp.com -d www.myapp.com

# Set up auto-renewal (certbot usually does this, but verify):
sudo crontab -e
# Add: 0 12 * * * /usr/bin/certbot renew --quiet

That is seven distinct steps. You need to understand nginx config syntax (proxy_set_header, symlinks, nginx -t), install a second tool (certbot), run an interactive command that asks you questions, and then verify a cron job was set up. If any one of those steps fails, you are debugging configuration files you did not write.

Now here is the same thing with Caddy:

# Install Caddy (Ubuntu/Debian)
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
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

# Create your Caddyfile at /etc/caddy/Caddyfile
myapp.com {
    reverse_proxy localhost:3000
}

That is it. No certbot. No cron job. No symlinks. No proxy_set_header lines. You write your domain name, tell Caddy what port your app is on, and Caddy goes off and gets your HTTPS certificate automatically. The padlock appears in the browser within about 60 seconds of starting Caddy.

What a Web Server Actually Does

Before going further, let's make sure the concept is solid. A web server is software that sits at the front door of your server and decides what to do with every incoming request.

When someone types myapp.com into their browser, their computer sends a request to your server. The web server is the thing that answers the door. It might:

  • Hand the visitor a static file (an HTML page, an image, a CSS file) — this is called serving static files
  • Forward the request to your app (Node.js, Python, etc.) and send back the response — this is called a reverse proxy
  • Redirect visitors from HTTP to HTTPS automatically
  • Handle the HTTPS encryption and decryption so your app does not have to

nginx does all of this. So does Caddy. The difference is how much work you have to do to set them up, and who handles the HTTPS certificates.

The front-desk analogy: Your app is the expert in the back office — it knows how to process orders, send emails, handle accounts. The web server is the receptionist at the front desk. Visitors never talk to the back-office expert directly; they talk to the receptionist, who routes their requests appropriately. nginx is like a very experienced receptionist who needs detailed written instructions for every scenario. Caddy is a newer receptionist who already knows the standard procedures and handles most situations on their own.

The Killer Feature: Automatic HTTPS

HTTPS is what makes the padlock appear in your browser's address bar. Without it, browsers show a "Not Secure" warning that scares visitors away — and Google penalizes your site in search rankings. HTTPS works through something called an SSL/TLS certificate — a digital document that proves your site is who it says it is.

These certificates are issued by certificate authorities (CAs). The most popular free one is Let's Encrypt. The problem is that Let's Encrypt certificates expire every 90 days, by design. This keeps the internet more secure — if a certificate gets compromised, it stops working quickly. But it also means you need to renew your certificate four times a year, or your site breaks with a scary security warning.

With nginx, the renewal process requires certbot (a separate tool), correct permissions, and usually a cron job. If the cron job fails silently — which it does sometimes — you wake up to a broken site. With Caddy, renewal is completely automatic. Caddy monitors your certificate's expiry date, contacts Let's Encrypt on your behalf, downloads the renewed certificate, and reloads itself — all without any intervention from you. It has been doing this since Caddy 2.0 launched in 2020.

This is not a small quality-of-life improvement. For vibe coders running side projects, the number of sites that have gone offline because someone forgot to renew an SSL certificate is enormous. Caddy makes that class of problem disappear.

Understanding the Caddyfile

Caddy's configuration file is called the Caddyfile. It uses a format that looks almost like plain English. Here are the most common patterns you will encounter and generate with AI.

Serving a static website

mysite.com {
    root * /var/www/mysite
    file_server
}

This tells Caddy: "When someone visits mysite.com, serve files from the /var/www/mysite folder." That is two lines. Caddy handles HTTPS automatically.

Reverse proxy to a local app

myapp.com {
    reverse_proxy localhost:3000
}

One line. Any request to myapp.com gets forwarded to your app running on port 3000. Caddy handles HTTPS, HTTP-to-HTTPS redirects, and all the headers that nginx requires you to set manually.

Multiple sites on the same server

myapp.com {
    reverse_proxy localhost:3000
}

myblog.com {
    root * /var/www/myblog
    file_server
}

api.myapp.com {
    reverse_proxy localhost:4000
}

Three different domains, three different destinations, three separate HTTPS certificates — all handled automatically. In nginx, each of these would be a separate server block with 15+ lines each, plus three separate certbot commands.

Adding basic authentication

admin.myapp.com {
    basicauth {
        alice $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG
    }
    reverse_proxy localhost:3000
}

Password-protect a subdomain with two extra lines. The password is stored as a bcrypt hash (a scrambled version that cannot be reversed). Caddy can generate the hash for you with caddy hash-password.

Redirecting www to non-www

www.myapp.com {
    redir https://myapp.com{uri}
}

myapp.com {
    reverse_proxy localhost:3000
}

Visitors who type www.myapp.com get sent to myapp.com. One extra block, one line.

Why the Caddyfile is so short: Caddy has smart defaults built in. When you say reverse_proxy localhost:3000, Caddy already knows to set X-Forwarded-For headers, handle WebSocket upgrades, and do other things that nginx requires you to specify explicitly. Think of it like ordering at a restaurant — with nginx you specify every ingredient; with Caddy you say "the usual" and it knows what you mean.

Caddy vs Nginx: Side-by-Side Comparison

Here is the honest comparison between the two most common web server options you will encounter.

Feature nginx Caddy
HTTPS / SSL Manual — install certbot, run commands, set up cron Automatic — just add your domain name
Certificate renewal Manual or via certbot cron job (can fail silently) Fully automatic, zero maintenance
Config file length 15–60 lines for a basic reverse proxy 2–5 lines for the same setup
Config file readability Dense syntax, cryptic directives Near-plain English
Reverse proxy headers Must set manually (proxy_set_header, etc.) Set automatically with smart defaults
HTTP/2 & HTTP/3 HTTP/2 needs config; HTTP/3 needs extra setup HTTP/2 and HTTP/3 enabled by default
Multiple sites Separate config files + symlinks + reload Multiple blocks in one Caddyfile
Reload without downtime Yes (nginx -s reload) Yes (caddy reload)
Static file serving Excellent — battle-tested at massive scale Excellent — handles most use cases perfectly
Performance at huge scale Extreme — used by Netflix, Cloudflare, etc. Very good — handles thousands of req/sec easily
Documentation & community Massive — 20 years of tutorials and Stack Overflow answers Good — growing fast, official docs are excellent
AI code generation quality High — AI knows nginx extremely well Good — AI knows Caddy well, especially for v2
Default on servers Often pre-installed on many hosting setups Must install (easy, but one extra step)
Best for High-traffic production apps, existing setups, teams with ops experience Personal projects, startups, vibe coders, anyone who wants things to just work

Bottom line: If you are setting up a new server and do not already know nginx, use Caddy. The automatic HTTPS alone will save you hours over the life of your project. If you are working with an existing nginx setup or deploying to a platform that has nginx pre-configured, it is not worth switching — but know that Caddy exists for next time.

What AI Gets Wrong About Caddy

AI coding tools generate Caddy configs reasonably well, but there are a handful of mistakes that come up consistently. Knowing these in advance will save you debugging time.

Generating Caddy v1 syntax instead of Caddy v2

Caddy had a major version change between v1 and v2. The Caddyfile syntax changed significantly. If AI generates something that looks like this:

# ❌ This is Caddy v1 syntax — it will not work in Caddy v2
myapp.com {
    proxy / localhost:3000 {
        transparent
    }
    tls email@example.com
}

...that is the old format. In Caddy v2, the correct version is:

# ✅ Caddy v2 syntax
myapp.com {
    reverse_proxy localhost:3000
}

The proxy directive does not exist in Caddy v2. It was replaced with reverse_proxy. The tls email@example.com directive is also gone — Caddy v2 gets certificates automatically without you specifying an email in each block (you set the email globally in the global options block or it uses your system email). If Caddy gives you an error about an unknown directive, this version mismatch is the likely cause.

Forgetting to set up the global email for Let's Encrypt

Let's Encrypt requires an email address so they can notify you if something goes wrong with your certificate. Caddy handles this, but it needs to know your email. AI often forgets to include this in the config. Add it at the top of your Caddyfile:

{
    email your@email.com
}

myapp.com {
    reverse_proxy localhost:3000
}

The curly braces at the top with no domain name is the global options block. Without the email, Caddy still works but logs a warning, and Let's Encrypt may rate-limit you in edge cases.

Generating nginx configs when you want Caddy

The most common issue: you ask for a web server setup and AI defaults to nginx. Always be explicit in your prompt:

Prompt Fix

Use Caddy v2 as the web server — not nginx.
Give me a Caddyfile, not an nginx config.
My domain is myapp.com and my app runs on port 3000.

Overconfiguring things that Caddy handles automatically

AI sometimes adds directives that are unnecessary because Caddy already handles them by default. A common example:

# ❌ AI over-generates this sometimes
myapp.com {
    reverse_proxy localhost:3000 {
        header_up Host {host}
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        header_up X-Forwarded-Proto {scheme}
    }
    tls {
        protocols tls1.2 tls1.3
    }
}

Those header_up lines? Caddy sets them automatically when you use reverse_proxy. The tls block forcing TLS 1.2/1.3? Caddy already enforces that by default. The simpler version works exactly as well:

# ✅ This is enough
myapp.com {
    reverse_proxy localhost:3000
}

If your AI is generating a much longer Caddyfile, ask it to simplify: "Remove any directives that Caddy v2 handles automatically by default."

Using Caddy as if it requires a system service setup

When you install Caddy via the official package (apt, brew, etc.), it automatically registers as a system service that starts on boot. AI sometimes includes manual systemctl enable commands or tells you to create a systemd unit file — that is unnecessary if you installed via the official package. Check with sudo systemctl status caddy before following those instructions.

How to Debug with AI When Caddy Is Not Working

When Caddy is misbehaving, here is the process for diagnosing the problem and getting AI to help you fix it effectively.

Step 1: Check the Caddy logs

Caddy logs are where all the answers are. If your HTTPS certificate is failing, if your reverse proxy is returning errors, if your config has a syntax error — it shows up here.

# View Caddy service logs (systemd systems)
sudo journalctl -u caddy --no-pager -n 50

# Or follow live logs as they appear
sudo journalctl -u caddy -f

Copy the relevant error lines and paste them to AI with your Caddyfile. That gives AI everything it needs to diagnose the problem.

Step 2: Validate your Caddyfile syntax

Before blaming anything else, check that your config file has no syntax errors:

caddy validate --config /etc/caddy/Caddyfile

If there is a syntax error, Caddy tells you exactly which line and what is wrong. This catches typos and mismatched braces instantly.

Step 3: Test if Caddy can reach your app

If your site loads but returns a "Bad Gateway" error, Caddy can reach your server but cannot reach your app. Test that your app is actually running on the port you specified:

# Check if something is listening on port 3000
sudo ss -tlnp | grep 3000

# Try to reach your app directly from the server
curl http://localhost:3000

If that curl fails, your app is not running — fix that first. If it works, the problem is in your Caddyfile or your firewall.

Step 4: Check that your ports are open

HTTPS requires port 443 to be open. HTTP requires port 80. If your server's firewall blocks these, Caddy cannot get certificates (Let's Encrypt needs to reach your server on port 80 to verify you own the domain).

# Ubuntu/Debian with ufw firewall
sudo ufw status
sudo ufw allow 80
sudo ufw allow 443

Step 5: Give AI the right context

When asking AI to help debug, include all of this in one message:

Effective Debug Prompt

I'm using Caddy v2 on Ubuntu 22.04.
My Caddyfile is:

[paste your Caddyfile here]

I'm getting this error in the logs:

[paste the error lines from journalctl here]

The app I'm proxying to is a Node.js app running on port 3000.
I confirmed with 'curl localhost:3000' that the app is responding.
My domain DNS is pointing at this server's IP.
What is causing the error and how do I fix it?

That context — OS, Caddy version, your exact config, the exact error, and what you have already confirmed — gives AI everything it needs to give you a precise answer instead of a generic troubleshooting checklist.

Step 6: Reload after config changes

When you change your Caddyfile, Caddy does not pick up the changes automatically. Tell it to reload:

# Graceful reload (zero downtime)
sudo systemctl reload caddy

# Or using the caddy CLI directly
caddy reload --config /etc/caddy/Caddyfile

Using Caddy with Docker

If you are running your app with Docker, Caddy fits in cleanly. Here is the pattern AI typically generates, and what to watch for.

# docker-compose.yml
version: '3.8'

services:
  app:
    image: my-node-app
    expose:
      - "3000"

  caddy:
    image: caddy:2-alpine
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config

volumes:
  caddy_data:
  caddy_config:
# Caddyfile (in the same directory as docker-compose.yml)
myapp.com {
    reverse_proxy app:3000
}

A few things to notice: the app:3000 uses the Docker service name (app) instead of localhost:3000, because inside a Docker network the containers talk to each other by service name. The caddy_data volume is critical — it is where Caddy stores your HTTPS certificates. If you delete that volume, Caddy has to get new certificates, and Let's Encrypt has rate limits (5 certificates per domain per week). Do not delete that volume.

When Should You Actually Use Caddy?

Here is the honest, practical guide to when Caddy is the right choice.

Use Caddy when:

  • You are setting up a new server and do not already know nginx
  • You want HTTPS working in minutes without reading a tutorial about certbot
  • You are hosting one or more personal or side-project apps on a VPS
  • You are deploying with Docker and want a clean reverse proxy setup
  • You have been burned before by an expired SSL certificate that nobody renewed
  • You want to ask AI to generate a web server config and actually understand what was generated

Stick with nginx when:

  • Your server already has nginx configured and working — do not fix what is not broken
  • Your team already knows nginx deeply and has standard configs they reuse
  • You are following a tutorial or course that teaches nginx specifically
  • You are at a scale where you need to squeeze maximum performance out of your infrastructure (nginx still has an edge at extreme load)
  • Your hosting platform pre-configures nginx for you (some managed hosting does this)

The practical reality: For 95% of projects built with AI assistance — apps deployed on a VPS, side projects, client sites, MVP launches — Caddy is objectively simpler and more forgiving than nginx. The main reason people still use nginx is that they learned it first, and switching feels risky. For vibe coders who are picking a web server for the first time, Caddy removes a significant source of deployment friction.

Setting Up Caddy in Under 5 Minutes

If you want to try Caddy on a fresh Ubuntu or Debian server, here is the complete setup from zero to HTTPS:

# Step 1: Install Caddy (official Ubuntu/Debian package)
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
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

# Step 2: Open firewall ports
sudo ufw allow 80
sudo ufw allow 443

# Step 3: Write your Caddyfile
sudo nano /etc/caddy/Caddyfile

Replace the contents of the Caddyfile with:

{
    email your@email.com
}

yourdomain.com {
    reverse_proxy localhost:3000
}
# Step 4: Reload Caddy to apply the config
sudo systemctl reload caddy

# Step 5: Check the status
sudo systemctl status caddy

Within about 60 seconds of step 4, Caddy will have contacted Let's Encrypt, verified your domain, downloaded your certificate, and your site will be live at https://yourdomain.com. No certbot. No cron job. No certificate management — ever.

Before running this: Make sure your domain's DNS A record is pointing at your server's IP address. Caddy cannot get a certificate for a domain that does not resolve to your server. DNS changes can take up to 24 hours to propagate, though usually it is much faster. You can check with dig yourdomain.com or use an online DNS lookup tool.

What to Learn Next

Now that you understand what Caddy is and why it makes deployment dramatically simpler, here is where to go next to build the full picture:

  • What Is nginx? — Understand the web server Caddy is often compared to. Knowing what nginx does — and why its config is so verbose — helps you appreciate exactly what Caddy simplifies.
  • What Is HTTPS/SSL? — Go deeper on the certificate system Caddy automates. Understanding how Let's Encrypt works helps you debug certificate issues when they arise.
  • What Is a Reverse Proxy? — The core thing Caddy does for your app. Understanding reverse proxies helps you configure Caddy correctly and explain your architecture to AI.
  • What Is Deployment? — The bigger process that Caddy is one part of. From building your app to making it accessible on the internet, deployment is the full journey.
  • What Is Docker? — Caddy and Docker pair together extremely well. If you are containerizing your app, you will want Caddy in your Docker Compose setup.

Frequently Asked Questions

Does Caddy work with any domain name?

Yes — Caddy automatically gets HTTPS certificates for any domain you own and point at your server. The domain must have its DNS records pointing at your server's IP address before Caddy can issue a certificate. Local development on localhost works automatically too, with a self-signed certificate that Caddy manages for you.

Is Caddy production-ready or just for hobbyists?

Caddy is used in production by thousands of companies including large-scale SaaS products. It is written in Go — the same language used to build Docker and Kubernetes. Its automatic HTTPS is not a toy feature; it uses the same Let's Encrypt certificates that professionals pay services to manage. The main reason teams stick with nginx in production is familiarity, not capability.

Can Caddy replace nginx completely?

For most vibe coder use cases — serving a website, running a reverse proxy for an app, hosting multiple sites on one server — yes, Caddy can fully replace nginx. The one area where nginx still dominates is extremely high-traffic scenarios where every millisecond of performance counts and teams have the expertise to tune nginx's configuration. For 99% of projects built with AI, Caddy does everything nginx does with far less configuration.

What happens to my HTTPS certificate if Caddy goes down?

Caddy stores your certificates on disk in a directory it manages (usually /var/lib/caddy or ~/.local/share/caddy). If Caddy restarts, it reads the stored certificate immediately — no need to contact Let's Encrypt again. Certificates are valid for 90 days and Caddy renews them automatically before they expire, typically when 30 days remain. Your site stays secure even across server reboots.

Can I use Caddy with Docker?

Yes. There is an official Caddy Docker image. You mount your Caddyfile into the container and map ports 80 and 443. The main thing to watch out for: for automatic HTTPS to work inside Docker, your container needs to be reachable from the public internet on ports 80 and 443 so Let's Encrypt can verify you own the domain. If you are running locally or behind a firewall, use the localhost directive instead and Caddy will use a local certificate.