Configure a Linux PHP app for Azure App Service

This guide shows you how to configure the built-in PHP runtime for web apps, mobile back ends, and API apps in Azure App Service.

This guide provides key concepts and instructions for PHP developers who use a built-in Linux container in App Service. If you've never used Azure App Service, follow the PHP quickstart and PHP with MySQL tutorial first.

Show PHP version

To show the current PHP version, run the following command in the Cloud Shell:

az webapp config show --resource-group <resource-group-name> --name <app-name> --query linuxFxVersion

To show all supported PHP versions, run the following command in the Cloud Shell:

az webapp list-runtimes --linux | grep PHP

Set PHP version

Run the following command in the Cloud Shell to set the PHP version to 7.2:

az webapp config set --name <app-name> --resource-group <resource-group-name> --linux-fx-version "PHP|7.2"

Run Composer

By default, Kudu doesn't run Composer. To enable Composer automation during Kudu deployment, you need to supply a custom deployment script.

From a local terminal window, change directory to your repository root. Follow the command-line installation steps to download composer.phar.

Run the following commands:

npm install kuduscript -g
kuduscript --php --scriptType bash --suppressPrompt

Your repository root now has two new files in addition to composer.phar: .deployment and deploy.sh. These files work both for Windows and Linux flavors of App Service.

Open deploy.sh and find the Deployment section. Replace the whole section with the following code:

##################################################################################################################################
# Deployment
# ----------

echo PHP deployment

# 1. KuduSync
if [[ "$IN_PLACE_DEPLOYMENT" -ne "1" ]]; then
  "$KUDU_SYNC_CMD" -v 50 -f "$DEPLOYMENT_SOURCE" -t "$DEPLOYMENT_TARGET" -n "$NEXT_MANIFEST_PATH" -p "$PREVIOUS_MANIFEST_PATH" -i ".git;.hg;.deployment;deploy.sh"
  exitWithMessageOnError "Kudu Sync failed"
fi

# 3. Initialize Composer Config
initializeDeploymentConfig

# 4. Use composer
echo "$DEPLOYMENT_TARGET"
if [ -e "$DEPLOYMENT_TARGET/composer.json" ]; then
  echo "Found composer.json"
  pushd "$DEPLOYMENT_TARGET"
  php composer.phar install $COMPOSER_ARGS
  exitWithMessageOnError "Composer install failed"
  popd
fi
##################################################################################################################################

Commit all your changes and deploy your code again. Composer should now be running as part of deployment automation.

Customize start-up

By default, the built-in PHP container run the Apache server. At start-up, it runs apache2ctl -D FOREGROUND". If you like, you can run a different command at start-up, by running the following command in the Cloud Shell:

az webapp config set --resource-group <resource-group-name> --name <app-name> --startup-file "<custom-command>"

Access environment variables

In App Service, you can set app settings outside of your app code. Then you can access them using the standard getenv() pattern. For example, to access an app setting called DB_HOST, use the following code:

getenv("DB_HOST")

Change site root

The web framework of your choice may use a subdirectory as the site root. For example, Laravel, uses the public/ subdirectory as the site root.

The default PHP image for App Service uses Apache, and it doesn't let you customize the site root for your app. To work around this limitation, add an .htaccess file to your repository root with the following content:

<IfModule mod_rewrite.c>
    RewriteEngine on

    RewriteRule ^.*$ /public/$1 [NC,L,QSA]
</IfModule>

If you would rather not use .htaccess rewrite, you can deploy your Laravel application with a custom Docker image instead.

Detect HTTPS session

In App Service, SSL termination happens at the network load balancers, so all HTTPS requests reach your app as unencrypted HTTP requests. If your app logic needs to check if the user requests are encrypted or not, inspect the X-Forwarded-Proto header.

if (isset($_SERVER['X-Forwarded-Proto']) && $_SERVER['X-Forwarded-Proto'] === 'https') {
  // Do something when HTTPS is used
}

Popular web frameworks let you access the X-Forwarded-* information in your standard app pattern. In CodeIgniter, the is_https() checks the value of X_FORWARDED_PROTO by default.

Customize php.ini settings

If you need to make changes to your PHP installation, you can change any of the php.ini directives by following these steps.

Note

The best way to see the PHP version and the current php.ini configuration is to call phpinfo() in your app.

Customize-non-PHP_INI_SYSTEM directives

To customize PHP_INI_USER, PHP_INI_PERDIR, and PHP_INI_ALL directives (see php.ini directives), add an .htaccess file to the root directory of your app.

In the .htaccess file, add the directives using the php_value <directive-name> <value> syntax. For example:

php_value upload_max_filesize 1000M
php_value post_max_size 2000M
php_value memory_limit 3000M
php_value max_execution_time 180
php_value max_input_time 180
php_value display_errors On
php_value upload_max_filesize 10M

Redeploy your app with the changes and restart it. If you deploy it with Kudu (for example, using Git), it's automatically restarted after deployment.

As an alternative to using .htaccess, you can use ini_set() in your app to customize these non-PHP_INI_SYSTEM directives.

Customize PHP_INI_SYSTEM directives

To customize PHP_INI_SYSTEM directives (see php.ini directives), you can't use the .htaccess approach. App Service provides a separate mechanism using the PHP_INI_SCAN_DIR app setting.

First, run the following command in the Cloud Shell to add an app setting called PHP_INI_SCAN_DIR:

az webapp config appsettings set --name <app-name> --resource-group <resource-group-name> --settings PHP_INI_SCAN_DIR="/usr/local/etc/php/conf.d:/home/site/ini"

/usr/local/etc/php/conf.d is the default directory where php.ini exists. /home/site/ini is the custom directory in which you'll add a custom .ini file. You separate the values with a :.

Navigate to the web SSH session with your Linux container (https://cephalin-container.scm.azurewebsites.net/webssh/host).

Create a directory in /home/site called ini, then create an .ini file in the /home/site/ini directory (for example, settings.ini) with the directives you want to customize. Use the same syntax you would use in a php.ini file.

Tip

In the built-in Linux containers in App Service, /home is used as persisted shared storage.

For example, to change the value of expose_php run the following commands:

cd /home/site
mkdir ini
echo "expose_php = Off" >> ini/setting.ini

For the changes to take effect, restart the app.

Enable PHP extensions

The built-in PHP installations contain the most commonly used extensions. You can enable additional extensions in the same way that you customize php.ini directives.

Note

The best way to see the PHP version and the current php.ini configuration is to call phpinfo() in your app.

To enable additional extensions, by following these steps:

Add a bin directory to the root directory of your app and put the .so extension files in it (for example, mongodb.so). Make sure that the extensions are compatible with the PHP version in Azure and are VC9 and non-thread-safe (nts) compatible.

Deploy your changes.

Follow the steps in Customize PHP_INI_SYSTEM directives, add the extensions into the custom .ini file with the extension or zend_extension directives.

extension=/home/site/wwwroot/bin/mongodb.so
zend_extension=/home/site/wwwroot/bin/xdebug.so

For the changes to take effect, restart the app.

Access diagnostic logs

You can access the console logs generated from inside the container. First, turn on container logging by running the following command in the Cloud Shell:

az webapp log config --name <app-name> --resource-group myResourceGroup --docker-container-logging filesystem

Once container logging is turned on, run the following command to see the log stream:

az webapp log tail --name <app-name> --resource-group myResourceGroup

If you don't see console logs immediately, check again in 30 seconds.

Note

You can also inspect the log files from the browser at https://<app-name>.scm.azurewebsites.net/api/logs/docker.

To stop log streaming at any time, type Ctrl+C.

Open SSH session in browser

To make open a direct SSH session with your container, your app should be running.

Paste the following URL into your browser and replace <app-name> with your app name:

https://<app-name>.scm.azurewebsites.net/webssh/host

If you're not yet authenticated, you're required to authenticate with your Azure subscription to connect. Once authenticated, you see an in-browser shell, where you can run commands inside your container.

SSH connection

Note

Any changes you make outside the /home directory are stored in the container itself and don't persist beyond an app restart.

To open a remote SSH session from your local machine, see Open SSH session from remote shell.

Troubleshooting

When a working PHP app behaves differently in App Service or has errors, try the following:

  • Access the log stream.
  • Test the app locally in production mode. App Service runs your Node.js apps in production mode, so you need to make sure that your project works as expected in production mode locally. For example:
    • Depending on your composer.json, different packages may be installed for production mode (require vs. require-dev).
    • Certain web frameworks may deploy static files differently in production mode.
    • Certain web frameworks may use custom startup scripts when running in production mode.
  • Run your app in App Service in debug mode. For example, in Laravel, you can configure your app to output debug messages in production by setting the APP_DEBUG app setting to true.

robots933456

You may see the following message in the container logs:

2019-04-08T14:07:56.641002476Z "-" - - [08/Apr/2019:14:07:56 +0000] "GET /robots933456.txt HTTP/1.1" 404 415 "-" "-"

You can safely ignore this message. /robots933456.txt is a dummy URL path that App Service uses to check if the container is capable of serving requests. A 404 response simply indicates that the path doesn't exist, but it lets App Service know that the container is healthy and ready to respond to requests.

Next steps