Caddy vs Nginx + Certbot in 2026: Should You Switch? banner

Caddy vs Nginx + Certbot in 2026: Should You Switch?

Section: DevOps

For years, my default setup for deploying web applications on a VPS has been familiar:

Text
Internet
   ↓
Nginx
   ↓
Django / FastAPI / Node.js

Add HTTPS, and the stack usually becomes:

Text
Internet
   ↓
Nginx
   ↓
Let's Encrypt certificates
   ↑
Certbot
   ↓
Application

It works. It is battle-tested. It is everywhere.

But recently I took a closer look at Caddy, and one question became difficult to ignore:

If I were building the same infrastructure from scratch today, would I still choose Nginx + Certbot?

My answer is: probably not.

For a fresh VPS or a new small-to-medium deployment, I would now lean toward Caddy. However, that does not mean I would immediately migrate an existing production server that already runs multiple applications behind Nginx.

That distinction is important.

This article compares both approaches from a practical backend engineer's perspective: reverse proxying, HTTPS, configuration, Docker, performance, caching, reliability, and - most importantly - when switching is actually worth it.


TL;DR

If I were starting from a completely fresh server:

I would choose Caddy for most Django, FastAPI, Node.js, and Docker Compose deployments.

If I already had a VPS with multiple production applications behind a stable Nginx setup:

I would keep Nginx unless I had a concrete reason to migrate.

Caddy wins mainly on simplicity and operational ergonomics.

Nginx wins mainly on ecosystem maturity, advanced caching, fine-grained control, and the fact that it may already be deeply integrated into your infrastructure.


What Caddy Actually Replaces

For a common VPS setup, Caddy effectively combines several responsibilities:

Text
Nginx
+
Certbot
+
certificate renewal automation
+
HTTPS redirect configuration
+
TLS defaults

into one service.

With Caddy, this:

Caddyfile
api.example.com {
    reverse_proxy localhost:8000
}

can be enough for a production HTTPS reverse proxy.

If DNS points to the server and ports 80 and 443 are reachable, Caddy can automatically:

  • obtain a publicly trusted TLS certificate;
  • renew the certificate;
  • redirect HTTP to HTTPS;
  • terminate TLS;
  • proxy requests to the application.

That is the main reason Caddy feels so refreshing.

It is not necessarily doing something Nginx cannot do. It is removing several pieces of infrastructure that normally have to be configured separately.


Caddy Vs Nginx + Certbot

FeatureCaddyNginx + CertbotMy pick
Reverse proxyExcellentExcellentTie
HTTPS setupAutomaticCertbot requiredCaddy
Certificate renewalBuilt inScheduled Certbot renewalCaddy
HTTP -> HTTPSAutomaticExplicit configuration / CertbotCaddy
TLS defaultsModern automatic defaultsHighly configurableCaddy for most apps
Initial setupVery simpleMore moving partsCaddy
Config readabilityExcellentMore verboseCaddy
Advanced routingVery goodExcellentNginx
Load balancingBuilt inBuilt inTie
Active health checksBuilt inMore limited in Nginx OSSCaddy
Proxy cachingNot a core strengthExtremely matureNginx
Static filesExcellentExcellentTie
WebSocketsUsually automaticOften needs explicit headersCaddy
gRPCYesYesTie
Docker ComposeExcellentExcellentCaddy
Memory efficiencyGoodExcellentNginx
Raw performanceExcellentExcellentNginx by a small margin
Dynamic configurationStrong API-based modelTraditionally config + reloadCaddy
EcosystemGoodMassiveNginx
Enterprise adoptionGrowingMassiveNginx
Existing knowledge/resourcesGoodHugeNginx
Operational complexityLowMediumCaddy

The important point is that both are production-grade web servers and reverse proxies.

This is not a comparison between a serious tool and a toy.

The question is mostly about how much complexity you want to operate.


1. Configuration Complexity

Consider a basic API:

Text
api.example.com
       ↓
FastAPI / Django
       ↓
localhost:8000

Caddy

Caddyfile
api.example.com {
    reverse_proxy localhost:8000
}

That is almost the entire configuration.

Caddy automatically enables HTTPS when it knows the hostname it is serving.

Nginx

A typical Nginx reverse proxy starts closer to this:

Nginx
server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://127.0.0.1:8000;

        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;
    }
}

Then HTTPS is added separately, usually through Certbot or another ACME client.

For example:

Bash
sudo nginx -t
sudo systemctl reload nginx

sudo certbot --nginx -d api.example.com

None of this is particularly difficult.

But compare the number of concepts involved.

Caddy

Text
Caddyfile
   ↓
Caddy

Nginx + Certbot

Text
Nginx configuration
        ↓
Nginx

Certbot
   ↓
Let's Encrypt
   ↓
certificate files
   ↓
Nginx

systemd timer / scheduled renewal

Caddy simply has fewer moving parts.

Winner: Caddy


2. HTTPS and Certificate Management

This is Caddy's strongest advantage.

Caddy's automatic HTTPS system handles the certificate lifecycle directly.

Given:

Caddyfile
example.com {
    reverse_proxy localhost:8000
}

Caddy can take care of:

Text
ACME registration
      ↓
domain validation
      ↓
certificate issuance
      ↓
TLS configuration
      ↓
HTTP -> HTTPS redirects
      ↓
certificate renewal

No separate Certbot installation is required.

What about Certbot?

Certbot is mature and reliable.

Modern Certbot installations commonly come with automatic renewals already configured through a systemd timer or another scheduled task.

So the argument should not be:

"Certbot renewal is unreliable."

That would be unfair.

The better argument is:

"Caddy removes Certbot as a separate component."

That means fewer packages, fewer configuration files, fewer scheduled services, and fewer things to remember when maintaining the server.

Winner: Caddy


3. Reverse Proxying

Both tools are excellent reverse proxies.

Caddy

Caddyfile
api.example.com {
    reverse_proxy localhost:8000
}

Nginx

Nginx
server {
    listen 443 ssl;
    server_name api.example.com;

    location / {
        proxy_pass http://127.0.0.1:8000;

        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;
    }
}

For a simple API, Caddy is dramatically cleaner.

However, Nginx becomes attractive when the routing gets complicated:

Text
/api/
/admin/
/static/
/media/
/websocket/
/internal/
/legacy/

Nginx's configuration model gives you extremely granular control over locations, rewriting, buffering, headers, caching, timeouts, and upstream behavior.

Caddy can handle sophisticated routing too, but Nginx has decades of examples, documentation, modules, and production experience around unusual configurations.

Winner

  • Simple and medium deployments: Caddy
  • Very complex routing: Nginx

4. Load Balancing and Health Checks

Caddy can load balance between multiple upstreams with very little configuration:

Caddyfile
api.example.com {
    reverse_proxy app1:8000 app2:8000 app3:8000
}

It also supports load-balancing policies and both active and passive health checking.

For example:

Caddyfile
api.example.com {
    reverse_proxy app1:8000 app2:8000 {
        lb_policy least_conn

        health_uri /health
        health_interval 10s
        health_timeout 2s
    }
}

Nginx is also excellent at load balancing:

Nginx
upstream backend {
    least_conn;

    server app1:8000;
    server app2:8000;
}

server {
    location / {
        proxy_pass http://backend;
    }
}

Open-source Nginx supports standard strategies such as round-robin, least-connected, IP hash, weights, and passive health handling.

Caddy has a surprisingly strong feature set here for such a simple-looking server.

Winner: Approximately a Tie

For normal application deployments, either is more than capable.


5. Performance

This is where benchmarks can create more confusion than useful engineering decisions.

Nginx is written in C and has an extremely efficient event-driven architecture.

Caddy is written in Go and uses Go's networking stack and concurrency model.

Depending on workload and tuning, Nginx may achieve:

  • lower memory usage;
  • slightly higher throughput;
  • lower overhead in synthetic benchmarks.

But consider a normal backend stack:

Text
Browser
   ↓
Caddy / Nginx
   ↓
Django / FastAPI
   ↓
PostgreSQL
   ↓
Redis
   ↓
external services

In most real applications, the reverse proxy is not the bottleneck.

Database queries, serialization, application logic, external APIs, network latency, and poorly optimized endpoints are far more likely to matter.

If your application spends 40-100 ms processing a request, optimizing a tiny fraction of a millisecond in the proxy layer usually gives you nothing useful.

My Practical view

MetricCaddyNginx
ThroughputExcellentExcellent
LatencyExcellentExcellent
Memory efficiencyGoodExcellent
Real-world API impactUsually negligibleUsually negligible

I would not choose Nginx over Caddy solely because of theoretical performance unless I had actual measurements proving that the proxy layer was a bottleneck.

Winner: Nginx Technically, Tie for Most Applications


6. Proxy Caching

This is an area where Nginx clearly wins.

Nginx has a mature caching system built around directives such as:

Nginx
proxy_cache
proxy_cache_path
proxy_cache_key
proxy_cache_lock
proxy_cache_bypass
proxy_cache_revalidate
proxy_cache_use_stale

This enables architectures such as:

Text
Client
   ↓
Nginx
   ↓
microcache
   ↓
Django

For expensive or high-traffic endpoints, Nginx can sometimes reduce application load dramatically before requests ever reach Python.

If reverse-proxy caching is an important part of your architecture, Nginx has a very strong advantage.

Winner: Nginx


7. WebSockets

For Caddy, a normal reverse proxy configuration is generally enough:

Caddyfile
example.com {
    reverse_proxy localhost:8000
}

With Nginx, WebSocket deployments often require explicit HTTP/1.1 upgrade configuration:

Nginx
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

This is not difficult.

It is just another example of Caddy choosing sensible proxy behavior automatically.

Winner: Caddy


8. Docker Compose

Caddy fits Docker Compose deployments extremely well.

Yaml
services:
  app:
    image: myapp
    expose:
      - "8000"

  caddy:
    image: caddy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data

volumes:
  caddy_data:

Then:

Caddyfile
example.com {
    reverse_proxy app:8000
}

The Caddy data volume is important because certificate and ACME state should persist across container recreations.

An Nginx + Certbot Docker setup often involves more orchestration:

Text
nginx container
     +
certbot container
     +
shared certificate volume
     +
renewal command/timer

There are many clean ways to solve this, but Caddy's architecture naturally removes most of that work.

Winner: Caddy


9. Configuration Reloads

Both are excellent.

Nginx

A safe deployment workflow usually looks like:

Bash
nginx -t && systemctl reload nginx

Nginx starts new workers with the new configuration while existing workers finish serving their current connections.

Caddy

Caddy can reload configuration without stopping the server:

Bash
caddy reload

Caddy also exposes a configuration API, making dynamic configuration a first-class part of its architecture.

Winner: Tie for Normal Deployments

Caddy becomes more interesting if infrastructure needs to modify routing programmatically.


10. Dynamic Configuration

Caddy internally uses structured JSON configuration and exposes an admin API.

That means a control plane can theoretically do something like:

Text
SaaS control plane
       ↓
Caddy API
       ↓
add/remove routes dynamically
       ↓
customer deployments

This can be useful for:

  • temporary environments;
  • preview deployments;
  • dynamic customer domains;
  • SaaS routing;
  • internal developer platforms.

Nginx traditionally uses:

Text
generate config
      ↓
nginx -t
      ↓
reload

That model is extremely reliable, but Caddy's configuration API is architecturally elegant for dynamic systems.

Winner: Caddy


11. Static Files

Both are very capable.

Caddy:

Caddyfile
example.com {
    root * /var/www/app
    file_server
}

Nginx:

Nginx
location /static/ {
    alias /var/www/app/static/;
}

For the normal collection of:

Text
CSS
JavaScript
images
Django static files
SPA builds

I would not choose between Caddy and Nginx based on static-file performance.

Both are fast enough that the difference will rarely matter.

Winner: Tie


12. Security Philosophy

Both can be configured securely.

The main difference is philosophy.

Caddy

Caddy favors:

Text
secure defaults
automatic TLS
automatic certificate renewal
HTTPS by default
less configuration

The advantage is not necessarily better cryptography.

It is fewer opportunities for configuration mistakes.

Nginx

Nginx gives you enormous control.

That is valuable when you need it.

It can also become a liability when configuration is copied from an outdated tutorial containing old TLS recommendations or unnecessary directives.

Winner

  • Safe defaults: Caddy
  • Maximum manual control: Nginx

13. Ecosystem and Documentation

This is one of Nginx's strongest advantages.

Nginx is everywhere:

  • VPS deployments;
  • enterprise infrastructure;
  • Kubernetes;
  • CDNs;
  • cloud platforms;
  • Docker stacks;
  • hosting systems;
  • legacy systems.

When you search for an obscure problem, there is a good chance somebody has already solved the exact same issue with Nginx.

Caddy has good documentation and an active ecosystem, but it cannot match decades of Nginx adoption.

That matters in production.

Winner: Nginx


14. Operational Complexity

This is the real reason I find Caddy attractive.

A traditional deployment may involve:

Text
/etc/nginx/
    nginx.conf
    sites-available/
    sites-enabled/
    snippets/

/etc/letsencrypt/
    live/
    archive/
    renewal/

systemd:
    nginx.service
    certbot.timer

Then there are operational checks such as:

Bash
nginx -t
certbot renew --dry-run
systemctl status nginx
systemctl list-timers

With Caddy, the conceptual surface is smaller:

Text
/etc/caddy/Caddyfile
Caddy service
Caddy data directory

That difference becomes valuable over time.

The best infrastructure is often not the one with the most features.

It is the one you do not have to think about.

Winner: Caddy


Caddy Pros and Cons

ProsCons
Automatic HTTPSSmaller ecosystem
Automatic certificate renewalFewer historical troubleshooting resources
Automatic HTTP -> HTTPSProxy caching is not a core strength
Very small configurationUsually somewhat higher memory usage
Modern TLS defaultsLess common in enterprise environments
Built-in ACME supportSome advanced functionality may require modules
Strong reverse proxyNginx has deeper low-level tuning
Active health checksLess transferable knowledge in Nginx-heavy companies
Dynamic configuration API
Excellent Docker experience
Simple WebSocket proxying
Low operational complexity

Nginx + Certbot Pros and Cons

ProsCons
Extremely matureMore configuration
Massive ecosystemTLS lifecycle is a separate concern
Excellent performanceCertbot is another component
Mature proxy cachingMore moving pieces
Fine-grained tuningEasier to overconfigure
Huge enterprise adoptionMore boilerplate
Excellent documentationWebSockets often need explicit configuration
Large module ecosystemOld examples online can contain outdated advice
Proven at enormous scaleConfiguration can become difficult to maintain

Which One Would I Choose?

Here is my current decision matrix.

SituationChoice
Fresh VPSCaddy
New personal projectCaddy
Django API on a new serverCaddy
FastAPI on a new serverCaddy
Docker Compose on a new serverCaddy
Small startup infrastructureCaddy
Simple load balancingCaddy
Automatic customer domainsCaddy
Dynamic routingCaddy
Existing VPS already built around NginxNginx
Many production apps already depend on NginxNginx
Heavy proxy cachingNginx
Highly specialized request routingNginx
Existing enterprise Nginx standardsNginx
Need a specific Nginx moduleNginx

Should I Migrate an Existing Nginx VPS to Caddy?

This is where the answer becomes more nuanced.

My own VPS already has multiple live applications behind Nginx.

That means migrating is no longer:

Text
replace nginx

It becomes:

Text
inventory every domain
        ↓
inventory every location rule
        ↓
inventory redirects
        ↓
inventory static/media routes
        ↓
inventory WebSocket rules
        ↓
inventory upload/body limits
        ↓
inventory timeouts
        ↓
inventory headers
        ↓
inventory Certbot certificates
        ↓
rewrite everything for Caddy
        ↓
test every app
        ↓
cut over ports 80/443
        ↓
monitor for regressions

And what do I get immediately after doing all of that?

Mostly:

Text
cleaner configuration
+
simpler certificate management
+
fewer infrastructure components

Those are real advantages.

But they are not automatically worth introducing migration risk into a server that is already stable.

This leads to one of my favorite infrastructure rules:

Do not migrate stable production infrastructure only because the alternative is prettier.

There should be a concrete payoff.


Should a New Project on the Same VPS Use Caddy?

This sounds like an opportunity to try Caddy, but there is an important technical problem:

Nginx already owns ports 80 and 443.

You generally do not want:

Text
Nginx :80/:443
Caddy :80/:443

on the same IP address.

They cannot both independently bind the same ports in the normal setup.

You could build something like:

Text
Internet
   ↓
Nginx :443
   ↓
Caddy :8080
   ↓
new app

but that defeats much of the reason for adopting Caddy.

Caddy would no longer be responsible for public TLS termination because Nginx would still sit in front of it.

You would effectively add another proxy hop:

Text
Client
  ↓
Nginx
  ↓
Caddy
  ↓
Application

That gives you more complexity instead of less.

So for a new project on an existing Nginx VPS, I would simply add another Nginx server block:

Nginx
server {
    server_name new-project.example.com;

    location / {
        proxy_pass http://127.0.0.1:9000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Then let the existing TLS workflow continue doing what it already does.


A Better Migration Strategy

I still want to use Caddy.

I just would not migrate everything at once.

A much better strategy is:

Text
Existing VPS
------------
Nginx
├── app 1
├── app 2
├── app 3
└── new project

New VPS / future server
-----------------------
Caddy
├── new app
└── future projects

This gives me real production experience with Caddy without destabilizing infrastructure that already works.

After running Caddy in production for a while, I can evaluate:

  • certificate management;
  • logs;
  • debugging;
  • resource usage;
  • observability;
  • deployment workflow;
  • WebSockets;
  • uploads;
  • Docker networking;
  • failure behavior.

Then I can decide whether migrating the older VPS still provides enough value.

That is a much better engineering decision than a "big bang" rewrite of infrastructure.


When I Would Actually Migrate the Existing VPS

I would seriously consider replacing Nginx if one or more of these became true:

  1. I am rebuilding the VPS anyway.
  2. I am moving applications to another server.
  3. My Nginx configuration has become difficult to maintain.
  4. Certbot management is creating operational problems.
  5. I need Caddy's automatic/on-demand TLS behavior.
  6. I am building infrastructure with many dynamically created domains.
  7. I want to standardize future deployments around Caddy.
  8. Most applications are containerized and I want simpler Compose stacks.

At that point the migration is attached to a real infrastructure improvement rather than being a cosmetic rewrite.


When I Would Definitely Keep Nginx

I would keep Nginx when:

  • everything is stable;
  • certificate renewals work;
  • the configuration is understandable;
  • I already have monitoring around it;
  • multiple production applications depend on it;
  • I use Nginx-specific caching or routing features;
  • I have no operational pain that Caddy would solve.

Under those conditions, replacing Nginx is mostly churn.

And churn in infrastructure creates risk.


My Final Take

Caddy changed my view of what a modern web server configuration should look like.

For a simple FastAPI application, this is incredibly attractive:

Caddyfile
api.example.com {
    encode zstd gzip
    reverse_proxy localhost:8000
}

It gives me:

Text
HTTPS
certificate issuance
certificate renewal
HTTP -> HTTPS
reverse proxy
compression

with almost no configuration.

If I were provisioning a new VPS today, I would probably start with Caddy.

But infrastructure decisions are not made in a vacuum.

On a server where Nginx is already:

Text
stable
+
configured
+
tested
+
serving multiple applications
+
successfully renewing certificates

I would not replace it simply to save some configuration lines.

So my current rule is:

Fresh infrastructure: prefer Caddy.

Stable existing Nginx infrastructure: keep Nginx until there is a real reason to migrate.

Sometimes the better technology is not the technology you should deploy today.

The cost of changing a working system is part of the architecture too.


References