Windows/Apache Sysadmin – Fixing the unadvertised speed limit

This is a post for any Windows sysadmins out there who administrate a server using Apache and PHP.

For a while now, our Windows download servers at work have had some transfer speed issues.  I would turn off all external traffic and try downloading a file directly, and it went nowhere near as quickly as it should have.  Turns out, there were 2 problems.

Problem 1: PHP readfile() apparently sucks

The first thing I noticed was that directly downloading a file via Apache was much faster than using PHP to pass it through with readfile() for some reason.  I fretted for a bit and ended up just doing this:

// Sends a file using fread, 16K at a time
function my_send_file($filename) {
$fp = fopen($filename, “rb”);
//start buffered download
if ($fp) {
while(!feof($fp)) {
print(fread($fp,1024*16));
flush(); ob_flush();
}
fclose($fp);
}
}

That made downloads go about 50% faster, but it still wasn’t near where it should be for some reason.  I left well enough alone for a while until it piqued my curiosity again, and lo and behold I found a few hits on Google!

Problem 2: Windows ignores Apache SendBufferSize setting

This is a rather annoying bug.  The only way to fix it is to edit the registry thusly:

  1. Open up regedit, then go to HKEY_LOCAL_MACHINE > SYSTEM > CurrentControlSet > Services > AFD > Parameters
  2. Create two DWORD values called “DefaultReceiveWindow” and “DefaultSendWindow”
  3. You then set both these values using DECIMAL (not Hex) using this formula:
    DefaultReceiveWindow = (Download Capacity in Kbps * 1024) / 8
    DefaultSendWindow = (Upload Capacity in Kbps * 1024) / 8
    For example, for a 10Mbps (~10000 Kbps) upload and download:
    DefaultReceiveWindow = (10000 * 1024) / 8 = 1280000
    DefaultSendWindow = (10000 * 1024) / 8 = 1280000
  4. Reboot and test your Apache speed.

We saw a 4-6x increase in speed after doing this. It’s possible that problem 1 above was related to this, so maybe after doing this readfile() would work just as well as fread().  But it ain’t broke now, so I’m not going to fix it.