HTTP video streaming
Spring webserver
proxy video stream
client-server architecture
web development

How to proxy a HTTP video stream to any amount of clients through a Spring Webserver

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Proxying video through Spring sounds simple until several clients connect at once. The hard part is not writing bytes to the response; the hard part is scaling bandwidth, preserving range requests, and deciding whether each client should open its own upstream stream or whether a dedicated media layer is the better architecture.

Start with the Right Question

There are two very different scenarios:

  1. Proxy a static video file or byte-range source.
  2. Fan out one live upstream stream to many viewers.

Spring can handle the first case quite well. The second case is harder because your server becomes a relay, and network bandwidth becomes the limiting factor long before controller code does.

Byte-Range Proxying for Regular HTTP Video

Most browser video playback uses HTTP range requests. Your proxy must forward the Range header and stream the upstream response back without buffering the whole file into memory.

A Spring Boot controller can do that with StreamingResponseBody:

java
1import java.io.InputStream;
2import org.springframework.http.HttpHeaders;
3import org.springframework.http.MediaType;
4import org.springframework.http.ResponseEntity;
5import org.springframework.web.bind.annotation.GetMapping;
6import org.springframework.web.bind.annotation.RequestHeader;
7import org.springframework.web.bind.annotation.RestController;
8import org.springframework.web.client.RestTemplate;
9import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
10
11@RestController
12public class VideoProxyController {
13
14    private final RestTemplate restTemplate = new RestTemplate();
15
16    @GetMapping("/video")
17    public ResponseEntity<StreamingResponseBody> proxyVideo(
18            @RequestHeader(value = "Range", required = false) String rangeHeader) {
19
20        var request = new org.springframework.http.RequestEntity<Void>(
21                HttpHeaders.EMPTY,
22                org.springframework.http.HttpMethod.GET,
23                java.net.URI.create("https://origin.example.com/movie.mp4"));
24
25        var response = restTemplate.execute(
26                request.getUrl(),
27                request.getMethod(),
28                clientRequest -> {
29                    if (rangeHeader != null) {
30                        clientRequest.getHeaders().set("Range", rangeHeader);
31                    }
32                },
33                clientResponse -> {
34                    StreamingResponseBody body = outputStream -> {
35                        try (InputStream in = clientResponse.getBody()) {
36                            in.transferTo(outputStream);
37                        }
38                    };
39
40                    return ResponseEntity.status(clientResponse.getStatusCode())
41                            .header("Content-Type", clientResponse.getHeaders().getFirst("Content-Type"))
42                            .header("Content-Length", clientResponse.getHeaders().getFirst("Content-Length"))
43                            .header("Content-Range", clientResponse.getHeaders().getFirst("Content-Range"))
44                            .header("Accept-Ranges", "bytes")
45                            .body(body);
46                });
47
48        return response;
49    }
50}

The important behavior is streaming pass-through, not holding the full video in RAM.

Why "Any Amount of Clients" Is Misleading

No application server can serve an unlimited number of video clients. Even with efficient streaming, each client consumes outbound bandwidth and connection capacity.

For example, a 4 Mbps stream sent to 1,000 viewers requires roughly 4 Gbps of egress before protocol overhead. That is an infrastructure problem, not a Java syntax problem.

If you expect real scale, push static assets or segmented streams through a CDN. For live video, use a proper streaming architecture such as HLS or DASH with origin caching, or a media server built for fan-out.

When Spring Is Still Useful

Spring is appropriate when you need application logic in front of the stream, such as:

  • authentication
  • signed URL validation
  • audit logging
  • token exchange
  • selective header rewriting

In those cases, Spring can authorize the request and either proxy the bytes or redirect the client to a signed upstream URL.

Often the redirect pattern is better because it removes your application server from the heavy data path.

Common Pitfalls

The biggest mistake is reading the entire upstream video into a byte array before returning it. That destroys memory usage and increases startup latency.

Another mistake is ignoring Range and Content-Range. Browser video scrubbing depends on partial content support, so a naive proxy often appears to "work" until users seek around the timeline.

A third issue is treating Spring MVC threads as infinite. Even when using asynchronous response streaming, network egress and connection management still have real limits. If the workload is large, a CDN or specialized streaming stack is the right answer.

Summary

  • Spring can proxy HTTP video effectively when it streams bytes instead of buffering them.
  • Forward range headers so browser seeking continues to work.
  • Static and live fan-out problems need different architectures.
  • Large viewer counts are usually a CDN or media-delivery concern, not just a controller concern.
  • Use Spring in the data path only when you need application-level control there.

Course illustration
Course illustration

All Rights Reserved.