Capstone: Designing a Production Nginx Reverse-Proxy Tier

Nginx In Depth

Chapter 10 · Capstone: Designing a Production Nginx Reverse-Proxy Tier

Web Servers Fundamentals' own capstone compared three servers across several scenarios. This one is different, by design: a single, cohesive worked example, layering in each of this course's own eight prior chapters, one at a time, until one complete production-shaped configuration exists.

The Scenario

A containerized API service runs three backend instances. It needs traffic distributed across them sensibly, protection from slow clients and abusive traffic, caching for its read-heavy public endpoints, correct client-identity forwarding, TLS termination, and basic real-time visibility — every one of this course's own chapters, applied to one real job.

Step 1 — Worker Tuning (Chapter 2)

worker_processes auto; events { worker_connections 2048; }

worker_processes auto matches the container's own allotted CPU cores; worker_connections 2048 is set generously, on the assumption the OS file-descriptor limit has already been raised to match, per Chapter 2's own warning about the real ceiling being whichever of the two numbers is lower.

Step 2 — The Upstream Pool (Chapter 4)

upstream api_backend { least_conn; server api-1:3000; server api-2:3000; server api-3:3000; keepalive 32; }

Three backend instances form the pool, with keepalive 32 letting Nginx reuse its own connections to them (Chapter 4).

Step 3 — Choosing a Load Balancing Algorithm (Chapter 5)

least_conn, already shown above, is chosen deliberately: this API's own request times vary meaningfully by endpoint (Chapter 5's own case for preferring it over plain round robin), so routing toward whichever backend is currently least busy fits better than blindly cycling through the pool.

Step 4 — Reverse Proxy Headers (Chapter 6)

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr;

These live in the outer server {} block, so every location nested inside it inherits them automatically — a deliberate choice, since Chapter 6's own warning box showed that any nested block adding its own proxy_set_header would otherwise silently lose all of these.

Step 5 — TLS Termination

TLS itself is configured exactly per Web Servers Fundamentals Chapter 7 — listen 443 ssl, ssl_certificate/ssl_certificate_key — not repeated here since that chapter already covers it fully; this capstone assumes it's already in place ahead of everything else below.

Step 6 — Caching Read-Heavy Endpoints (Chapter 7)

proxy_cache_path /var/cache/nginx keys_zone=api_cache:10m max_size=1g inactive=30m; location /api/public/ { proxy_cache api_cache; proxy_cache_valid 200 5m; proxy_cache_bypass $cookie_session_id; proxy_no_cache $cookie_session_id; }

Only the public, read-heavy /api/public/ path is cached — proxy_cache_bypass/proxy_no_cache on the session cookie keep any authenticated request out of the cache in both directions, exactly the discipline Chapter 7's own warning box insisted on.

Step 7 — Rate Limiting (Chapter 8)

limit_req_zone $binary_remote_addr zone=api_limit:10m rate=20r/s; location /api/ { limit_req zone=api_limit burst=40 nodelay; proxy_pass http://api_backend; }

A per-IP rate limit protects the backend pool from any single abusive client, with a burst allowance absorbing normal traffic spikes without rejecting legitimate short bursts.

Step 8 — Performance & Monitoring (Chapter 9)

sendfile on; tcp_nopush on; tcp_nodelay on; proxy_buffering on; location /nginx_status { stub_status; allow 127.0.0.1; deny all; }

proxy_buffering stays on deliberately (Chapter 9's own warning box), and stub_status is restricted to internal access only, giving ops visibility without exposing it publicly.

Capstone StepChapter It Draws From
Step 1 — Worker tuningChapter 2
Step 2 — Upstream poolChapter 4
Step 3 — least_conn algorithmChapter 5
Step 4 — Proxy headersChapter 6
Step 5 — TLS terminationWeb Servers Fundamentals Chapter 7
Step 6 — CachingChapter 7
Step 7 — Rate limitingChapter 8
Step 8 — Performance & monitoringChapter 9
Two capstones, one full journey
Web Servers Fundamentals' own capstone answered "which server fits this job" — this one answers "given Nginx was already chosen, how do you actually configure it well." Together, the two capstones cover the full journey from choosing a web server to running a genuinely production-shaped configuration of it.
This config is a solid foundation, not a complete go-live checklist
Every directive above reflects real material from this course, but a genuine production rollout still needs steps beyond any single config file: validating the config (nginx -t) before reloading, a gradual rollout rather than switching all traffic at once, and real alerting configured on top of whatever monitoring is in place (Observability's own material, well beyond stub_status's own simple snapshot). Treat this chapter's config as a strong, correct starting point — not a substitute for an actual deployment process.

Hands-On Exercises

Exercise 1

This capstone's config places X-Forwarded-For, X-Forwarded-Proto, and X-Real-IP in the outer server {} block rather than inside the /api/ location block. Explain why this placement was a deliberate choice, referencing the relevant earlier chapter.

📄 View solution
Exercise 2

A new /api/private/ endpoint is added, requiring authentication, returning genuinely personalized data per user. Should it be added to the api_cache zone the same way /api/public/ was? Explain your answer using this course's own material.

📄 View solution
Exercise 3

Write a short chapter-attribution summary (2-3 sentences) explaining how this capstone's own shape differs from Web Servers Fundamentals' own capstone, and why that difference makes sense given what each course actually covers.

📄 View solution
Course Complete

Nginx In Depth — 10 of 10 chapters complete. The Web Servers subject now has both its comparative foundation and its Nginx deep dive.

Chapter 10 Quick Reference

  • This capstone builds one cohesive production config, layering in Chapters 2, 4, 5, 6, 7, 8, and 9 step by step — a deliberately different shape from Web Servers Fundamentals' own multi-scenario capstone
  • Proxy headers belong in the outer server block so every nested location inherits them (Chapter 6)
  • Only non-personalized, read-heavy endpoints belong in a cache zone; authenticated/personalized endpoints need proxy_no_cache or their own identity-aware cache key (Chapter 7)
  • A working config is a foundation, not a complete production rollout — validation, gradual deployment, and real alerting still matter beyond this chapter's own scope