Unable to download static content when using php images on Azure App Service - Linux

3 minute read | By Srikanth Sureddy

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.

  1. If you are downloading the file directly using a link.
  2. 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.

  1. Using a startup script, we will modify apache configuration to disable mmap and sendfile support. You can find more about these features here:
  2. Steps to update apache configuration using startup script:

    1. 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.

    2. 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.

    3. 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
      
    4. 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.

      Screenshot here:

      From az cli:

      az webapp config set -g MyResourceGroup -n mysamplesite --startup-file /home/site/wwwroot/startup.sh
      
    5. 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.";
      }
      ?>