Performance Tuning & Troubleshooting
Nginx In Depth
Chapter 9 · Performance Tuning & Troubleshooting
Web Servers Fundamentals Chapter 9 covered keep-alive and gzip at a general, three-server level. This chapter is Nginx-specific: how it actually moves bytes off disk and onto the network, how it manages a backend response before handing it to a client, and the tools to find out what's actually happening when something's slow.
sendfile, tcp_nopush & tcp_nodelay
sendfile lets the kernel copy a static file's data directly to the network socket, without that data passing through Nginx's own userspace process memory first — a real, meaningful win specifically for serving static files. tcp_nopush tells the kernel to wait and accumulate a full network packet's worth of data before sending, working alongside sendfile to batch data efficiently. tcp_nodelay does the seemingly opposite thing — disabling Nagle's algorithm so small packets are sent immediately rather than held back to be batched. The pairing isn't actually contradictory: Nginx uses tcp_nopush to batch data efficiently through most of a response, then tcp_nodelay to flush the final packet immediately rather than waiting — both enabled together is the correct, common configuration, not a conflict to resolve.
Proxy Buffering
By default, Nginx buffers a backend's response before sending it on to the client — letting the backend connection close quickly even if the client itself is slow to receive the data, rather than holding that backend connection open for the full duration of a slow client transfer. proxy_buffer_size sets the buffer for the response headers specifically; proxy_buffers sets the number and size of buffers used for the response body. A response larger than the configured buffer space spills over to a temporary file on disk, which can introduce real disk I/O the config didn't anticipate if buffers are sized too small for typical response sizes.
stub_status: A Real-Time Window Into Nginx
The stub_status module exposes a simple plain-text endpoint showing active connections right now, and running totals for accepted/handled/total requests, plus how many connections are currently reading, writing, or waiting. It's a lightweight, immediate diagnostic tool — genuinely useful as a first stop, though it's not a substitute for a real observability stack (Observability's own material) that retains history, alerts, and correlates across multiple systems.
Diagnosing Common Misconfigurations
- Check the error log first (Web Servers Fundamentals Chapter 8) — many misconfigurations announce themselves there directly.
- Check
stub_statusto see whether Nginx itself is under load, or whether the real bottleneck is a slow backend it's simply waiting on. - Check
X-Cache-Status(Chapter 7) to rule caching in or out of a given slow response. - Check whether
worker_connectionsor the OS file-descriptor limit (Chapter 2) is silently being hit, rather than assuming the problem is purely about traffic volume.
| Directive | Purpose |
|---|---|
sendfile | Kernel sends file data directly, bypassing userspace copy |
tcp_nopush/tcp_nodelay | Batch most of a response, flush the final packet immediately |
proxy_buffering | Buffer a backend response so a slow client doesn't hold the backend connection open |
stub_status | Real-time connection/request counters for quick diagnosis |
stub_status's value is its immediacy — a quick, no-setup snapshot of what Nginx is doing right now. Observability's own material covers what comes next: retaining that data over time, alerting when it crosses a threshold, and correlating it against the rest of a system's own metrics — none of which stub_status itself does.
proxy_buffering off means Nginx passes each chunk of the backend's response straight through to the client as it arrives — if the client is slow to receive data, the connection to the backend stays open for the entire duration of that slow transfer, tying up a backend connection (and the resources behind it) for much longer than buffering would have. The real trade-off is backend resource protection (buffering on) against marginally lower latency for an already-fast client (buffering off) — not a straightforward "off is obviously better" choice.
Hands-On Exercises
Write the three directives that enable sendfile and the correct paired TCP behavior for serving static files efficiently.
📄 View solutionA backend response is under heavy load and its connections seem to stay open unusually long whenever a client on a slow mobile connection requests a large file. A teammate suggests turning proxy_buffering off to "speed things up." Using this chapter's own warning box, explain why this is likely to make the actual problem worse, not better.
📄 View solutionA site is running slowly. Using this chapter's own diagnostic checklist, list the order of checks you'd perform to figure out whether the bottleneck is Nginx itself, the backend, caching, or a connection limit — and briefly explain what each check would tell you.
📄 View solutionChapter 9 Quick Reference
- sendfile — kernel sends file data directly; tcp_nopush/tcp_nodelay — batch most of a response, flush the last packet immediately, both on together (not conflicting)
- proxy_buffering — protects backend connections from slow clients; turning it off shifts that cost onto the backend, not automatically "faster"
- stub_status — a lightweight, real-time first diagnostic stop; not a substitute for a real observability stack
- Diagnostic order: error log → stub_status → X-Cache-Status → worker_connections/file-descriptor limits