AV
HomeAboutProjectBlog

© 2026 Ave syah Shina. All rights reserved.

  1. Home
  2. Blog
  3. 10 — Web Servers — Nginx, Apache, Caddy, and What They Actually Do

10 — Web Servers — Nginx, Apache, Caddy, and What They Actually Do

August 13, 20268 min read
Download as Markdown

"The application itself" is what I used to call the web server, and the conflation cost me hours reading config files. The separation that fixed it: a web server is the front door that terminates connections, serves static files, and reverse-proxies to my application; the application is the code that produces dynamic responses. [1] They're different processes with different jobs, and conflating them blocks understanding both.

The frame that helped is what the web server does that my app doesn't want to. Terminating TLS, serving static files (images, CSS, JS) straight from disk, handling thousands of idle connections, load-balancing across multiple app instances — these are things a dedicated, battle-tested server process does better than my application code. The web server sits in front, handles the parts that are generic, and forwards the dynamic requests to my app over a local socket. This separation is the whole shape of a production deployment.

clients Web server — listens :80 / :443 terminate TLS serve static files reverse-proxy dynamic requests your app process on :3000 Nginx Apache Caddy IIS

The job: terminate, serve, proxy

Three responsibilities define a web server in front of an application:

  • Terminate TLS. The HTTPS connection from the client ends at the web server, which holds the TLS certificate, decrypts the request, and forwards plaintext to the app over the loopback interface. The app never has to know about certificates. This is the separation I flagged in the internet-fundamentals notes.
  • Serve static files. Images, CSS, JS, fonts — files that don't change per request. The web server reads them straight from disk and streams them back, far faster than asking the app to do it. Some servers cache file metadata; all of them handle the OS-level I/O efficiently.
  • Reverse-proxy dynamic requests. Anything that needs the application (an API call, a rendered page) gets forwarded to the app process — over HTTP, or over a faster binary protocol like FastCGI or a Unix domain socket. The server holds the connection from the client while the app produces the response, then streams the response back.

The win of putting a server in front is that the app process is freed from the generic work. The app handles business logic; the server handles connection management, TLS, static files, and often load-balancing across multiple app instances.

Nginx: the event-driven default

Nginx is the high-performance, event-driven web server and reverse proxy [2]. Where older servers used a thread or process per connection (expensive at scale), Nginx uses an asynchronous, event-driven loop that handles thousands of concurrent connections per worker process with minimal memory. That architecture is why Nginx dominates as the "thing in front of your app" — it's extraordinarily efficient at holding many idle connections and proxying the busy ones.

Nginx is configured via a declarative config file (nginx.conf) where you declare server blocks, location blocks, upstream pools, and proxy rules. The learning curve is the config syntax; the payoff is a server that handles enormous load on modest hardware. Nginx is the default reverse proxy for modern deployments — the thing sitting in front of Node, Go, Python, Ruby apps, terminating TLS and load-balancing.

Apache: the modular veteran

Apache HTTP Server is the older, module-based web server, the A in LAMP [3]. Apache dominated the early web and remains widely used, particularly in shared hosting and legacy setups. Its defining trait is a modular architecture — functionality (URL rewriting, auth, PHP execution) is added via loadable modules — and its process/thread-per-connection model (in the prefork/worker MPMs), which is simpler to reason about but more memory-hungry than Nginx's event loop at high concurrency.

Apache vs. Nginx is a classic comparison, and the practical summary: Nginx excels at serving static content and reverse-proxying at scale; Apache excels at per-directory configuration (.htaccess) and module-based dynamic content. In production they're often combined — Nginx in front for static + proxy, Apache behind for dynamic — though for greenfield work Nginx-alone-in-front-of-your-app is now the more common shape.

Caddy: the zero-config modern option

Caddy is the modern, Go-based web server whose killer feature is automatic HTTPS — it provisions and renews Let's Encrypt TLS certificates with no configuration [4]. Caddy's pitch is that for small-to-medium projects, the TLS/cert choreography that Nginx requires (certbot, cron, reload) is built in and invisible. Its config is also dramatically simpler than Nginx's for the common case.

Caddy is the choice when "hassle-free setup" is the priority — a personal project, a small service, anything where I'd rather not write Nginx config and run cert management. It's less battle-tested at the extreme scale Nginx operates at, but for the vast majority of deployments it's more than sufficient, and the automatic HTTPS is genuinely a quality-of-life upgrade.

MS IIS: the Windows enterprise option

Microsoft IIS (Internet Information Services) is the web server bundled with Windows Server, the choice for ASP.NET and Windows-centric enterprise deployments [5]. IIS integrates tightly with Windows authentication, SSL/TLS, and the .NET runtime. If the stack is Microsoft-first, IIS is the natural — often mandatory — server; outside the Windows world it's rarely chosen.

Servers vs. application frameworks

One confusion worth heading off: many application frameworks ship with a "development server" (Node's http, Django's runserver, Flask's built-in). Those are not production web servers — they're single-process, not hardened, and exist only for local development. In production, the app runs behind a real web server (or a PaaS that provides one). The portfolio's Next.js app runs behind Vercel's edge network, which is effectively a managed reverse proxy doing the terminate-serve-proxy job at scale — the same three responsibilities, just packaged.

How I use this

The practical decision is which server to put in front of the app, and the answer is almost always Nginx or Caddy:

  • Default, scale, control → Nginx. The most documented, most proven, most efficient reverse proxy. The config is verbose but I can make it do anything.
  • Small project, hate writing config → Caddy. Automatic HTTPS, two-line config, "just works."
  • Windows / ASP.NET shop → IIS. Not a choice, a constraint.
  • PaaS (Vercel, Render, Fly) → managed. The server is provided; I never touch it.

The habit that matters: I never expose my application process directly to the internet. There's always a web server (or managed equivalent) in front, terminating TLS and absorbing the connection-management work. My app listens on a local port and answers proxied requests. That separation is the single most important deployment discipline — it lets the app and the server each do what they're good at.

References

[1] Mozilla, "What is a web server?," MDN Web Docs. [Online]. Available: https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_web_server

[2] "Nginx." [Online]. Available: https://nginx.org/

[3] "Apache HTTP Server." [Online]. Available: https://httpd.apache.org/

[4] "Caddy Server." [Online]. Available: https://caddyserver.com/

[5] Microsoft, "Internet Information Services (IIS)." [Online]. Available: https://www.iis.net/

Knowledge check · Question 1 of 5

What are the three responsibilities a web server typically handles in front of an application?

Comments

Leave a Comment

You must be signed in to comment

0 Comments

No comments yet. Be the first to comment!