[{"data":1,"prerenderedAt":29},["ShallowReactive",2],{"$fewlykehXEUW304XbTxS90ABAIwpcKBPZX2sywXiQS1s":3},{"slug":4,"title":5,"description":6,"date":7,"tags":8,"draft":13,"readingMinutes":14,"html":15,"related":16},"nginx-vs-caddy","Nginx vs Caddy: Which Reverse Proxy I'd Pick in 2026","Nginx vs Caddy for a Node, Laravel or Nuxt app on one server: HTTPS, WebSockets, headers, the new nginx ACME module, and when each one wins.","2026-11-02",[9,10,11,12],"nginx","caddy","devops","reverse-proxy",false,4,"\u003Cp>For one app on one server, I&#39;d pick Caddy. It gets a certificate on its own, renews it, speaks HTTP/3, proxies WebSockets and passes the right forwarding headers, all from a three-line config. Nginx is still my choice when the server is busy, when I need fine control over caching and buffering, or when nginx is already there and someone else on the team knows it. The gap between them got smaller this year, and most comparisons online haven&#39;t caught up.\u003C/p>\n\u003Cp>Versions I checked on November 2, 2026: Caddy 2.11.7 (released October 3) and nginx 1.31.8 mainline / 1.30.6 stable, from the \u003Ca href=\"https://github.com/caddyserver/caddy/releases\">Caddy releases\u003C/a> and the \u003Ca href=\"https://nginx.org/en/CHANGES\">nginx changelog\u003C/a>.\u003C/p>\n\u003Ch2>The same job in both\u003C/h2>\n\u003Cp>The job is the most common one I&#39;m asked to set up: a Node app on port 3000 behind a domain, HTTPS, WebSockets working, the app seeing the real client IP.\u003C/p>\n\u003Cimg src=\"/blog/nginx-vs-caddy/nginx-vs-caddy-same-proxy.webp\" alt=\"The same HTTPS reverse proxy to a Node app on port 3000: a 3-line Caddyfile next to a 21-line nginx config plus a certbot command\" width=\"1600\" height=\"900\" loading=\"lazy\">\u003Cp>Caddy:\u003C/p>\n\u003Cpre>\u003Ccode>app.example.com {\n\treverse_proxy localhost:3000\n}\n\u003C/code>\u003C/pre>\n\u003Cp>That&#39;s the whole file. Caddy sees a real domain, gets a certificate from Let&#39;s Encrypt (or ZeroSSL if that fails), redirects HTTP to HTTPS, and renews before expiry. The \u003Ca href=\"https://caddyserver.com/docs/caddyfile/directives/reverse_proxy\">reverse_proxy docs\u003C/a> list what it does by default: it sets \u003Ccode>X-Forwarded-For\u003C/code>, \u003Ccode>X-Forwarded-Proto\u003C/code> and \u003Ccode>X-Forwarded-Host\u003C/code>, and WebSocket upgrades just work.\u003C/p>\n\u003Cp>Nginx needs a \u003Ccode>server\u003C/code> block for port 80 that redirects, a \u003Ccode>server\u003C/code> block for 443 with the certificate paths, \u003Ccode>proxy_set_header\u003C/code> lines for \u003Ccode>Host\u003C/code>, \u003Ccode>X-Real-IP\u003C/code>, \u003Ccode>X-Forwarded-For\u003C/code> and \u003Ccode>X-Forwarded-Proto\u003C/code>, the \u003Ccode>Upgrade\u003C/code> and \u003Ccode>Connection\u003C/code> headers for WebSockets, and certbot to fetch the certificate and a timer to renew it. About 20 lines and a second tool. None of it is hard. All of it is something you can get slightly wrong, and the usual mistake is forgetting \u003Ccode>X-Forwarded-Proto\u003C/code>, so the app thinks every request is plain HTTP and generates \u003Ccode>http://\u003C/code> links or loops on its own HTTPS redirect.\u003C/p>\n\u003Ch2>What changed in nginx this year\u003C/h2>\n\u003Cp>Two things that most &quot;nginx vs caddy&quot; posts still get wrong.\u003C/p>\n\u003Cp>First, nginx 1.29.7 (March 2026) changed the proxy defaults: \u003Ccode>proxy_http_version\u003C/code> is now 1.1 and keepalive to upstreams is on by default. Old configs that set \u003Ccode>proxy_http_version 1.1;\u003C/code> by hand still work, they&#39;re just redundant now. But Ubuntu 24.04 ships nginx 1.24 from its own repository, so on a typical VPS you&#39;re still on the old behaviour unless you use the nginx.org packages.\u003C/p>\n\u003Cp>Second, nginx has native ACME now. The \u003Ca href=\"https://nginx.org/en/docs/http/ngx_http_acme_module.html\">ngx_http_acme_module\u003C/a> is available as a prebuilt \u003Ccode>nginx-module-acme\u003C/code> package and gets and renews certificates from inside nginx, no certbot. It&#39;s more verbose than Caddy&#39;s version (an \u003Ccode>acme_issuer\u003C/code> block, a shared zone, a \u003Ccode>resolver\u003C/code>, and you still need the port 80 listener for the HTTP-01 challenge), and it&#39;s a separate module to install and load. Still, &quot;nginx can&#39;t do automatic HTTPS&quot; is no longer true. I&#39;d use it on new nginx setups; I wouldn&#39;t rip out a working certbot setup for it.\u003C/p>\n\u003Ch2>Where nginx still wins\u003C/h2>\n\u003Cp>\u003Cstrong>Load.\u003C/strong> Nginx is C with an event loop that has been tuned for twenty years. Caddy is Go and is fast, but under heavy traffic nginx uses less memory and CPU for the same work. On a $6 VPS serving one app, you will never see the difference. On a box doing tens of thousands of requests a second, you will.\u003C/p>\n\u003Cp>\u003Cstrong>Caching and buffering.\u003C/strong> \u003Ccode>proxy_cache\u003C/code> with its keys, lock and stale-while-revalidate options is a serious cache. Caddy&#39;s caching lives in a third-party plugin, which means building a custom binary with \u003Ccode>xcaddy\u003C/code>. Same story for rate limiting.\u003C/p>\n\u003Cp>\u003Cstrong>Everyone knows it.\u003C/strong> Every Laravel Forge, Ploi and RunCloud server is nginx. Every Stack Overflow answer about a 502 is nginx. When I hand a server over to a client&#39;s next developer, nginx is the thing they&#39;ll be able to read. That matters more than it sounds.\u003C/p>\n\u003Cp>\u003Cstrong>PHP.\u003C/strong> Both work with PHP-FPM (Caddy has \u003Ccode>php_fastcgi\u003C/code>, which is pleasant), but the nginx + PHP-FPM setup for Laravel is so well trodden that I don&#39;t see a reason to change it on an existing server.\u003C/p>\n\u003Ch2>Where Caddy wins\u003C/h2>\n\u003Cp>Small servers where I&#39;m the only one touching the config. Side projects, staging boxes, an internal tool on a VPS, a Nuxt app behind a proxy. Also anything with lots of customer domains: Caddy&#39;s on-demand TLS issues a certificate the first time a new hostname connects (with an \u003Ccode>ask\u003C/code> endpoint so random domains can&#39;t make it request certificates). Doing that with nginx means writing your own glue.\u003C/p>\n\u003Cp>One thing to know about Caddy: it runs an admin API on \u003Ccode>localhost:2019\u003C/code> by default, which can change the running config. It only listens on localhost, so it&#39;s fine on a normal server, but in a container or with other users on the box I turn it off with \u003Ccode>admin off\u003C/code> in the global options.\u003C/p>\n\u003Ch2>What I actually run\u003C/h2>\n\u003Cp>New single-app VPS for a client: Caddy, unless they already have a hosting panel that uses nginx. Existing server on nginx: leave it, switch to the nginx.org package if it&#39;s an old distro version, and look at the ACME module next time the certificates need attention. High-traffic or cache-heavy: nginx, no discussion.\u003C/p>\n\u003Cp>If you have an app that needs to get onto a server properly, with HTTPS, deploys and backups that someone has thought about, that&#39;s the kind of \u003Ca href=\"/services/devops-consulting\">DevOps work\u003C/a> I do. The \u003Ca href=\"https://calendly.com/director-norsky/meeting\">first call is free\u003C/a>.\u003C/p>\n",[17,21,25],{"slug":18,"title":19,"description":20},"node-cron","node-cron in Production: Overlaps, Replicas and DST","node-cron 4 in Node.js, checked November 2026: noOverlap, running on one replica, the day-of-week AND rule, DST gaps, and when systemd or a queue is better.",{"slug":22,"title":23,"description":24},"pgbouncer","PgBouncer: What Transaction Mode Breaks, and the Fix","PgBouncer in transaction mode lets 1,000 clients share 20 Postgres connections. What it breaks (SET, LISTEN, advisory locks), and what 1.24 fixed.",{"slug":26,"title":27,"description":28},"valkey-vs-redis","Valkey vs Redis: Which One I'd Run in Late 2026","Valkey vs Redis in October 2026: licenses, Valkey 9.1 vs Redis 8.10, the data-file break after Redis 7.4, and ElastiCache's 20-33% Valkey discount.",1794095794455]