Web Servers (Apache, Nginx, Caddy)

Nginx vs Caddy for App Gateways 2025 Engineer's Guide

Published · Updated 8 min read
Knowledge base article
Contents (18 sections)

A practical, productionready comparison of Nginx and Caddy for use as an application gateway / reverse proxy: TLS automation, HTTP/3, performance, caching, observability, and secure defaults--plus copypaste configs for Node, Python, PHP, and more.


Short Summary

If you want the easiest automatic HTTPS and HTTP/3 out of the box with clean configs, choose Caddy. If you need battletested performance, builtin reverseproxy caching, and finegrained modules (rate limits, connection limits, geo/IP controls) on every distro, choose Nginx. Both excel as secure app gateways for APIs, SSR apps, and static sites.


Table of Contents

  1. Quick Answer: When to Pick Which

  2. Feature Comparison Matrix

  3. Architecture Basics

  4. Install & First Reverse Proxy

  5. TLS & Certificates

  6. HTTP/2, HTTP/3, WebSockets, gRPC

  7. Performance & Caching

  8. Security Hardening Checklist

  9. Cloudflare / CDN Integration

  10. Logging & Observability

  11. Migration CheatSheet (Nginx Caddy)

  12. Troubleshooting Tips

  13. Conclusion / Next Steps

  14. Related Articles


1) Quick Answer: When to Pick Which

Situation / Requirement Choose Nginx Choose Caddy
Zerotouch HTTPS for many sites Works (with Certbot) Automatic by default
HTTP/3 (QUIC) today Available in newer builds; extra config may be needed On by default in most builds
Reverseproxy caching builtin proxy_cache is mature Use a plugin or CDN (not core)
Finegrained rate/conn limits limit_req / limit_conn Plugins or CDN
Very low memory footprint Extremely lean Slightly higher (Go runtime), still modest
Simplest config syntax Verbose but powerful Humanfriendly Caddyfile
Enterprise ubiquity & docs Longstanding standard Growing fast, great docs
Dynamic/onthefly config nginx -s reload (no drop) Admin API; live reloads

Rule of thumb:

  • Prefer Caddy if you value autoHTTPS + HTTP/3 and concise configs with great defaults.

  • Prefer Nginx if you need builtin caching, kernellevel tuning, and broad distro packaging.


2) Feature Comparison Matrix

Capability Nginx Caddy
Automatic HTTPS Via Certbot / ACME clients Builtin ACME, OCSP stapling, automatic renewals
HTTP/2
HTTP/3 (QUIC) in recent mainline builds; check your distro enabled by default in most builds
Reverseproxy cache proxy_cache Plugin/edge CDN
Rate/conn limiting limit_req / limit_conn Plugin/edge CDN
WebSockets
gRPC / h2c (via reverse_proxy with HTTP/2)
Brotli/gzip (zlib; Brotli via module) (automatic compression; Brotli support in many builds)
Config ergonomics Block/location directives Caddyfile (simple) or JSON API
Live reload nginx -s reload Auto reload on file change + Admin API
RealIP/CDN headers realip module trusted_proxies / header passthrough
Observability Access/error logs, exporters JSON logs, structured fields, metrics module

Note: Caching and advanced ratelimits are where Nginx still has the edge without plugins.


3) Architecture Basics

A typical appgateway (reverse proxy) sits in front of one or more upstream services (Node.js/PM2, Django/Uvicorn, PHPFPM, Rails/Puma, Go, etc.), terminating TLS, handling HTTP/2/3, setting headers, and enforcing security.

code
Internet (CDN/WAF) Nginx/Caddy App(s) on 127.0.0.1:PORT DB/Cache/Queues
  • Put the proxy on the same host as the app for lowest latency.

  • Use Cloudflare or similar when you want global caching, WAF, and DDoS protection.


4) Install & First Reverse Proxy

Nginx (Ubuntu)

bash
# Install
sudo apt update && sudo apt install -y nginx
sudo systemctl enable --now nginx

# Basic vhost for app on :3000 (WebSockets enabled)
sudo tee /etc/nginx/sites-available/app.conf > /dev/null <<'NGX'
server {
 listen 80;
 server_name example.com www.example.com;

 # Cloudflare / proxy aware (optional)
 real_ip_header CF-Connecting-IP;
 set_real_ip_from 173.245.48.0/20;
 set_real_ip_from 103.21.244.0/22;
 set_real_ip_from 103.22.200.0/22;
 set_real_ip_from 103.31.4.0/22;
 set_real_ip_from 141.101.64.0/18;
 set_real_ip_from 108.162.192.0/18;
 set_real_ip_from 190.93.240.0/20;
 set_real_ip_from 188.114.96.0/20;
 set_real_ip_from 197.234.240.0/22;
 set_real_ip_from 198.41.128.0/17;

 location / {
 proxy_pass http://127.0.0.1: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-Forwarded-For $proxy_add_x_forwarded_for;
 proxy_set_header X-Forwarded-Proto $scheme;
 }
}
NGX

sudo ln -sf /etc/nginx/sites-available/app.conf /etc/nginx/sites-enabled/app.conf
sudo nginx -t && sudo systemctl reload nginx

Caddy (Ubuntu)

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

# Caddyfile (auto HTTPS, HTTP/3)
sudo tee /etc/caddy/Caddyfile > /dev/null <<'CADDY'
example.com, www.example.com {
 encode zstd gzip
 reverse_proxy 127.0.0.1:3000 {
 # WebSockets pass-through is automatic
 # Trust local reverse proxies or Cloudflare if used
 trusted_proxies private_ranges
 header_up X-Forwarded-Proto {scheme}
 header_up X-Forwarded-For {remote}
 }
 @health path /healthz
 respond @health 200
}
CADDY

sudo systemctl reload caddy

Tip: With Caddy, just point DNS to the server and it will fetch & renew certificates automatically.


5) TLS & Certificates

  • Nginx: use Certbot (ACME) for Let's Encrypt. For wildcard domains, use DNS01 plugins.

bash
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com --redirect
# Auto-renewal is installed as a systemd timer; verify with:
systemctl list-timers | grep certbot
  • Caddy: automatic HTTPS is core. Wildcards via DNS providers are supported in the Caddyfile with a DNS module.

  • OCSP stapling, HSTS, modern ciphers: both servers support hardened TLS; Caddy defaults are very strong; Nginx requires explicit TLS config blocks.


  • 6) HTTP/2, HTTP/3, WebSockets, gRPC

    • HTTP/2: both on by default with TLS.

    • HTTP/3 (QUIC): Caddy enables it by default; Nginx supports it in recent mainline builds--ensure your package includes HTTP/3 and configure listen 443 quic reuseport; plus QUIC headers.

    • WebSockets: both supported; preserve Upgrade/Connection headers in Nginx; Caddy handles automatically.

    • gRPC / h2c: both can proxy gRPC; ensure HTTP/2 upstream and proper headers.


    7) Performance & Caching

    • Nginx:

      • Tune worker_processes auto; worker_connections 4096; based on cores and expected concurrency.

      • Builtin proxy_cache to accelerate upstreams:

    nginx
    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=STATIC:10m max_size=1g inactive=60m use_temp_path=off;
    map $request_method $no_cache { default 0; GET 0; HEAD 0; POST 1; }
    
    server { ...
     location / {
     proxy_cache STATIC;
     proxy_cache_bypass $no_cache;
     add_header X-Cache $upstream_cache_status;
     proxy_pass http://127.0.0.1:3000;
     }
    }
    
  • limit_req & limit_conn for abuse control.

  • Caddy:

    • Excellent throughput and concurrency; minimal tuning needed.

    • For reverseproxy caching or ratelimits, prefer edge CDN or Caddy plugins; otherwise rely on upstream app caches (Redis) and strong CDN policies.


  • 8) Security Hardening Checklist

    • Enforce HTTPS only; redirect HTTP HTTPS

    • Set HSTS, XFrameOptions, XContentTypeOptions, ReferrerPolicy, PermissionsPolicy

    • Enable Brotli/gzip and reasonable timeouts

    • Sanitize/forward XForwarded headers* and preserve real client IP

    • Apply rate limits (Nginx) or do at the CDN/WAF layer

    • Keep packages updated; restrict admin panels by IP; disable unused modules

    Nginx header example:

    nginx
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Permissions-Policy "geolocation=(), microphone=()";
    

    Caddy header example:

    caddy
    header {
     Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
     X-Frame-Options "SAMEORIGIN"
     X-Content-Type-Options "nosniff"
     Referrer-Policy "strict-origin-when-cross-origin"
    }
    

    9) Cloudflare / CDN Integration

    • Terminate TLS at the proxy and enable orangecloud on DNS for WAF/CDN.

    • Preserve real client IP:

      • Nginx: real_ip_header CF-Connecting-IP; + set_real_ip_from for all CF ranges.

      • Caddy: use trusted_proxies and forward X-Forwarded-For/Proto.

    • Send cachefriendly headers from upstream (or from the proxy) and respect Cache-Control.


    10) Logging & Observability

    • Nginx: log_format (include request ID), ship via Fluent Bit/Vector/Promtail; metrics via stub_status + Prometheus exporter.

    • Caddy: JSON logs out of the box; integrate with Loki/ELK/Datadog; metrics available via modules or logs metrics pipelines.

    Nginx JSON logs:

    nginx
    log_format json_combined escape=json '{"time":"$time_iso8601","remote":"$remote_addr","host":"$host","method":"$request_method","uri":"$request_uri","status":$status,"size":$bytes_sent,"req_time":$request_time,"upstream":"$upstream_addr"}';
    access_log /var/log/nginx/access.json json_combined;
    

    Caddy (log level/site logger):

    caddy
    {
     log {
     level INFO
     }
    }
    example.com {
     log {
     output file /var/log/caddy/access.json
     format json
     }
     reverse_proxy 127.0.0.1:3000
    }
    

    11) Migration CheatSheet (Nginx Caddy)

    Use Case Nginx Directive Caddyfile Equivalent
    Redirect www apex return 301 https://example.com$request_uri; redir https://example.com{uri} permanent
    Proxy upstream proxy_pass http://127.0.0.1:3000; reverse_proxy 127.0.0.1:3000
    Add headers add_header ... header { ... }
    Gzip/Brotli gzip on; brotli on; encode gzip zstd
    Healthcheck location =/healthz { return 200; } @health path /healthz + respond @health 200
    Rate limit limit_req_zone ... Plugin / CDN (recommend CDN)
    Proxy cache proxy_cache ... Plugin / CDN (recommend CDN)

    12) Troubleshooting Tips

    • Certs won't issue: Check DNS A/AAAA, port 80/443 open, and no extra listener binding to :80.

    • HTTP/3 not working: Confirm browser support, server build, and QUIC/UDP open (port 443/UDP).

    • Real IP missing: Validate CF ranges and headers; ensure no double proxying strips headers.

    • WebSockets disconnects: Keepalive, proxy_http_version 1.1, upgrade headers (Nginx); timeouts and buffers.

    • CORS/auth bugs: Terminate headers at one layer; pass through required Authorization and Cookie headers.


    Conclusion / Next Steps

    • Pick Caddy for autoHTTPS + HTTP/3 and a fast, delightful config experience.

    • Pick Nginx when you need builtin caching, ratelimits, and ultramature knobs for hightraffic edges.

    • Either way, you can Deploy, Secure, Optimize, and Scale reliably on a tuned KVM NVMe VPS.

    Need a reference implementation See our endtoend guide below.


    Was this article helpful?

    Your answer helps us decide what to improve next.

    Still need help? Open a support ticket and our team will reply.

    Prefer an app? Add this site to your home screen.Get the app
    Nginx vs Caddy for App Gateways 2025 Engineer's Guide - Knowledge Base