Чтобы принудительно загрузить файл, необходимо отправить заголовки HTTP, которые сообщат браузеру, что этот файл следует считать загрузкой. Это переопределить Content-Type файла.
http://en.wikipedia.org/wiki/List_of_HTTP_headers
По сути, когда ваш веб-сервер получает файл, запрошенный браузером, он сначала проверяет расширение файла и угадывает тип mime. Затем он создает ответ HTTP и вставляет заголовок Content-Type со значением угаданного mime-типа.
Вы можете сделать так, чтобы PHP явно устанавливал тип содержимого файла.
Пример уже приведен выше.
header("Content-type: application/force-download");
или
header("Content-type: application/octet-stream");
Для загрузки также необходимо установить заголовки ответа HTTP Content-Disposition и Content-Transfer-Encoding.
Они определены в спецификациях MIME: http://www.faqs.org/rfcs/rfc1521.html
Также полезно определить длину содержимого, поскольку это позволяет браузеру предоставить пользователю индикатор выполнения загрузки.
Возможно, вы захотите использовать заголовки диапазона, чтобы разрешить запрос по частям.
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html