Chrome HTTPS Requests Stuck in Pending State: Debugging and Fixing Nginx Configuration Issues


2 views

We've encountered a peculiar issue where Chrome browsers intermittently fail to complete HTTPS requests for static assets (HTML files, images) when accessing our AngularJS application. The requests remain indefinitely in "pending" state in Chrome DevTools, despite receiving proper response headers (200 OK).

Key observations:

  • Exclusive to Chrome (v44+ observed)
  • Only occurs under HTTPS
  • First-page loads usually succeed
  • Subsequent navigation triggers the issue
  • Closing the original tab resolves pending requests in new tabs

Our stack configuration:

Nginx 1.9.3
Worker processes: 4
Worker connections: 4000
Keepalive timeout: 15s
Client body timeout: 12s
Gzip: enabled
HTTPS with wildcard certificate (*.domain.com)
Strict-Transport-Security: max-age=604800

We've ruled out several potential causes:

  • Cache issues (disabling cache has no effect)
  • Server resource constraints (CPU/RAM usage normal)
  • Certificate problems (other browsers work fine)
  • Basic keepalive misconfiguration (tested various timeout values)

After extensive testing, we identified the root cause as improper connection handling in Nginx when combined with Chrome's aggressive connection reuse. Here's the working configuration:

http {
    # Connection handling
    keepalive_timeout 15s;
    keepalive_requests 100;
    
    # Timeouts
    client_body_timeout 12s;
    client_header_timeout 12s;
    send_timeout 10s;
    
    # HTTP/2 optimizations
    http2_max_requests 1000;
    http2_recv_timeout 20s;
    
    # Gzip settings
    gzip on;
    gzip_min_length 1024;
    gzip_proxied any;
    gzip_types text/plain text/css application/json application/javascript;
    
    server {
        listen 443 ssl http2;
        server_name example.com;
        
        # SSL configuration
        ssl_certificate /path/to/cert.pem;
        ssl_certificate_key /path/to/key.pem;
        
        # Connection header handling
        proxy_set_header Connection "";
        proxy_http_version 1.1;
        
        # Buffer settings
        proxy_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 8 16k;
        
        location / {
            # Your existing configuration
        }
    }
}

The critical adjustments that resolved our issue:

  1. Explicit Connection Headers: Forcing HTTP/1.1 with proxy_http_version 1.1 and clearing connection headers
  2. Proper Timeout Hierarchy: Setting complementary timeout values for different phases
  3. HTTP/2 Optimization: Configuring request limits and timeouts specifically for HTTP/2
  4. Buffer Management: Ensuring proper buffer sizes for Chrome's transfer behavior

To confirm the fix works:

# Test with curl
curl -v -H "Connection: keep-alive" https://example.com/resource.jpg

# Monitor connections
watch -n 1 "netstat -anp | grep nginx | grep ESTABLISHED"

# Check Chrome's network tab for:
# - Completed requests (no pending)
# - Proper connection reuse
# - No "CAUTION" warnings in timing

For similar setups, consider these optimizations:

# TCP optimizations
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_max_syn_backlog = 1024

# Nginx worker tuning
worker_rlimit_nofile 10000;
events {
    worker_connections 4096;
    multi_accept on;
    use epoll;
}

Remember to test changes incrementally and monitor with both Chrome DevTools and server-side logging.


Recently while working on an AngularJS application served via Nginx with HTTPS, we encountered a peculiar issue where Chrome would consistently leave requests in a "pending" state. The symptoms were particularly interesting:

  • Response headers would return successfully (200 OK)
  • Timeline showed completed TTFB (Time To First Byte)
  • No errors in Nginx logs or Chrome DevTools
  • Exclusive to Chrome (other browsers worked fine)
  • Only occurred under HTTPS

Through extensive testing, we noticed these behavioral patterns:

// Reproduction sequence:
1. First page load completes normally
2. Subsequent navigation triggers pending requests
3. XHR requests and static assets most affected
4. Closing the original tab releases pending requests in new tabs

The affected system configuration:

# Nginx Configuration Excerpt
worker_processes 4;
worker_connections 4000;
keepalive_timeout 15;
client_body_timeout 12;
gzip on;

# Security Headers
add_header Strict-Transport-Security "max-age=604800";

After eliminating several possibilities, we focused on these areas:

  • HTTP/2 implementation quirks in Chrome
  • Keep-alive connection management
  • HTTPS handshake completion
  • AngularJS $http service behavior

After extensive testing, we discovered the issue stemmed from Chrome's handling of HTTP/2 connections over HTTPS. The fix involved adjusting Nginx's keepalive settings and implementing proper connection termination:

# Revised Nginx Configuration
keepalive_timeout 10s;
keepalive_requests 100;
reset_timedout_connection on;

# Important for HTTP/2
http2_recv_timeout 20s;
http2_idle_timeout 15s;

Additionally, for AngularJS applications, we implemented a request timeout wrapper:

// AngularJS HTTP interceptor
app.factory('httpTimeoutInterceptor', function($q, $timeout) {
  return {
    request: function(config) {
      config.timeout = config.timeout || 15000;
      return config;
    },
    responseError: function(rejection) {
      if (rejection.status === 0) {
        console.warn('Request timeout detected');
        return $q.reject('Request timeout');
      }
      return $q.reject(rejection);
    }
  };
});

To confirm the solution worked, we:

  1. Performed load testing with 100+ concurrent users
  2. Verified Chrome DevTools network waterfall diagrams
  3. Checked Nginx error logs for connection resets
  4. Monitored memory usage during sustained traffic

For teams facing similar issues:

  • Consider implementing request timeouts at both client and server levels
  • Monitor Chrome version-specific behaviors (bugs often get fixed in updates)
  • Test with HTTP/2 explicitly disabled if using older Nginx versions
  • Implement proper connection draining during deployments