Troubleshooting Guide: HTTP/2 Stream Reset (RST_STREAM) & GOAWAY Frames
Quick Fix Summary
TL;DRCheck 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
Recovery Steps
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.
sudo tcpdump -i any -s 0 -w /tmp/http2_capture.pcap 'port 443'
nghttp -nv --hexdump --no-verify-peer https://your-target.com 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).
grep -i "RST_STREAM\|GOAWAY" /var/log/nginx/error.log
journalctl -u apache2 | grep -E "(stream error|goaway)" 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 Example
http2_max_concurrent_streams 128;
http2_recv_timeout 30s;
large_client_header_buffers 4 32k;
# Apache Example (mod_http2)
H2MaxStreams 100
H2StreamTimeout 30 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.
# 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" 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.
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.