How to Handle Nginx 302 Redirect Internally for HLS Streaming Without Client-Side Redirection


4 views

When working with HLS streaming architectures, we often encounter a scenario where origin servers respond with 302 redirects containing dynamic timestamps in the URLs. This creates compatibility issues with:

  • Players that don't properly handle HTTP redirects
  • Caching layers that can't cache dynamic URLs
  • Client-side analytics that get broken by redirect chains

The solution involves configuring Nginx to:

  1. Intercept client requests
  2. Proxy them to the origin
  3. Follow redirects internally
  4. Return the final content directly

Here's the complete Nginx configuration to solve this:

server {
    listen 80;
    server_name frontend.example.com;

    location /hls/ {
        proxy_pass http://origin1.example.com/m3ugen/segsrc/;
        proxy_intercept_errors on;
        error_page 301 302 = @handle_redirect;
    }

    location @handle_redirect {
        resolver 8.8.8.8;
        set $orig_uri $upstream_http_location;
        proxy_pass $orig_uri;
        proxy_set_header Host $proxy_host;
    }
}

proxy_intercept_errors: Allows Nginx to intercept backend error responses (including 30x redirects) rather than passing them to the client.

error_page: Catches 301/302 responses and routes them to our named location block.

resolver: Required when using variables in proxy_pass to resolve the redirect target.

Verify the setup works correctly:

curl -v http://frontend.example.com/hls/jet480.mp4.m3u8

You should see:

  • No 302 status code in the response
  • The final HLS playlist content
  • Only one HTTP request from the client perspective

For production environments, consider adding:

proxy_cache my_cache;
proxy_cache_valid 200 302 10m;
proxy_redirect off;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

These additional directives enable caching and proper header handling while maintaining security.


When dealing with HLS (HTTP Live Streaming) content delivery, many origin servers (particularly those running Helix Server) implement 302 redirects that can cause compatibility issues with certain media players. The core problem manifests when:

  • Original request: http://origin1.example.com/m3ugen/segsrc/jet480.mp4
  • Server responds with 302 to: http://origin1.example.com/Segments/HLS_TS/segsrc/jet480.mp4-20140831-142558.m3u8

Many media players and client applications don't properly handle 302 redirects for HLS manifests. The timestamp appended by Helix Server creates additional complexity as it generates unique URLs for each request.

We can configure Nginx to internally follow the redirect chain and serve the final content directly. Here's the complete configuration:

server {
    listen 80;
    server_name frontend.example.com;

    location /hls/ {
        proxy_pass http://origin1.example.com/m3ugen/segsrc/;
        proxy_redirect off;
        proxy_intercept_errors on;
        error_page 301 302 = @handle_redirect;
    }

    location @handle_redirect {
        set $original_uri $uri;
        resolver 8.8.8.8;
        proxy_pass $upstream_http_location;
        proxy_set_header Host $upstream_host;
        proxy_hide_header Location;
    }
}
  • proxy_intercept_errors: Allows Nginx to handle 3xx responses
  • error_page: Routes redirects to our custom handler
  • proxy_hide_header: Removes the Location header from final response
  • resolver: Required for DNS resolution during redirects

After implementing this configuration:

  1. Client requests: http://frontend.example.com/hls/jet480.mp4.m3u8
  2. Nginx fetches: http://origin1.example.com/m3ugen/segsrc/jet480.mp4
  3. Follows 302 to: http://origin1.example.com/Segments/HLS_TS/segsrc/jet480.mp4-20140831-142558.m3u8
  4. Serves final manifest directly to client

For production environments:

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=HLS_CACHE:10m inactive=60m;

server {
    # ... previous configuration ...
    location /hls/ {
        proxy_cache HLS_CACHE;
        proxy_cache_valid 200 302 10m;
        proxy_cache_use_stale error timeout updating;
        # ... rest of config ...
    }
}