Unable to download static content when using php images on Azure App Service - Linux
Unable to download static content (>2MB) when using PHP 7.2 or 7.3 or 7.4 images on Azure App Service Linux
With a recent update that was pushed out to App Service Linux, sites using our php images have started seeing issues downloading static files (jpg,png,zip,pdf etc) > 2MB.
This is happening at 2 levels.
- If you are downloading the file directly using a link.
- If you are downloading the file through php script using specific PHP APIs (e.g. fpassthru()).
We are currently investigating why this is happening. But you can fix this with a bit of customization.
How to fix if your download is failing using a direct link
- Using a startup script, we will modify apache configuration to disable mmap and sendfile support. You can find more about these features here:
-
Steps to update apache configuration using startup script:
-
Create a startup.sh in your local machine with below content. Make sure you have Linux-style (LF) line endings in startup.sh file.
startup.sh contents:
#!/bin/sh echo "Executing custom startup script." cp /home/site/wwwroot/apache2.conf /etc/apache2/apache2.conf cd /home/site/wwwroot export APACHE_PORT=8080 if [ -n "$PHP_ORIGIN" ] && [ "$PHP_ORIGIN" = "php-fpm" ]; then export NGINX_DOCUMENT_ROOT='/home/site/wwwroot' service nginx start else export APACHE_DOCUMENT_ROOT='/home/site/wwwroot' fi apache2-foreground
Sample startup script is here.
-
Download apache2.conf file for your site. Modify it to disable mmap and sendfile. A snippet is below:
apache configuration snippet:
<Directory "${APACHE_DOCUMENT_ROOT}"> Options Indexes FollowSymLinks EnableMMAP Off EnableSendfile Off AllowOverride None Require all granted </Directory>
Sample apache2.conf file is here.
-
Upload startup.sh and apache2.conf. You can use your publishing credentials and curl to acheive this.
Sample below for a site mysamplesite
If uploading from a Linux Machine:
curl -X PUT --data-binary @startup.sh 'https://$mysamplesite:<publishing-password>@mysamplesite.scm.azurewebsites.net/api/vfs/site/wwwroot/starup.sh' -v curl -X PUT --data-binary @apache2.conf 'https://$mysamplesite:<publishing-password>@mysamplesite.scm.azurewebsites.net/api/vfs/site/wwwroot/apache2.conf' -v
If uploading from a Windows Machine:
curl -X PUT --data-binary @startup.sh https://$mysamplesite:<publishing-password>@mysamplesite.scm.azurewebsites.net/api/vfs/site/wwwroot/startup.sh -v curl -X PUT --data-binary @apache2.conf https://$mysamplesite:<publishing-password>@mysamplesite.scm.azurewebsites.net/api/vfs/site/wwwroot/apache2.conf -v
If your site is in an ASE (App Service Environment), then use below samples. Assuming your site name is mysamplesite and ase domain name is contoso.com.
If uploading from a Linux Machine:
curl -X PUT --data-binary @startup.sh 'https://$mysamplesite:<publishing-password>@mysamplesite.scm.contoso.com/api/vfs/site/wwwroot/starup.sh' -v curl -X PUT --data-binary @apache2.conf 'https://$mysamplesite:<publishing-password>@mysamplesite.scm.contoso.com/api/vfs/site/wwwroot/apache2.conf' -v
If uploading from a Windows Machine:
curl -X PUT --data-binary @startup.sh https://$mysamplesite:<publishing-password>@mysamplesite.scm.contoso.com/api/vfs/site/wwwroot/startup.sh -v curl -X PUT --data-binary @apache2.conf https://$mysamplesite:<publishing-password>@mysamplesite.scm.contoso.com/api/vfs/site/wwwroot/apache2.conf -v
-
Set up startup script using Azure Portal or az cli.
From Portal, Browse to the Site -> Configuration (under Settings) -> General Settings Tab -> Startup Command (under Stack settings)
Set it to /home/site/wwwroot/startup.sh.
From az cli:
az webapp config set -g MyResourceGroup -n mysamplesite --startup-file /home/site/wwwroot/startup.sh
-
This will restart the site and change apache configuration that will enable file downloads > 2MB.
-
If you are downloading files through php script using specific PHP APIs (e.g. fpassthru())
-
The fix will require changing your code that reads files. It involves moving away from reading files using fpassthru.
A sample php code snippet is below:
<?php $file = './xx.txt'; $query_file = htmlspecialchars($_GET["file"]); if ($query_file != null) { $file = $query_file; } if (file_exists($file)) { header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="'.basename($file).'"'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: ' . filesize($file)); $fp = fopen($file, 'rb'); // fpassthru($fp); ob_flush(); flush(); while(!feof($fp)) { print(fread($fp, 2048)); ob_flush(); } ob_flush(); flush(); fclose ($fp); exit; } else { echo "File " . $file . " not found."; } ?>