Network Latency Optimization

Reduce network latency at every layer of the stack. Covers TCP optimization, connection reuse, DNS prefetching, edge computing, protocol selection, and the patterns that shave milliseconds that compound into competitive advantage.

Network latency optimization is the practice of reducing the time between when a request is sent and when its response is received, with a focus on minimizing the round-trip delay across networked systems. It matters most in real-time applications—interactive web apps, video conferencing, online gaming, financial trading—where users perceive delays beyond 100ms as sluggish, and beyond 500ms as noticeable interruptions. It is critical when data flows across geographically distributed services, especially across wide area networks (WANs), and becomes non-trivial when multiple hops, variable bandwidth, and packet loss compound the delay.

Reducing RTT through TCP Tuning

TCP’s default behavior often underperforms in high-latency or high-bandwidth environments. Optimizing TCP parameters directly impacts the time to first byte (TTFB) and overall throughput.

The key TCP parameters to tune are:

On Linux, apply these using sysctl:

sudo sysctl -w net.ipv4.tcp_rto_min=50
sudo sysctl -w net.ipv4.tcp_rto_max=30000
sudo sysctl -w net.ipv4.tcp_rtt_min=50
sudo sysctl -w net.ipv4.tcp_slow_start_after_idle=1
sudo sysctl -w net.ipv4.tcp_max_syn_backlog=8192
sudo sysctl -w net.ipv4.tcp_syn_retries=3
sudo sysctl -w net.ipv4.tcp_fin_timeout=30

The default tcp_syn_retries=6 (60s total timeout) is often too aggressive for long-haul connections. Reducing it to 3 (30s total) improves responsiveness for interactive services.

Failure mode: A service with high tcp_syn_retries=6 but no tcp_slow_start_after_idle may exhibit slow initial data transfer after a client idle period, as the connection re-enters slow start only after the first data packet, delaying window growth.

Minimizing TTFB with Connection Reuse

The time from request start to first byte is dominated by connection establishment (TCP handshake) and application layer handshake (e.g., TLS). Reusing connections reduces both.

Use HTTP/2 or HTTP/1.1 with Keep-Alive and Connection: keep-alive headers.

For HTTP/1.1, configure keep-alive with:

Connection: keep-alive
Keep-Alive: timeout=60, max=100

In NGINX, this is set in the http block:

http {
    keepalive_timeout 60s 60s;
    keepalive_requests 100;
}

For HTTP/2, enable http2 in the server block:

server {
    listen 443 ssl http2;
    # ...
}

With HTTP/2, multiple requests can be multiplexed over a single connection, eliminating head-of-line blocking.

Critical failure: A client sends a request with Connection: keep-alive, but the server does not echo the header in the response. This causes the client to assume keep-alive is enabled, but the server closes the connection after each request, negating the benefit.

Optimizing TLS Handshake

TLS handshake adds 1–3 RTTs to the TTFB. The most effective optimizations are:

To enable 0-RTT in TLS 1.3, the server must support early_data and the client must send TLS 1.3 with 0-RTT support.

In NGINX, configure TLS 1.3 with session resumption and early data:

ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets on;
ssl_early_data on;

The client sends TLS 1.3 with 0-RTT enabled. The server responds with early_data in the EncryptedExtensions message, allowing the client to send application data in the first flight.

Failure mode: A client sends 0-RTT data, but the server does not validate it. The server may accept the data, but the client may later receive a NewSessionTicket with a new session ID, causing the client to lose the early data if the server uses a different session.

Reducing Latency with Edge Caching

Caching at the network edge reduces the distance data must travel. The effectiveness depends on cache hit ratio and freshness.

Use HTTP Cache-Control headers to control cacheability:

Cache-Control: public, max-age=3600, stale-while-revalidate=3600, stale-if-error=7200

This tells caches to serve stale content while revalidating in the background, reducing latency during cache misses.

In NGINX, configure caching with:

location /static/ {
    alias /var/www/static/;
    add_header Cache-Control "public, max-age=3600";
    add_header Vary "Accept-Encoding";
    expires 1h;
    etag on;
    gzip on;
}

For dynamic content, use Vary: Accept-Encoding and ETag to ensure accurate cache keying.

Failure mode: A client requests image.png, but the server returns image.png with Content-Type: image/png and ETag: "abc123", but does not set Vary: Accept-Encoding. A CDN caches the response, but a client requesting image.png with Accept-Encoding: gzip gets the non-gzipped version, even though the server has a gzipped variant.

Optimizing DNS for Latency

DNS lookup can contribute 10–100ms of latency per request. Reduce it by:

Use systemd-resolved on Linux to enable DoH:


[Resolve]
DNS=1.1.1.1 1.0.0.1
FallbackDNS=8.8.8.8 8.8.4.4
DNSOverTLS=yes
DNSSEC=yes

Enable DoH via resolvconf:

sudo systemctl enable systemd-resolved
sudo resolvectl status

In HTML, preload DNS records:

<link rel="dns-prefetch" href="https://api.example.com">
<link rel="dns-prefetch" href="https://cdn.example.com">

Failure mode: A client requests api.example.com, but the DNS resolver returns CNAME records for api.example.com to api.prod.example.com, but does not include A records for the CNAME in the answer. The client must issue a second DNS query for api.prod.example.com, doubling the DNS latency.

Reducing Latency in Client-Side Applications

Client-side applications—web, mobile, desktop—can reduce latency through proactive and intelligent request scheduling.

Use fetch() with preload and prefetch hints:

// Preload critical resources
const preload = new Promise((resolve) => {
    const link = document.createElement('link');
    link.rel = 'preload';
    link.href = '/api/initial-data';
    link.as = 'fetch';
    link.onload = resolve;
    document.head.appendChild(link);
});

// Prefetch less critical resources
const prefetch = new Promise((resolve) => {
    const link = document.createElement('link');
    link.rel = 'prefetch';
    link.href = '/api/user-activities';
    link.onload = resolve;
    document.head.appendChild(link);
});

Promise.all([preload, prefetch]).then(() => {
    // Start fetching critical data
    fetch('/api/initial-data', { cache: 'no-cache' })
        .then(res => res.json())
        .then(data => render(data));
});

Failure mode: A client fetches /api/data, but the response is large and includes Cache-Control: max-age=3600, but the client does not set Cache-Control: no-cache in the request. The server returns a cached response, but the client does not revalidate it, leading to stale data.

Reducing Latency with Proactive Data Fetching

Proactive fetching anticipates user actions before they occur.

Use Intersection Observer to trigger data fetching when an element enters the viewport:

const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            const url = entry.target.dataset.load;
            fetch(url, { cache: 'no-cache' })
                .then(res => res.json())
                .then(data => {
                    const container = document.getElementById(entry.target.id);
                    container.innerHTML = render(data);
                });
            observer.unobserve(entry.target);
        }
    });
}, { threshold: 0.1 });

document.querySelectorAll('[data-load]').forEach(el => observer.observe(el));

Failure mode: A user scrolls quickly through a list, but the IntersectionObserver triggers multiple fetches for the same element because the threshold is not tight enough. The client sends redundant requests, increasing latency and bandwidth.

Reducing Latency in Microservices

Microservices architectures often introduce latency due to multiple service calls.

Use batching to reduce round-trips:

POST /api/batch HTTP/1.1
Host: gateway.example.com
Content-Type: application/json

[
  { "method": "GET", "url": "/users/123" },
  { "method": "GET", "url": "/posts/456" },
  { "method": "POST", "url": "/notifications", "body": { "user": 123, "msg": "Welcome!" } }
]

The gateway routes each request, aggregates results, and returns a single response.

Failure mode: A client batches 100 requests, but the gateway does not handle Content-Type: application/json correctly. It parses the batch request as application/x-www-form-urlencoded, causing the JSON body to be malformed.

Managing Latency in High-Frequency Data Streams

For real-time data streams—such as sensor telemetry or live video—latency is managed through:

Use udp with SO_SNDBUF and SO_RCVBUF to tune buffer sizes:

int send_buf = 65536;
int recv_buf = 65536;
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &send_buf, sizeof(send_buf));
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recv_buf, sizeof(recv_buf));

Failure mode: A sensor sends 100ms of telemetry over UDP, but the receiver does not process packets in a timely manner. The sender fills the send buffer, but the receiver processes packets at 100ms intervals, causing the sender to stall and increasing end-to-end latency.

Latency-Optimized Configuration for a Typical Web Service

A complete latency-optimized service stack:

# /etc/nginx/sites-available/example.com
server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate_file /etc/ssl/certs/example.com.crt;
    ssl_certificate_key_file /etc/ssl/private/example.com.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_session_tickets on;
    ssl_early_data on;
    ssl_stapling on;
    ssl_stapling_verify on;

    location / {
        root /var/www/html;
        index index.html;
        add_header Cache-Control "public, max-age=3600, stale-while-revalidate=3600";
        add_header Vary "Accept-Encoding";
        expires 1h;
        etag on;
        gzip on;
    }

    location /api/ {
        proxy_pass http://backend:8080;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
        proxy_cache_valid 200 302 10m;
        proxy_cache_valid 404 1m;
        proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
        proxy_cache_min_uses 2;
        proxy_cache_lock on;
        proxy_cache_lock_timeout 5s;
        proxy_cache_key "$host$request_uri";
    }

    # Enable HTTP/2 and server push
    location = /styles.css {
        add_header Link '</styles.css>; rel=preload; as=style';
        add_header Link '</scripts.js>; rel=preload; as=script';
        add_header Link '</images/logo.png>; rel=preload; as=image';
    }
}

This configuration reduces TTFB, enables connection reuse, supports 0-RTT TLS, and leverages HTTP/2 server push to deliver critical resources early.

This page was rewritten on 18 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.