...

HTTP Keep-Alive Tuning: verbindingsbeheer en serverbelasting

HTTP Keep-Alive vermindert handshakes en houdt verbindingen open, zodat meerdere verzoeken via dezelfde socket kunnen worden uitgevoerd en de Serverbelasting daalt. Met gerichte afstemming controleer ik time-outs, limieten en workers, verlaag ik Latencies en verhoog de doorvoer zonder wijzigingen in de code.

Centrale punten

  • Hergebruik van verbindingen vermindert CPU-overhead en handshakes.
  • Kort Time-outs voorkomen lege verbindingen.
  • Schoon Grenzen voor keepalive_requests stabiliseren belasting.
  • HTTP/2 en HTTP/3 nog sterker bundelen.
  • Realistisch belastingtests Sla de instellingen op.

Hoe HTTP Keep-Alive werkt

In plaats van voor elke bron een nieuwe TCP-verbinding te openen, gebruik ik een bestaande verbinding opnieuw en bespaar zo Handdrukken en roundtrips. Dit vermindert wachttijden, omdat noch TCP- noch TLS-setups continu actief hoeven te zijn en de pijplijn snel reageert. De client herkent aan de header dat de verbinding open blijft en verstuurt verdere verzoeken achtereenvolgens of met multiplexing (bij HTTP/2/3) via dezelfde Socket. De server beheert de idle-fase via een keep-alive-time-out en verbreekt de verbinding als er te lang geen verzoek volgt. Dit gedrag versnelt pagina's met veel assets aanzienlijk en ontlast de CPU, omdat er minder verbindingen worden opgebouwd.

Hergebruik van verbindingen: effect op de serverbelasting

Elke vermeden nieuwe verbinding bespaart CPU-tijd voor kernel- en TLS-werk, wat ik in de monitoring zie als een vlakkere belastingscurve. Gegevens tonen aan dat het hergebruik van bestaande sockets de doorvoer met wel 50 procent kan verhogen wanneer er veel kleine verzoeken binnenkomen. In benchmarks met veel GET-verzoeken wordt de totale duur soms met een factor drie gehalveerd, omdat er minder handshakes en minder contextwisselingen plaatsvinden. Ook de netwerkbelasting neemt af, omdat SYN/ACK-pakketten minder vaak voorkomen en de server meer budget overhoudt voor de eigenlijke applicatielogica. Deze interactie zorgt voor snellere antwoorden en stabielere Reactietijden onder belasting.

Risico's: te lange time-outs en open verbindingen

Een te royale keep-alive-time-out laat verbindingen inactief blijven en blokkeert Werknemer of threads, hoewel er geen verzoek is. Bij veel verkeer groeien open sockets, bereiken ze de grenzen van de bestandsdescriptoren en zorgen ze voor een hoog geheugengebruik. Bovendien veroorzaken ongeschikte client-time-outs „dode“ verbindingen, die verzoeken naar reeds gesloten sockets sturen en foutmeldingen produceren. Ingress- en NAT-gateways kunnen inactieve lijnen eerder sluiten dan de server, wat leidt tot sporadische resets. Daarom beperk ik bewust de inactieve tijd, stel ik duidelijke limieten in en houd ik de tegenpartij (clients, proxies) in het oog.

HTTP Keep-Alive versus TCP Keepalive

Ik maak een strikt onderscheid tussen HTTP Keep-Alive (persistente verbindingen op applicatieniveau) en het TCP-mechanisme „keepalive“. HTTP Keep-Alive bepaalt of verdere HTTP-verzoeken via dezelfde socket worden uitgevoerd. TCP Keepalive daarentegen verstuurt met grote tussenpozen testpakketten om „dode“ tegenpartijen te detecteren. Voor prestatie-tuning is vooral HTTP Keep-Alive van belang. Ik gebruik TCP Keepalive specifiek voor lange inactieve periodes (bijvoorbeeld bij edge-verbindingen of in bedrijfsnetwerken met agressieve firewalls), maar stel de intervallen defensief in om onnodige netwerkbelasting te voorkomen.

Speciale gevallen: Long Polling, SSE en WebSockets

Langdurige streams (server-sent events), long polling of websockets botsen met korte idle-time-outs. Ik scheid deze eindpunten van standaard API- of asset-routes, wijs ze hogere time-outs en speciale worker-pools toe en beperk het aantal gelijktijdige streams per IP. Zo blokkeren langlopers geen resources voor klassieke korte verzoeken. Voor SSE en WebSockets geldt: liever duidelijke limieten, read/write-time-outs en een schoon heartbeat- of ping/pong-interval dan alle time-outs globaal verhogen.

Centrale keep-alive-parameters in de webserver

Ik activeer bijna altijd Keep-Alive, stel een korte idle-time-out in en beperk het aantal verzoeken per verbinding om resources te recyclen. Daarnaast regel ik de worker-/thread-pools, zodat inactieve verbindingen niet te veel processen in beslag nemen. De volgende tabel toont typische richtlijnen, doelen en startwaarden die ik in de praktijk regelmatig gebruik. De waarden variëren per toepassing en latentieprofiel, maar bieden een solide basis voor eerste tests. Daarna werk ik stapsgewijs aan time-outs, limieten en Discussies op basis van echte meetgegevens.

Server/component richtlijn Doel Startwaarde
Apache KeepAlive Persistente verbindingen activeren Op
Apache KeepAliveTimeout Idle-tijd tot einde verbinding 5–15 s
Apache MaxKeepAliveRequests Max. aantal verzoeken per verbinding 100–500
Nginx keepalive_timeout Idle-tijd tot einde verbinding 5–15 s
Nginx keepalive_requests Max. aantal verzoeken per verbinding 100
HAProxy optie http-keep-alive Persistente verbindingen toestaan actief
Kernel/OS somaxconn, tcp_max_syn_backlog Wachtrijen voor verbindingen aangepast aan het verkeer
Kernel/OS FD-limieten (ulimit -n) Open bestanden/sockets >= 100k bij veel verkeer

Apache: startwaarden, MPM en worker-besturing

Voor sterk parallelle sites gebruik ik in Apache het MPM evenement, omdat het Idle-Keep-Alive-verbindingen efficiënter afhandelt dan het oude prefork. In de praktijk kies ik vaak voor 5-15 seconden voor KeepAliveTimeout, zodat clients resources kunnen bundelen zonder workers lang te blokkeren. Met MaxKeepAliveRequests 100-500 forceer ik gematigd recycling, wat lekken voorkomt en piekbelastingen afvlakt. Ik verminder de algemene time-out tot 120-150 seconden, zodat vastgelopen verzoeken geen processen blokkeren. Wie zich verder verdiept in threads en processen, vindt belangrijke aanwijzingen over Threadpool-instellingen voor verschillende webservers.

Nginx en HAProxy: praktische patronen en anti-patronen

Bij reverse proxies zie ik vaak twee fouten: ofwel wordt Keep-Alive om „veiligheidsredenen“ globaal uitgeschakeld (wat een enorme handshake-belasting veroorzaakt), ofwel zijn de idle-time-outs hoog terwijl er weinig verkeer is (wat resources bindt). Ik houd frontend-time-outs korter dan backend-time-outs, zodat proxies open kunnen blijven, zelfs als clients de verbinding verbreken. Bovendien scheid ik upstream-pools naar serviceklassen (statische assets vs. API), omdat hun verzoekvolgorde en inactiviteit profielafhankelijk is. Ook correct Lengte van de inhoud/Transfercodering-Behandeling: onjuiste lengte-informatie verhindert hergebruik van verbindingen en veroorzaakt „connection: close“ – met als gevolg onnodige nieuwe verbindingen.

Nginx en HAProxy: upstream-pools correct gebruiken

Met Nginx bespaar ik veel handshakes wanneer ik upstream-verbindingen naar backends open houd en via keepalive Poolgroottes aanpassen. Dit vermindert TLS-setups naar applicatieservers en verlaagt daar de CPU-belasting aanzienlijk. Ik bekijk het aantal open upstream-sockets, hergebruikquota's en latentieverdelingen in de logboeken om de poolgroottes doelgericht te verhogen of te verlagen. Aan de kernelzijde verhoog ik de FD-limieten en pas ik somaxconn en tcp_max_syn_backlog aan, zodat wachtrijen niet overstromen. Zo blijft de proxy onder hoge parallelliteit responsief en verdeelt hij het verkeer gelijkmatig over de Backends.

TLS- en QUIC-optimalisatie voor minder overhead

Om Keep-Alive optimaal te laten werken, optimaliseer ik de TLS-laag: TLS 1.3 met hervatting (sessietickets) verkort handshakes, OCSP-stapling verkort certificaatcontroles, een slanke certificaatketen vermindert bytes en CPU. Ik gebruik 0-RTT alleen voor idempotente verzoeken en met de nodige voorzichtigheid om replay-risico's te vermijden. Bij HTTP/3 (QUIC) is dat idle_timeout beslissend: te hoog kost opslagruimte, te laag onderbreekt streams. Ik test ook hoe initiële congestievenster en versterkingslimieten bij koude verbindingen, vooral over grote afstanden.

HTTP/2, HTTP/3 en multiplexing doelgericht gebruiken

HTTP/2 en HTTP/3 bundelen veel verzoeken via één verbinding en elimineren Hoofd van de lijn-Blocking op applicatieniveau. Hierdoor profiteert Keep-Alive nog meer, omdat er minder verbindingen tot stand komen. In mijn setups zorg ik ervoor dat prioriteiten en flow-control zo worden geconfigureerd dat kritieke assets als eerste worden uitgevoerd. Daarnaast controleer ik of Connection Coalescing zinvol is, bijvoorbeeld wanneer meerdere hostnamen hetzelfde certificaat gebruiken. Een blik op HTTP/3 vs. HTTP/2 helpt bij het selecteren van het juiste protocol voor globale gebruikersprofielen.

Clients en app-stacks: pooling correct configureren

Ook de client- en app-zijde zijn bepalend voor hergebruik: in Node.js activeer ik voor HTTP/HTTPS de keepAlive-Agent met een beperkt aantal sockets per host. In Java stel ik bij HttpClient/OkHttp redelijke poolgroottes en idle-time-outs in; in Go pas ik MaxIdleConns en MaxIdleConnsPerHost . gRPC-clients profiteren van lange verbindingen, maar ik definieer ping-intervallen en keepalive-time-outs zodat proxies niet overspoelen. Consistentie is belangrijk: te agressieve herverbindingen van clients ondermijnen elke serveroptimalisatie.

Belastingtests en meetstrategie

Blind draaien op time-outs levert zelden stabiele resultaten op. Resultaten, dus ik meet systematisch. Ik simuleer typische gebruikerspaden met veel kleine bestanden, een realistische mate van parallellisatie en geografisch verspreide latentie. Ondertussen log ik hergebruikpercentages, gemiddelde verbindingsduur, foutcodes en de verhouding tussen open sockets en het aantal workers. Vervolgens varieer ik KeepAliveTimeout in kleine stappen en vergelijk ik de curven van de responstijden en het CPU-verbruik. Pas als de statistieken over meerdere runs robuust blijven, neem ik de waarden over in de Productie.

Observeerbaarheid: welke statistieken tellen mee?

Ik houd concrete statistieken bij: nieuwe verbindingen per seconde, verhouding hergebruik/heropbouw, TLS-handshakes per seconde, open sockets en hun verblijftijd, 95/99-percentiel van de latentie, verdeling van de statuscodes (inclusief 408/499), evenals kernelstatussen zoals TIME_WAIT/FIN_WAIT2. Pieken bij handshakes, stijgende 499's en groeiende TIME_WAIT-buckets duiden vaak op te korte idle-time-outs of te kleine pools. Een goed geïnstrumenteerde logica maakt tuning reproduceerbaar en voorkomt dat optimalisaties slechts placebo-effecten opleveren.

Time-out afstemming tussen client en server

Cliënten moeten inactieve verbindingen iets eerder verbreken dan de server, zodat er geen „dode“ verbindingen ontstaan.“ Sockets ontstaan. In frontend-apps stel ik daarom lagere HTTP-client-time-outs in dan op de webserver en documenteer ik deze instellingen. Hetzelfde geldt voor load balancers: hun idle-time-out mag de server niet ondermijnen. Ik houd ook NAT- en firewall-idle-waarden in de gaten, zodat verbindingen niet in het netwerkpad verdwijnen. Deze nette interactie voorkomt sporadische resets en stabiliseert Retransmissies.

Veerkracht en veiligheid onder belasting

Persistente verbindingen mogen geen uitnodiging zijn voor Slowloris & Co. Ik stel korte header-/body-read-timeouts in, beperk headergroottes, beperk gelijktijdige verbindingen per IP en zorg voor backpressure in upstreams. Bij protocolfouten sluit ik verbindingen consequent (in plaats van ze open te houden) en voorkom zo request smuggling. Daarnaast definieer ik zinvolle grace-tijden bij het sluiten, zodat de server open antwoorden netjes afsluit zonder verbindingen eindeloos in lingering-toestanden te handhaven.

Hostingfactoren en architectuur

Krachtige CPU's, snelle NIC's en voldoende RAM versnellen handshakes, contextwisselingen en versleuteling, wat Keep-Alive-Tuning ten volle benut. Een reverse proxy vóór de app vereenvoudigt offloading, centraliseert time-outs en verhoogt het hergebruikpercentage naar backends. Voor meer controle over TLS, caching en routing vertrouw ik op een duidelijke Reverse proxy-architectuur. Het blijft belangrijk om limieten zoals ulimit -n en accept-queues vroegtijdig op te heffen, zodat de infrastructuur hoge parallelliteit aankan. Met een goede observability herken ik knelpunten sneller en kan ik Grenzen veilig vastmaken.

Implementaties, drain en OS-subtiliteiten

Bij rolling deployments laat ik keep-alive-verbindingen op een gecontroleerde manier aflopen: ik accepteer geen nieuwe verzoeken meer, bestaande verzoeken mogen kort worden afgehandeld (drain). Zo voorkom ik verbroken verbindingen en 5xx-pieken. Op OS-niveau houd ik de ephemeral-port-span in de gaten., somaxconn, SYN-backlog en tcp_fin_timeout, zonder verouderde tweaks zoals agressief hergebruik van TIME_WAIT. SO_REUSEPORT Ik verdeel het over meerdere worker-processen om Accept-concurrentie te verminderen. Het doel is altijd: veel kortstondige verbindingen stabiel afhandelen, zonder dat er opstoppingen ontstaan in kernel-queues.

Samenvatting: Tuning als prestatieverhogende factor

Consequent gebruik van HTTP Keep-Alive zorgt voor minder verbindingsopbouw, een lagere CPU-belasting en merkbaar snellere reacties. Korte idle-time-outs, duidelijke limieten per verbinding en voldoende gedimensioneerde workers houden inactieve sockets onder controle. Met HTTP/2/3, upstream-pools en afgestemde OS-limieten schaal ik parallelliteit zonder stabiliteit te verliezen. Realistische belastingstests laten zien of instellingen echt werken en waar de volgende procentpunten liggen. Wie deze bouwstenen combineert, verhoogt de doorvoer, houdt de latentie laag en maakt gebruik van bestaande Bronnen maximaal.

Huidige artikelen