File download results in 0 byte file when using cloudflare

I’ve been running this test on my website for days now and it still baffles me.

I have a zip file on my server. Within the zip file is a single text file that contains the phrase “this is a test”.

In my file api.php, I have the following code:


$originalFileName = "";

header("content-encoding: none");
header("Content-Type: application/zip");  
header("Content-Disposition: attachment; filename=\"" . "" . "\"");
header("Content-Length: " . filesize($originalFileName));



When I visit the api.php file in firefox. It prompts to download “” with a file size of 128 bytes.

When I click save. It creates the file on my desktop, but the file is 0 bytes in size and is empty.

If I visit the url using the direct IP address i.e.

It downloads the zip file and contains the text file as its suppose to.

The domain is under Cloudflare with the caching disabled and developer mode on so it shouldn’t be that different.

I used firefox’s dev tools to copy the responses from the domain request and the ip request

Domain request - This makes the 0 byte empty zip file

HTTP/1.1 200 OK
Date: Sat, 01 Jun 2019 10:15:22 GMT
content-disposition: attachment; filename=""
Content-Length: 128
Content-Type: application/zip
content-encoding: none
Connection: keep-alive
X-Powered-By: PHP/7.2.16
Vary: User-Agent
Alt-Svc: h2=":443"; ma=60
CF-RAY: 4e006d0d4e615629-ORD
Server: Cloudflare


Direct IP request - This makes the 128 byte zip file

HTTP/1.1 200 OK
Date: Sat, 01 Jun 2019 10:15:57 GMT
content-disposition: attachment; filename=""
content-length: 128
Content-Type: application/zip
content-encoding: none
Connection: Upgrade, Keep-Alive
X-Powered-By: PHP/7.2.16
Upgrade: h2,h2c
Vary: User-Agent
Keep-Alive: timeout=5
Server: Apache


Both the response payloads are identical and besides a few tiny changes in the response headers.

Such as the letter casing and the Cloudflare headers.

I don’t see any reason why the zip file should be 0 bytes when trying to save the download prompt through the domain.

When I press ctrl+j to view the download history. It looks fine for the direct ip download on the bottom and a weird message for the top domain one.

enter image description here

Additionally, I’ve tried to make my PHP script output the exact same headers as the when I do the direct ip download.

However, it seems Cloudflare (even when disable mode is on) strips out the “Connection” header whenever I set it.

Does anyone have any suggestions on how to solve this?

Edit: Additional info, it only does this issue with zip files.

It works fine with text and 7z files.

The first thing I notice is the content length does not match the length of the response body (128 vs. 172 characters). Did you check that?

Can you post the actual URL?

I’d rather not post the domain since it would defeat the point of using Cloudflare to hide the ip from ddosing since anyone with google would be able to find this post.

As for the response body length. It is 172 since it is base64 encoded even though I am not encoding it myself and I confirmed the file size is 128 bytes by using filesize().

I don’t know if it is actually encoded or if firefox dev tools is showing it to me that way.

Firefox dev tools says 482 B was transferred, but the total length of the response headers and the response payload is 526 characters in length.

The Content-Length is 128 bytes in the direct ip get request as well and it downloads fine there.

If you want you can submit the IP and the domain at and respond back here with the time when you ran the checks.

As for base64, you should check if that comes as base64 over the wire or is only displayed in that way. In the former case you might have just found the reason for your error, as the body would be longer than the specified content length.

Anyhow, for starters I’d omit the two header calls for Content-Encoding and Content-Length. The former does not contain a valid value to begin with and the latter will be automatically handled by PHP anyhow.

This topic was automatically closed after 31 days. New replies are no longer allowed.