Вариантов это сделать - 2.
1) Если у Вас файлы маленькие - можно просто читать их из php скрипта и отдавать.
Перед этим можно проверять имеет ли право текущий пользователь скачивать этот файл.
Что то вроде
$content = file_get_contents('/path/to/file/in/private/dir/filename');
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.$file_info->title.'"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . strlen($content));
echo ($content);
Плюс то что не требуется никакой дополнительной настройки, не зависит от ПО сервера, минус то что это решение жрет память т.к файл целиком грузится в php, жрет время бекенда т.к php скрипт работает пока не закончится загрузка.
В целом это решение я использовать не рекомендую.
2) Использовать nginx и директиву X-Accel-Redirect.
В конфиге nginx
location /protected/ {
root /www/mysite.com/;
rewrite ^/protected/(.*)$ /$1 break;
internal;
}
В php коде соответственно
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.$file_info->title.'"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('X-Accel-Redirect: /protected'. $file_info->file);
Решение отличное, быстрое.