Public R2 bucket doesn't handle range requests well

About a week ago I found that neither files served through public access nor files served through domain access are properly responding to range requests. I opened a ticket about this assuming it was a bug, but after 8 days I haven’t received any response. I must admit I had slightly higher hopes for Cloudflare support.

When accessing my files through domain access with a range header, R2 responds with the proper amount of bytes and a 206 status code. Except, it does this only some times.

$ curl -I -H "Range:bytes=0-1023" https://mnemosyne.xxxxx.net/v0.12.3.2/windows.zip?fileName=test.zip
HTTP/1.1 200 OK
Date: Fri, 04 Nov 2022 17:35:18 GMT
Content-Type: application/zip
Content-Length: 595531828
Connection: keep-alive
ETag: "f8a823ce3ce6908b78638b79bb39f87f"
Last-Modified: Thu, 13 Oct 2022 15:10:20 GMT
Vary: Accept-Encoding
Cache-Control: max-age=14400
CF-Cache-Status: MISS
Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=d3K0J3y8V0dz%2ByDL4ZbktvZ1I%2FxM1%2BioDfXY0q0Hq9IGv6IiiP0Uq7Csqvi9dcF3DhjuNNlcU%2BItlF6anyq4KSZFvz632%2F4fqnCv%2BF%2B7rQihWRfWo1eAPjVj1%2BHiW6cBdGIGd%2BDEw6YJ39bH%2FEwbl1manLPmnqN5"}],"group":"cf-nel","max_age":604800}
NEL: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
Content-Disposition: attachment; filename="test.zip"
Server: cloudflare
CF-RAY: 764f1afd1905b8a3-AMS


$ curl -I -H "Range:bytes=0-1023" https://mnemosyne.xxxxx.net/v0.12.3.2/windows.zip?fileName=test.zip
HTTP/1.1 206 Partial Content
Date: Fri, 04 Nov 2022 17:35:22 GMT
Content-Type: application/zip
Content-Length: 1024
Connection: keep-alive
Content-Range: bytes 0-1023/595531828
ETag: "f8a823ce3ce6908b78638b79bb39f87f"
Last-Modified: Thu, 13 Oct 2022 15:10:20 GMT
Vary: Accept-Encoding
Cache-Control: max-age=14400
CF-Cache-Status: MISS
Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=C6%2FLk47TwAGG4X%2FIwXhCSNDl66naRN6jdD8rNOOUrZOBfZZ2qG5JUD%2B%2FgqjMnGnQcNvCEn17TWs5gbxjpT3U6BgRpUI%2Bu1VpHxcfKCDDWmlKa2psooTQyQjzUJX02sF7uim5gCY6%2FVC%2Babavm9I6NLT2V1ReuO53"}],"group":"cf-nel","max_age":604800}
NEL: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
Content-Disposition: attachment; filename="test.zip"
Server: cloudflare
CF-RAY: 764f1b129f7c0a79-AMS

When accessing my files through public access with a range header, R2 always responds with the proper amount of bytes but never with a 206 status code. Unfortunately I need both in order not to break the tools I rely on.

$ curl -I -H "Range:bytes=0-1023" https://pub-yyyyy.r2.dev/v0.12.3.2/windows.zip
HTTP/1.1 200 OK
Date: Fri, 04 Nov 2022 17:38:20 GMT
Content-Type: application/zip
Content-Length: 1024
Connection: keep-alive
Accept-Ranges: bytes
ETag: "f8a823ce3ce6908b78638b79bb39f87f"
Last-Modified: Thu, 13 Oct 2022 15:10:20 GMT
Server: cloudflare
CF-RAY: 764f1f6e0945b73a-AMS


$ curl -I -H "Range:bytes=0-1023" https://pub-yyyyyy.r2.dev/v0.12.3.2/windows.zip
HTTP/1.1 200 OK
Date: Fri, 04 Nov 2022 17:38:33 GMT
Content-Type: application/zip
Content-Length: 1024
Connection: keep-alive
Accept-Ranges: bytes
ETag: "f8a823ce3ce6908b78638b79bb39f87f"
Last-Modified: Thu, 13 Oct 2022 15:10:20 GMT
Server: cloudflare
CF-RAY: 764f1fbd4bf2b7a0-AMS


$ curl -I -H "Range:bytes=0-1023" https://pub-yyyyy.r2.dev/v0.12.3.2/windows.zip
HTTP/1.1 200 OK
Date: Fri, 04 Nov 2022 17:38:50 GMT
Content-Type: application/zip
Content-Length: 1024
Connection: keep-alive
Accept-Ranges: bytes
ETag: "f8a823ce3ce6908b78638b79bb39f87f"
Last-Modified: Thu, 13 Oct 2022 15:10:20 GMT
Server: cloudflare
CF-RAY: 764f202a2993b8fd-AMS

Moreover, I need domain access to be able to use WAF, and I need WAF to be able to manage access to my R2 resources.

By now I figure, maybe I missed some fine print or maybe I’m just stupid. But I can’t explain the logs. Any ideas?

I also facing some problem on range request.
I want to use the html5 to play mp4 video. But there are some problems when playing videos on Safari(Safari must support range request to play video). I’m not sure if this is a bug of R2, but will be helpful if R2 can change the range request behavior.

In the case that the video can be played normally, safari will generate requests similar to the following:

Safari Request 1:
Range:bytes=0-1
...

Response 1:
HTTP/1.1 206 Partial Content
Content-Length: 2
Content-Range: bytes 0-1/190861503
Content-Type: video/mp4
...

Safari Request 2:
Range:bytes=0-190861502
...

Response 2:
HTTP/1.1 206 Partial Content
Content-Length: 190861503
Content-Range: bytes 0-190861502/190861503
Content-Type: video/mp4
...

Safari Request 3:
Range:bytes=0-33575103
...

Response 3:
HTTP/1.1 206 Partial Content
Content-Length: 33575104
Content-Range: bytes 0-33575103/190861503
Content-Type: video/mp4
...

Safari Request N:
...

When using R2, the following requests and responses will be generated:

Safari Request 1:
Range:bytes=0-1
...

R2 Response 1:
HTTP/1.1 206 Partial Content
Content-Length: 2
Content-Range: bytes 0-1/190861503
Content-Type: video/mp4
...

Safari Request 2:
Range:bytes=0-190861502
...

R2 Response 2:
HTTP/1.1 200 OK
Content-Length: 190861503
Content-Type: video/mp4
...

It can be found that in the second request, since the bytes of the “Range” header is equal to the object size, R2 will think that the request is not a PARTIAL REQUEST, and the response will not contain the “Content-Range” header.

Without the Content-Range header will cause safari to completely download the entire video and the request cannot be paused even the video playback is being paused. In addition, the request will not interrupted even if the progress bar of the player is changed, and the playback cannot be continued until the video has been downloaded to the specified progress.

When the video file size is very large, this problem will seriously affect the video viewing experience.

It would be helpful if R2 could always respond with a “Content-Range” header when the request has a “Range” header.

I figure this might be related to the fact that domain access for R2 is proxied by Cloudflare. Maybe R2 works fine but Cloudflare removes the range header at random. The following topic seems to reinforce that suspicion.

Cloudflare keep removing my Range header randomly - Website, Application, Performance / Performance - Cloudflare Community

I’ve been able to explain the difference between domain access and public access, and it makes me feel stupid.

Domain access is indeed run through the standard Cloudflare proxy, which also means it will attempt to cache the files… In order to cache the files Cloudflare will remove the range header from a request, so that Cloudflare may receive the entire file in order to cache it. Makes sense, actually. Except in my case the files are always over 500mb, thus never eligible for cache, so this is always a disaster.

Also, Cloudflare doesn’t give a damn what the client asked for in this case. Cloudflare just gives the client the entire file while it’s at it. That’s a pretty terrible disregard for spec. I understand Cloudflare has to pull this trick in order to cache files, but why not satisfy the original range request toward the client? It is a mystery.

The solution in this case is to turn off caching for the domain which is used for R2 domain access through cache rules, pretty simple! Now the range is always satisfied by R2 (because R2 is cool like that).

Now there’s only one issue left:
R2 responds with a 200 to a range request, even though it consistently satisfies the range.

Edit:
Actually R2 responds to HEAD requests with a 200, normal range requests work absolutely fine. I suppose that solves all my problems, though surely the cache disaster is still a bug?

1 Like

I just wanted to say thanks for the insight and the solutions, it was a good read and definitely useful information.

I’d definitely call it a bug. I can totally see how each of the decisions made sense for their own products, but the result isn’t great. It’s a good sign that R2 itself can satisfy range requests though, both in terms of possible workarounds and the hope that the whole stack will get fixed at some point.

3 Likes

I have the same error where I’m serving mp4 in my app and in safari the video doesn’t play because Cloudflare only sometimes responds with the 206. I’m going to try your solution of disabling caching on the r2 domain because my files are also > 500mb and ineligible for caching and report back.

I’m actually having a bit of an issue disabling caching for the r2 bucket. How did you do it? I’m accessing my R2 public bucket through “domain access”.

If you are using domain access then the R2 domain should show up in the DNS records of your website. You cannot disable proxy here, because this record is managed by R2.

However, since the domain is proxied, not only caching applies but also WAF and caching rules. You can simply make a caching rule to exclude any request which match your R2 domain as a host.

In the same way you can apply WAF and transform rules. I myself use a transform to set the download name through the content-disposition response header based on an url parameter from the request. In a similar way I use WAF to verify hmac tokens, allowing me to make limited time access tokens for my R2 resources.

Awesome, thank you, that worked!