ERROR

Troubleshooting Guide: HTTP/2 Stream Reset (RST_STREAM) & GOAWAY Frames

Quick Fix Summary

TL;DR

Check server logs for RST_STREAM error codes and verify client timeout settings match server keep-alive configurations.

RST_STREAM frames abruptly terminate individual HTTP/2 streams, while GOAWAY frames signal the entire connection is being shut down. These errors indicate protocol violations, resource exhaustion, or intentional termination by either endpoint.

Diagnosis & Causes

  • Client cancelling a request before completion.
  • Server exceeding concurrent stream limits.
  • Request or response header size violations.
  • Internal server error forcing stream termination.
  • Load balancer or proxy misconfiguration.
  • Recovery Steps

    1

    Step 1: Capture and Decode HTTP/2 Frames with tcpdump/nghttp2

    Use tcpdump to capture raw traffic and nghttp2 to decode the HTTP/2 frames, identifying the specific RST_STREAM error code and GOAWAY last-stream-id.

    bash
    sudo tcpdump -i any -s 0 -w /tmp/http2_capture.pcap 'port 443'
    nghttp -nv --hexdump --no-verify-peer https://your-target.com
    2

    Step 2: Analyze Server-Side Logs for Error Codes

    Inspect web server (Nginx/Apache) and application logs for HTTP/2 error codes. Map RST_STREAM codes to their root cause (e.g., 0x1 PROTOCOL_ERROR, 0x3 FLOW_CONTROL_ERROR).

    bash
    grep -i "RST_STREAM\|GOAWAY" /var/log/nginx/error.log
    journalctl -u apache2 | grep -E "(stream error|goaway)"
    3

    Step 3: Verify and Tune Server HTTP/2 Configuration

    Ensure server settings align with client capabilities. Adjust stream concurrency, header size limits, and timeouts to prevent protocol violations.

    nginx
    # Nginx Example
    http2_max_concurrent_streams 128;
    http2_recv_timeout 30s;
    large_client_header_buffers 4 32k;
    # Apache Example (mod_http2)
    H2MaxStreams 100
    H2StreamTimeout 30
    4

    Step 4: Inspect Intermediate Proxies and Load Balancers

    Check configurations for proxies (HAProxy, Envoy) or CDNs that may be terminating HTTP/2 connections incorrectly or imposing stricter limits.

    bash
    # HAProxy - Check tune.h2. settings
    global
      tune.h2.initial-window-size 65535
      tune.h2.max-concurrent-streams 100
    # Check Envoy logs for HTTP/2 codec errors
    kubectl logs <envoy-pod> | grep -i "http2.codec"
    5

    Step 5: Reproduce with a Debug Client and Validate Fix

    Use a verbose HTTP/2 client (curl with --http2-verbose, nghttp) to reproduce the issue and validate configuration changes resolve the stream resets.

    bash
    curl -v --http2 https://your-target.com/api/endpoint 2>&1 | grep -A5 -B5 "RST_STREAM"
    nghttp -v -H ":method: POST" -d @payload.json https://your-target.com

    Architect's Pro Tip

    "A sudden spike in RST_STREAM with error code 0x8 (CANCEL) often points to aggressive client-side timeout settings that are shorter than your server's processing time."

    Frequently Asked Questions

    What's the difference between RST_STREAM and GOAWAY?

    RST_STREAM terminates a single stream within an HTTP/2 connection. GOAWAY is a polite notification that the entire connection is being shut down and no new streams should be initiated on it.

    Can I ignore occasional RST_STREAM frames?

    Occasional RST_STREAM frames (e.g., from user cancellation) are normal. However, a persistent pattern, especially with PROTOCOL_ERROR (0x1) or INTERNAL_ERROR (0x2), requires investigation.

    How do I find the official HTTP/2 error codes?

    Refer to RFC 7540, Section 7. The definitive list includes PROTOCOL_ERROR (0x1), INTERNAL_ERROR (0x2), FLOW_CONTROL_ERROR (0x3), SETTINGS_TIMEOUT (0x4), and others.

    Related HTTP Protocol Guides