logo
Discover Caddy: A Fast and Modern Alternative to Nginx

Discover Caddy: A Fast and Modern Alternative to Nginx

Dec 23, 2025

Introduction: The HTTPS Problem That Caddy Solves

For years, setting up a web server with HTTPS was a multi-step nightmare:

  1. Install Nginx

  2. Install Certbot (SSL certificate tool)

  3. Run Certbot to get a certificate

  4. Configure Nginx to use the certificate

  5. Set up a cron job to renew the certificate before it expires

  6. Hope the cron job runs successfully

  7. When it fails silently, your site goes down

  8. Spend hours debugging why HTTPS is broken

This process is fragile, error-prone, and requires constant maintenance.

Then Caddy came along and asked: "Why should this be so complicated?"

Caddy is a modern web server that makes HTTPS automatic. You don't configure certificates or renewal. You just point your domain at your server and Caddy handles everything.

In this guide, we'll explore what Caddy is, why it's revolutionary, and why Node.js developers should care.


What Is a Web Server?

Before we explain Caddy, let's clarify what a web server does.

The Role of a Web Server

When you visit a website, here's what happens:

You visit: https://example.com
    ↓
Your browser connects to the server
    ↓
[Web Server receives the request]
    ↓
Web Server routes the request:
  - If requesting a static file (CSS, JS, images)
    → Serve from disk
  - If requesting dynamic content
    → Forward to backend application
    ↓
Response sent back to browser
    ↓
You see the website

A web server's job:

  1. Listen for incoming connections on port 80 (HTTP) and 443 (HTTPS)

  2. Terminate HTTPS connections (decrypt the traffic)

  3. Route requests to the right place (static files or your app)

  4. Manage certificates and security

Web Servers You Might Know

ServerUse CaseConfigurationHTTPS Setup
ApacheEverything, legacyComplex XMLManual, Certbot
NginxPerformance, scalingModerate, text-basedManual, Certbot
CaddyModern appsSimple, intuitiveAutomatic!
LighttpdLightweightSimpleManual, Certbot
Node.jsBackend appsProgrammaticShould not handle HTTPS directly

What Is Caddy?

Caddy is a modern web server and reverse proxy built from the ground up with three core principles:

  1. Security first: HTTPS is the default, not an afterthought

  2. Simplicity: Configuration should be readable and straightforward

  3. Automation: Tedious tasks like certificate management should be automatic

Think of Caddy as:

  • Nginx's modern cousin: Similar role, but designed for 2020s best practices

  • Apache's replacement: Simpler, faster, less configuration

  • Your Node.js app's bodyguard: Sits in front and protects your app

Key Statistics About Caddy

MetricValue
Release Date2015
Current Version2.x (stable)
LanguageGo (compiled, fast, single binary)
Memory Usage~10-30 MB (vs Nginx ~5-10, Apache ~20-50)
Configuration Simplicity10 lines for complex setup (vs Nginx 50+)
Auto-renewal Success Rate99%+ (vs manual cron jobs ~95%)

The HTTPS Revolution: Why Caddy's Automatic HTTPS Matters

The Problem: Manual HTTPS Setup

Traditional workflow (Nginx + Certbot):

# Step 1: Install Nginx
sudo apt install nginx

# Step 2: Install Certbot
sudo apt install certbot python3-certbot-nginx

# Step 3: Get certificate (manual command)
sudo certbot certonly --nginx -d example.com

# Step 4: Configure Nginx to use certificate
sudo nano /etc/nginx/sites-available/example.com
# Edit with SSL paths...

# Step 5: Reload Nginx
sudo systemctl reload nginx

# Step 6: Set up auto-renewal cron job
sudo crontab -e
# Add: 0 3 * * * /usr/bin/certbot renew --quiet

# Step 7: Hope nothing breaks
# In reality: Something WILL break eventually

What can go wrong:

  • Certificate expires because cron job fails

  • Cron job runs but renewal is incomplete

  • Nginx configuration breaks during reload

  • Certificate path changes and nothing finds it

  • DNS isn't configured correctly and certificate validation fails

The Solution: Caddy's Automatic HTTPS

Caddy workflow:

# Step 1: Install Caddy (already handles HTTPS)
sudo apt install caddy

# Step 2: Create Caddyfile
echo 'example.com { reverse_proxy localhost:3000 }' | sudo tee /etc/caddy/Caddyfile

# Step 3: Reload Caddy
sudo systemctl reload caddy

# Done! HTTPS is automatic, certificates are renewed automatically.

What Caddy does automatically:

  • ✅ Detects domain name from configuration

  • ✅ Obtains certificate from Let's Encrypt

  • ✅ Renews certificate before expiry

  • ✅ Updates configuration without downtime

  • ✅ Handles edge cases and failures

Result: Zero maintenance, 99%+ uptime guarantee.


Key Features of Caddy

1. Automatic HTTPS with Let's Encrypt

What it does:

  • Automatically obtains SSL certificates

  • Automatically renews before expiry

  • Zero configuration needed

Traditional Nginx approach:

# Manual certificate request
certbot certonly --nginx -d example.com

# Manual renewal setup
0 3 * * * certbot renew

# Hope renewal works!

Caddy approach:

example.com {
    reverse_proxy localhost:3000
}
# Certificate is obtained and renewed automatically!

2. Simple, Readable Configuration (Caddyfile)

Nginx configuration (confusing):

server {
    listen 80;
    listen [::]:80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com;
    
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
    
    location / {
        proxy_pass http://localhost:3000;
        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;
    }
}

Caddy configuration (clear and simple):

example.com {
    reverse_proxy localhost:3000
}

That's it. Caddy handles:

  • HTTP → HTTPS redirect

  • SSL certificate obtaining and renewing

  • Security headers

  • Proper proxy headers

  • HTTP/2

3. Built-In Reverse Proxy

A reverse proxy forwards requests to backend applications. Caddy does this cleanly:

# Forward all requests to Node.js app
example.com {
    reverse_proxy localhost:3000
}

# Forward specific paths to different apps
api.example.com {
    reverse_proxy localhost:3001  # API server
}

admin.example.com {
    reverse_proxy localhost:3002  # Admin panel
}

4. Zero-Downtime Configuration Reloads

When you update Caddy's configuration, you can reload without disconnecting users:

# Old way (Nginx restart): Users get brief downtime
sudo systemctl restart nginx

# Caddy way (reload): Zero downtime
sudo systemctl reload caddy
# or
sudo caddy reload --config /etc/caddy/Caddyfile

Existing connections stay alive while new configuration is loaded.

5. Secure Defaults

Caddy prioritizes security by default:

FeatureCaddy DefaultNginx Default
HTTPS✅ Automatic❌ Manual setup
HTTP/2✅ Enabled⚠️ Must enable
TLS Version✅ 1.2+ only⚠️ Older versions by default
Security Headers✅ Sensible defaults❌ Must configure
HSTS✅ Automatic❌ Manual setup

You get security best practices automatically, not as an afterthought.

6. Single Binary Installation

Caddy is written in Go and compiles to a single binary:

# One file to download and run
# No dependencies, no complexity
wget https://github.com/caddyserver/caddy/releases/download/v2.x.x/caddy_linux_amd64
chmod +x caddy_linux_amd64
./caddy_linux_amd64 serve

Compare to Nginx:

# Requires multiple dependencies
sudo apt install nginx
# Which installs: libc, openssl, zlib, pcre, etc.
# 50+ MB of dependencies

Caddy vs Nginx: Detailed Comparison

When Caddy Is Better

ScenarioWhy Caddy
Node.js deploymentHTTPS automatic, simple config
New projectsFaster setup, less config
Small/medium teamsEasier to understand and maintain
Simplicity mattersCaddyfile is much easier to read
Automatic renewalNo manual certificate management
Development serversQuick setup with HTTPS

When Nginx Is Better

ScenarioWhy Nginx
High-traffic (1M+ requests/sec)Proven at massive scale
Extreme customizationMore control over every detail
Legacy systemsOlder infrastructure uses Nginx
Performance criticalTuned for maximum performance
Large ecosystemMore third-party modules available

Side-by-Side Comparison

FeatureCaddyNginx
HTTPS SetupAutomatic (5 min)Manual + Certbot (30 min)
Configuration ComplexitySimpleModerate to complex
Learning Curve1-2 hours4-8 hours
Memory Usage~20 MB~10 MB (lighter)
CPU UsageLowVery low
Configuration ReloadZero downtimeGraceful reload
API DocumentationModernLimited
Community SizeGrowingHuge
Enterprise SupportAvailableAvailable
Typical Setup Time15 minutes1-2 hours
Certificate RenewalAutomaticVia cron (fragile)
Reverse ProxyBuilt-in, easyBuilt-in, complex
Rate LimitingBuilt-inRequires modules
WebSocket SupportBuilt-inRequires config

Real-World Scenarios: When to Choose Caddy

Scenario 1: Deploying a Node.js API

Goal: Run a Node.js API behind HTTPS with zero hassle

Why Caddy:

api.example.com {
    reverse_proxy localhost:3000
}

That's it. Node.js app runs on port 3000, Caddy handles HTTPS, certificates, and everything.

With Nginx + Certbot:

  • 15+ configuration lines

  • Manual certificate setup

  • Scheduled renewal

  • Potential for things to break

Scenario 2: Multiple Microservices

Goal: Run multiple backends on different ports

Caddy:

api.example.com {
    reverse_proxy localhost:3000
}

auth.example.com {
    reverse_proxy localhost:3001
}

admin.example.com {
    reverse_proxy localhost:3002
}

Nginx:

  • Separate server blocks

  • More configuration

  • More potential for errors

Scenario 3: Development Environment

Goal: Test HTTPS locally before production

Caddy:

caddy file-server
# Serves HTTPS on localhost with auto-generated certificate
# Perfect for testing

Nginx:

  • Must manually create self-signed certificates

  • More setup required

Scenario 4: Adding Security Headers

Goal: Add security headers to protect against XSS, clickjacking, etc.

Caddy:

example.com {
    header {
        X-Frame-Options "DENY"
        X-Content-Type-Options "nosniff"
        Strict-Transport-Security "max-age=31536000"
    }
    reverse_proxy localhost:3000
}

Nginx:

  • More verbose configuration

  • Requires knowledge of each header

  • Longer setup


Technical Deep Dive: How Caddy's Automatic HTTPS Works

Step 1: Domain Detection

When you start Caddy, it reads the Caddyfile and detects domain names:

example.com {              # ← Caddy detects this domain
    reverse_proxy localhost:3000
}

Step 2: ACME Challenge

Caddy uses ACME (Automatic Certificate Management Environment) protocol to prove ownership of the domain:

Caddy contacts Let's Encrypt
    ↓
Let's Encrypt: "Prove you own example.com"
    ↓
Caddy: "I'll respond to a DNS or HTTP challenge"
    ↓
Challenge succeeds
    ↓
Let's Encrypt issues certificate

Step 3: Certificate Storage

Certificates are stored securely:

# Typically in:
~/.local/share/caddy/certificates/

# Or as a service:
/root/.local/share/caddy/certificates/

Caddy manages file permissions and security automatically.

Step 4: Automatic Renewal

Caddy monitors certificate expiry and renews automatically:

Every 24 hours, Caddy checks:
    ↓
Certificate expires in < 30 days?
    ↓
YES → Request renewal from Let's Encrypt
NO → Continue serving
    ↓
Renewal succeeds
    ↓
No downtime, no restart needed

Step 5: Graceful Updates

New certificates are deployed without downtime:

Old certificate serving traffic
    ↓
New certificate obtained
    ↓
Caddy switches to new certificate gracefully
    ↓
Connection stays alive
    ↓
Zero downtime update

Installation Overview

Supported Platforms

PlatformInstallationStatus
Linux (apt)sudo apt install caddy✅ Official
Linux (manual)Download binary✅ Official
macOS (brew)brew install caddy✅ Official
WindowsDownload binary or chocolatey✅ Official
Dockerdocker pull caddy✅ Official
Raspberry PiCompile or download✅ Works

System Requirements

RequirementMinimumRecommended
RAM256 MB512 MB
Storage50 MB100 MB
Bandwidth1 Mbps10 Mbps
CPU1 core2 cores

Caddy is lightweight and runs almost everywhere.


Architecture: How Caddy Fits In Your Stack

Traditional Setup

Internet
   ↓
Nginx (port 80, 443)
   ↓
Certificate management (Certbot, cron)
   ↓
Node.js App (port 3000)
   ↓
Database

Problems:

  • Nginx doesn't auto-renew

  • Certbot renewal can fail

  • Separate tools to manage

  • More attack surface

Caddy Setup

Internet
   ↓
Caddy (port 80, 443)
   - HTTPS automatic
   - Certificates automatic
   - Reverse proxy
   ↓
Node.js App (port 3000)
   ↓
Database

Advantages:

  • Everything integrated

  • Zero manual maintenance

  • Fewer moving parts

  • Simpler deployment


Why Caddy Matters for Node.js Developers

Problem: Node.js Shouldn't Handle HTTPS Directly

Running Node.js directly on ports 80/443 is:

IssueImpact
Requires root accessSecurity risk
App crashes = downtimeNo process manager restart
Certificate managementComplex in code
PerformanceApp wastes cycles on HTTPS
SecurityToo many responsibilities

Solution: Let Caddy Handle It

Caddy (handles HTTPS, certificates, security)
    ↓
Node.js (focuses on business logic)

This separation means:

  • ✅ Node.js runs on port 3000 (no special access)

  • ✅ Caddy handles HTTPS professionally

  • ✅ Caddy and Node.js can restart independently

  • ✅ Application code is cleaner

  • ✅ Easier to scale (add more Node.js instances)


Caddy Use Cases

Use Case 1: Simple Node.js Blog

myblog.com {
    reverse_proxy localhost:3000
}

Setup time: 5 minutes

Use Case 2: API with Multiple Subdomains

api.example.com {
    reverse_proxy localhost:3000
}

admin.example.com {
    reverse_proxy localhost:3001
}

cdn.example.com {
    file_server
    root /var/www/cdn
}

Setup time: 15 minutes

Use Case 3: Load Balancing

example.com {
    reverse_proxy localhost:3000 localhost:3001 localhost:3002 {
        policy round_robin
    }
}

Distribute load across multiple Node.js instances

Use Case 4: Static Site Hosting

example.com {
    file_server
    root /var/www/public
}

Serve static HTML, CSS, JS securely with HTTPS

Use Case 5: Development with HTTPS

localhost:443 {
    file_server
}

Test HTTPS locally (useful for OAuth, webhooks, etc.)


Comparison with Alternative Solutions

Caddy vs ExpressJS (HTTPS directly in Node.js)

ExpressJS + HTTPS:

const https = require('https');
const fs = require('fs');
const app = require('./app');

const options = {
    key: fs.readFileSync('key.pem'),
    cert: fs.readFileSync('cert.pem')
};

https.createServer(options, app).listen(443);

Problems:

  • App must run as root (security risk)

  • Certificate renewal in code (complex)

  • No process restart without losing connections

  • Mixing concerns (HTTPS + business logic)

Caddy approach:

  • Node.js on port 3000 (no root)

  • Caddy handles HTTPS

  • Can restart Node.js independently

  • Clean separation

Caddy vs Heroku/Vercel (Platform-as-a-Service)

Heroku:

  • ✅ HTTPS automatic (like Caddy)

  • ✅ Deployment simple

  • ❌ More expensive ($7/month minimum)

  • ❌ Less control

  • ❌ Vendor lock-in

Caddy (self-hosted):

  • ✅ HTTPS automatic (like Caddy)

  • ✅ Full control

  • ✅ Cheaper (one-time VPS cost)

  • ✅ No vendor lock-in

  • ❌ Need to manage server


Security Features Built Into Caddy

HTTPS by Default

example.com {
    reverse_proxy localhost:3000
}

No configuration needed. HTTPS is automatic and mandatory.

HSTS (HTTP Strict Transport Security)

Prevents downgrade attacks:

Browser learns: "This site only uses HTTPS"
↓
If user types http://example.com
↓
Browser automatically upgrades to https://

Caddy enables this automatically.

Security Headers

Caddy can add security headers to all responses:

example.com {
    header X-Frame-Options "DENY"
    header X-Content-Type-Options "nosniff"
    header X-XSS-Protection "1; mode=block"
    
    reverse_proxy localhost:3000
}

TLS Version Control

Only supports modern TLS versions (1.2+):

  • ✅ TLS 1.3 (latest, fastest)

  • ✅ TLS 1.2 (widely supported)

  • ❌ TLS 1.1, 1.0 (disabled for security)

Certificate Pinning

Pin specific certificates to prevent man-in-the-middle attacks.


Getting Started: 30-Second Setup

The Fastest Way to Try Caddy

# 1. Install Caddy (one command)
sudo apt install caddy

# 2. Create configuration (one line)
echo 'example.com { reverse_proxy localhost:3000 }' | sudo tee /etc/caddy/Caddyfile

# 3. Start Caddy (one command)
sudo systemctl start caddy

# Done! Visit https://example.com

Your Node.js app (running on port 3000) is now publicly accessible with HTTPS, automatic certificates, and automatic renewal.

No Certbot. No cron jobs. No manual renewal. Just HTTPS.


Key Takeaways

  1. Caddy is a modern web server designed for 2020s best practices, not legacy systems.

  2. Automatic HTTPS is revolutionary - No more certificate management headaches.

  3. Configuration is simple - Caddyfile is human-readable, unlike Nginx configs.

  4. Perfect for Node.js - Separates concerns (Caddy handles HTTP, Node.js focuses on logic).

  5. Secure by default - Modern TLS versions, security headers, and best practices built-in.

  6. Zero maintenance - Set it once, and it works forever.

  7. For most projects, Caddy is the better choice over Nginx (unless you have extreme scale or need).


Next Steps

Now that you understand Caddy:

  1. Install Caddy on your VPS

  2. Create a Caddyfile for your domain

  3. Run your Node.js app on a local port

  4. Point your domain to your VPS

  5. Reload Caddy and watch HTTPS work automatically

  6. Add security features (headers, rate limiting, IP filtering)

  7. Monitor logs to ensure everything is working

The complete setup guide is coming next in this series.


Misconceptions About Caddy

"Caddy is new and untested"

False. Caddy has been around since 2015 and powers thousands of production applications. It's stable and battle-tested.

"Caddy can't handle large scale"

False. Caddy is written in Go (like Docker, Kubernetes) and is highly efficient. It handles millions of requests per second just fine.

"I need Nginx for performance"

False. For most projects, Caddy's performance is indistinguishable from Nginx. The difference only matters at extreme scale (millions of concurrent connections).

"Caddy doesn't have community support"

False. Caddy has an active community, extensive documentation, and commercial support available.

"Caddy is more expensive"

False. Caddy is open-source and free. Same as Nginx.


Further Reading


What's Next in This Series

This guide covered Caddy fundamentals. The next guides cover:

  1. Setting up Caddy with Node.js - Complete deployment walkthrough

  2. Caddy Security Features - Headers, filtering, authentication

  3. Advanced Caddy Configuration - Multiple apps, load balancing, plugins

  4. Production Deployment - PM2, monitoring, logging, maintenance

Each guide builds on the previous one, taking you from setup to production-grade deployment.