Why small TCP writes stall: Nagle's algorithm, delayed ACKs and TCP_NODELAY
Split a request into two small writes and the second piece can wait for the peer's ACK. What RFC 896, 1122 and 9293 say about Nagle's algorithm and delayed ACKs, and what TCP_NODELAY changes.
A client sends a request header with one write() and the body with another. Once the whole request has arrived the server processes it quickly, yet the response comes back late, because the body itself reaches the server late. One thing worth suspecting here is the interaction between Nagle’s algorithm on the sending side and delayed ACKs on the receiving side. Each rule on its own is a sensible way to send fewer packets; in a request-response exchange they can end up waiting on each other.
Not every slow small write comes from this interaction, though. The sections below separate where each rule came from, what the standards require, and under which conditions the stall appears. The baseline is RFC 9293 (August 2022, STD 7), the current TCP specification, read alongside RFC 1122, which held the earlier requirements, and RFC 896 for the history. Linux behaviour rests only on the tcp(7) page from the man-pages repository (an unreleased development version), checked on 18 October 2026.
Sending side: Nagle’s algorithm
The starting point is RFC 896, written by John Nagle in 1984. Its opening paragraph says it is a memo for discussion and does not specify any standards. The problem it looked at was Telnet-style traffic sent one keystroke at a time: each character went out as a 41-byte packet, one byte of data and 40 bytes of header, and on a heavily loaded network that overhead added to congestion.
The rule RFC 896 proposed is simple. If any previously sent data is still unacknowledged, do not send a new segment when new data arrives from the user. The memo says this inhibition is unconditional, with no timers or size tests. On an idle connection there is no unacknowledged data, so a write goes out at once; held data goes out together when the peer’s ACK arrives.
RFC 1122 (1989) turned this into a requirement and added one condition. While data is unacknowledged, the sending TCP buffers user data regardless of the PSH bit, and sends once the outstanding data is acknowledged or once it can fill a full-sized segment (Eff.snd.MSS bytes). Implementations SHOULD implement Nagle’s algorithm, and there MUST be a way for an application to disable it on an individual connection.
RFC 9293 replaces the TCP parts of RFC 1122 and keeps the same text in section 3.7.4: implementing Nagle is SHLD-7, the per-connection off switch is MUST-17. The section notes that the algorithm is implemented in most current TCP code bases, sometimes with minor variations. In every case, sending is also limited by slow start.
Receiving side: delayed ACKs
A receiver can be more efficient by sending fewer than one ACK per data segment. That is a delayed ACK. RFC 1122 section 4.2.3.2 and RFC 9293 section 3.8.6.3 require the following:
- A TCP SHOULD implement delayed ACKs (SHLD-18 in RFC 9293).
- The delay MUST be less than 0.5 seconds (MUST-40).
- In a stream of full-sized segments, there SHOULD be an ACK for at least every second segment (SHLD-19). RFC 9293 adds “or 2×RMSS bytes of new data”.
RFC 1122 explains that one reason to hold back the ACK is to give the application a chance to update the window and send a response immediately, so the ACK, the window update and the response data share one segment. Its example is character-mode remote login, where this can cut the number of segments the server sends by a factor of three.
The standard sets an upper bound (under 0.5 seconds); the actual timer value is the implementation’s choice. This post does not cover any particular operating system’s value.
How the two meet
Say the request is written in two pieces and the server can only respond once it has all of it. The connection starts with no unacknowledged data. One more premise: the server’s delayed-ACK implementation chooses to delay the ACK for this first small segment. The standard allows that but does not require it, so another implementation may acknowledge the segment right away.
Client (Nagle on) Server (delayed ACK)
write(header) ── segment 1 ────────────▶ received; request incomplete, can't respond
write(body) held: data unacknowledged no response to carry the ACK; may delay it
◀────────────────────── ACK ── if nothing else ACKs first, the timer sends it (under 0.5 s per the standard)
── segment 2 ────────────▶ request complete, processing starts
◀─────────────────── response ──
- The header goes out immediately because nothing is unacknowledged.
- The body is small and the header is not yet acknowledged, so Nagle’s rule holds it.
- With only part of the request, the server cannot build a response and has no data to piggyback an ACK on. A single small segment doesn’t meet the “every second full-sized segment” condition either, so the server may delay the ACK, and in this scenario it does.
- If nothing else triggers an ACK first, the server’s delayed-ACK timer eventually sends it. The body goes out only when that ACK arrives.
This sequence is derived from the definitions of the two rules plus the premise above. RFC 9293 Appendix A.3 states that common operating systems implement and enable both Nagle’s algorithm and delayed ACKs by default, and that for request-response applications the combination can result in poor application performance.
Turned around, breaking any one of those conditions removes this stall: the request is written in one go, the ACK for the first segment has already arrived before the second write, the server has data to send so the ACK leaves with it, or the server acknowledges the first segment without delaying it. And small writes can be delayed for other reasons too. Sending is also bounded by slow start and by the usable window, the peer’s offered window minus data sent but not yet acknowledged. Before changing options, take a packet capture and check whether the second segment really leaves right after the peer’s ACK.
How to avoid it
Write the message in one go
If the application gathers the header and body into a buffer and writes them together, Nagle’s rule does not hold the send on a connection with no unacknowledged data, and the packet count doesn’t grow. If the message is larger than one segment, though, the full segments go out while a small remainder at the end can still wait for the ACK of what preceded it.
Turn Nagle off with TCP_NODELAY
On Linux, the per-connection off switch that the standard requires is the TCP_NODELAY socket option. RFC 9293 Appendix A.3 also notes that disabling Nagle is generally supported by a socket option and that many applications simply do so. According to Linux tcp(7), with TCP_NODELAY set, segments are always sent as soon as possible even when there is only a little data; without it, data is buffered until there is enough to send, avoiding frequent small packets.
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
int one = 1;
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof one) == -1) {
/* handle errno */
}
The cost is plain: with Nagle off, a message written in many small pieces can turn each write into its own small packet. That is the problem RFC 896 set out to fix, so even with TCP_NODELAY it pays to write each message in one go.
Linux-only options
tcp(7) says both of these should not be used in code intended to be portable.
TCP_CORK: the opposite of turning Nagle off; it gathers more. While set, partial frames are not sent; clearing it sends what has queued. The page describes it as useful for prepending headers beforesendfile(2). The current implementation has a 200-millisecond ceiling, after which queued data is sent automatically. It overridesTCP_NODELAY, but settingTCP_NODELAYforces an explicit flush of pending output.TCP_QUICKACK: switches the receiver into quickack mode, where ACKs are sent immediately rather than delayed. It is not permanent, only a switch into or out of the mode; later protocol processing inside the kernel can change the mode again.
A variant outside the standard
RFC 9293 Appendix A.3 mentions a modification to Nagle’s algorithm (Minshall’s proposal) that improves this case. It says the modification is implemented in some common operating systems and does not affect interoperability, but has not been added to the TCP standard. Which systems use it is not something this post checked.
In short
- RFC 896 is where Nagle’s algorithm comes from, but it is not a standard.
- The requirements were set in RFC 1122 and RFC 9293 is the current reference: implementing Nagle is SHOULD, a per-connection off switch is MUST, delayed ACKs are SHOULD with a MUST of under 0.5 seconds.
- The stall needs a small second write, unacknowledged data, and a receiver that can’t respond yet and actually chooses to delay its ACK, all at once.
- Write each message in one go first, and use
TCP_NODELAYif needed.TCP_CORKandTCP_QUICKACKapply only to Linux.
Sources
- RFC 896 Congestion Control in IP/TCP Internetworks: opening note, “The small-packet problem”, “The solution to the small-packet problem”
- RFC 1122 Requirements for Internet Hosts – Communication Layers: sections 4.2.3.2, 4.2.3.4, 4.2.5
- RFC 9293 Transmission Control Protocol (TCP): sections 3.7.4, 3.8.6.2.1, 3.8.6.3, 3.9.1.2, Appendix A.3
- Linux man-pages
tcp(7): Socket options,TCP_CORK,TCP_NODELAY,TCP_QUICKACK
Links and content checked on 18 October 2026.