diff --git a/src/aphront/response/AphrontFileResponse.php b/src/aphront/response/AphrontFileResponse.php
--- a/src/aphront/response/AphrontFileResponse.php
+++ b/src/aphront/response/AphrontFileResponse.php
@@ -139,4 +139,29 @@
     return $this->getCompressResponse();
   }
 
+  public function parseHTTPRange($range) {
+    $begin = null;
+    $end = null;
+
+    $matches = null;
+    if (preg_match('/^bytes=(\d+)-(\d*)$/', $range, $matches)) {
+      // Note that the "Range" header specifies bytes differently than
+      // we do internally: the range 0-1 has 2 bytes (byte 0 and byte 1).
+      $begin = (int)$matches[1];
+
+      // The "Range" may be "200-299" or "200-", meaning "until end of file".
+      if (strlen($matches[2])) {
+        $range_end = (int)$matches[2];
+        $end = $range_end + 1;
+      } else {
+        $range_end = null;
+      }
+
+      $this->setHTTPResponseCode(206);
+      $this->setRange($begin, $range_end);
+    }
+
+    return array($begin, $end);
+  }
+
 }
diff --git a/src/applications/celerity/controller/CelerityResourceController.php b/src/applications/celerity/controller/CelerityResourceController.php
--- a/src/applications/celerity/controller/CelerityResourceController.php
+++ b/src/applications/celerity/controller/CelerityResourceController.php
@@ -104,9 +104,30 @@
     }
 
     $response = id(new AphrontFileResponse())
-      ->setContent($data)
-      ->setMimeType($type_map[$type])
-      ->setCompressResponse(true);
+      ->setMimeType($type_map[$type]);
+
+    $range = AphrontRequest::getHTTPHeader('Range');
+
+    if (strlen($range)) {
+      $response->setContentLength(strlen($data));
+
+      list($range_begin, $range_end) = $response->parseHTTPRange($range);
+
+      if ($range_begin !== null) {
+        if ($range_end !== null) {
+          $data = substr($data, $range_begin, ($range_end - $range_begin));
+        } else {
+          $data = substr($data, $range_begin);
+        }
+      }
+
+      $response->setContentIterator(array($data));
+    } else {
+      $response
+        ->setContent($data)
+        ->setCompressResponse(true);
+    }
+
 
     // NOTE: This is a piece of magic required to make WOFF fonts work in
     // Firefox and IE. Possibly we should generalize this more.
diff --git a/src/applications/files/controller/PhabricatorFileDataController.php b/src/applications/files/controller/PhabricatorFileDataController.php
--- a/src/applications/files/controller/PhabricatorFileDataController.php
+++ b/src/applications/files/controller/PhabricatorFileDataController.php
@@ -62,24 +62,8 @@
     // an initial request for bytes 0-1 of the audio file, and things go south
     // if we can't respond with a 206 Partial Content.
     $range = $request->getHTTPHeader('range');
-    if ($range) {
-      $matches = null;
-      if (preg_match('/^bytes=(\d+)-(\d*)$/', $range, $matches)) {
-        // Note that the "Range" header specifies bytes differently than
-        // we do internally: the range 0-1 has 2 bytes (byte 0 and byte 1).
-        $begin = (int)$matches[1];
-
-        // The "Range" may be "200-299" or "200-", meaning "until end of file".
-        if (strlen($matches[2])) {
-          $range_end = (int)$matches[2];
-          $end = $range_end + 1;
-        } else {
-          $range_end = null;
-        }
-
-        $response->setHTTPResponseCode(206);
-        $response->setRange($begin, $range_end);
-      }
+    if (strlen($range)) {
+      list($begin, $end) = $response->parseHTTPRange($range);
     }
 
     $is_viewable = $file->isViewableInBrowser();