Customizing Web Server and Runtime Settings for PHP

Last Updated: 04 May 2015

apache document root fpm nginx php rewrites

Table of Contents

PHP has a built-in web server that can be used to run on Heroku web dynos, however this is not recommended. Instead you should be using a boot script which is referenced in your Procfile to launch a web server together with PHP.

This article explains the different ways in which you can pass arguments to this boot script to customize settings for the PHP and HHVM runtimes as well as the web server software.

How applications are launched during dyno boot

During a deploy, Heroku will install the heroku/heroku-buildpack-php Composer package into your application as an additional dependency. This package contains boot scripts for launching PHP or HHVM together with either the Apache or the Nginx web servers.

The names of the boot scripts available for your use are:

  • heroku-php-apache2 (for PHP & Apache2)
  • heroku-php-nginx (for PHP & Nginx)
  • heroku-hhvm-apache2 (for HHVM & Apache2)
  • heroku-hhvm-nginx (for HHVM & Nginx)

These boot scripts installed by the heroku/heroku-buildpack-php package are placed into Composer’s vendor/bin directory, and will use configuration files from vendor/heroku/heroku-buildpack-php/conf/ to launch PHP-FPM or HHVM together with the web server of your choice.

Heroku recommends you maintain dev/prod parity by running the same packages locally. You can use the package you are running in production as a development dependency by adding it to the require-dev section of your composer.json. Then you can run foreman start to launch a local development environment that will work seamlessly on your development machine, provided that you have the Apache or Nginx installed.

Once you’ve installed the heroku/heroku-buildpack-php package locally you can view any of the boot scripts' help screen. The boot scripts accept various options and arguments that you can use to control settings related to PHP and the web server software. To get the detailed help you can execute any of the installed binaries from the list above with the --help flag. For example, to get help with options for launching PHP & Apache, you would run:

$ vendor/bin/heroku-php-apache2 --help

For HHVM & Nginx the command would be:

$ vendor/bin/heroku-hhvm-nginx --help

The name of the vendor/bin/ directory can be changed using configuration settings in your composer.json, so the commands above may be different for your application. To determine the correct path for the commands above, run:

$ composer config bin-dir

If the path returned by this command is something other than vendor/bin, adjust the commands above (and any relevant commands in the instructions below) accordingly.

Specifying which runtime to use

In addition to PHP, you may use the experimental HHVM runtime for your applications. For full details on supported features and versions, check the PHP reference article.

The heroku-php-apache2 and heroku-php-nginx scripts will use the PHP runtime together with the respective web server, while heroku-hhvm-apache2 and heroku-hhvm-nginx will use the HHVM runtime.

Refer to the following sections to learn what your Procfile should look like and what arguments are available to customize the configuration of your application.

Specifying which web server to use

In your Procfile, you must specify which of the boot scripts in the list at the beginning of this document to use. Heroku will then launch the corresponding web server together with PHP or HHVM when your dyno starts.

For example, to use Nginx together with PHP, your Procfile should contain:

web: vendor/bin/heroku-php-nginx

If you’d like to use HHVM and Apache instead:

web: vendor/bin/heroku-hhvm-apache2

Setting the document root

The most common configuration customization applications require is the so-called document root.

The document root is the directory where the application will begin looking for your .php files. To change the document root, you may supply the path to the directory (relative to your application root directory) as an argument to the boot script. It is not necessary to change configuration files or rewrite rules to define the document root.

For example, if you’re using Apache with PHP, and you’d like your document root to be set to the public sub-directory of your application (because that’s where your index.php and all images, CSS and JavaScript reside), your Procfile would look like this:

web: vendor/bin/heroku-php-apache2 public/

If you specify additional options to the script (as described in the sections that follow), you must ensure that the document root argument is at the end of the command, after all options; for example:

web: vendor/bin/heroku-php-nginx -C rewrite.conf www/

In order to not define a document root (and thus have the root directory of your application act as the document root), simply omit the argument altogether:

web: vendor/bin/heroku-php-apache2

Default behavior

Heroku will launch a FastCGI Process Manager (PHP-FPM) or HHVM application server. This application server will accept requests to execute PHP files from a web server, either Apache or Nginx.

The web server will bind to the port in the $PORT environment variable and respond to incoming HTTP requests; if the request is for an existing file with a name ending in .php, the web server will pass the request to execute this PHP file to the application server using the FastCGI protocol.

The configuration for these web servers can be customized.

Depending on the web server you chose (Apache or Nginx) each will come with different defaults. You can find them directly below.

Apache defaults

Apache uses a Virtual Host that responds to all hostnames. The document root is set up as a <Directory> reachable without access limitations and AllowOverride All set to enable the use of .htaccess files. Any request to a URL ending on .php will be rewritten to PHP-FPM using a proxy endpoint named fcgi://heroku-fcgi via mod_proxy_fcgi. The DirectoryIndex directive is set to “index.php index.html index.html”.

Nginx defaults

Nginx uses a server that responds to all hostnames. The document root has no access limitations. Any request to a URL containing .php will be be rendered by fastcgi_pass with PHP-FPM using an upstream called “heroku-fcgi” after a try_files call on the entire URL. The index directive is set to “index.php index.html index.html

Web server settings

You can configure Apache or Nginx, by providing your own site-specific settings (recommended). You can also replace the entire configuration for the host, this can be dangerous and should not be attempted unless you wish to maintain these settings manually. Replacing the configuration for the host is possible, but we cannot support your individual configuration choices.

Apache

Using .htaccess

You may use regular .htaccess files to customize the behavior of the Apache HTTP server. The AllowOverride option for the document root is set to all, which means you can use any configuration directive allowed in .htaccess contexts.

This is the recommended approach for customizing Apache settings, as it does not require the use of a custom (and potentially error-prone) config include.

Using a custom application level Apache configuration

Inside the default server level configuration file Heroku uses during the startup of Apache, it includes a very simple default application level config file. You can replace this file with your custom configuration. For example, to configure Apache to use some rewrite rules for your Symfony2 application, you’d create an apache_app.conf inside your application’s root directory with the following contents:

RewriteEngine On

RewriteCond %{REQUEST_URI}::$1 ^(/.+)/(.*)::\2$
RewriteRule ^(.*) - [E=BASE:%1]

RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^app\.php(/(.*)|$) %{ENV:BASE}/$2 [R=301,L]

RewriteCond %{REQUEST_FILENAME} -f
RewriteRule .? - [L]

RewriteRule .? %{ENV:BASE}/app.php [L]

In this particular case, you may also put the corresponding rewrite rules into a .htaccess file. In fact, the entire snippet was copied from the Symfony Standard Edition’s .htaccess file

Then, you can use the -C argument of the boot script to tell Heroku to include this file for you:

web: vendor/bin/heroku-php-apache2 -C apache_app.conf

To understand in which context this configuration file will be included, we recommend you take a look at the server-level configuration for Apache that Heroku loads right after the system-wide configuration file during startup.

Nginx

Using a custom application level Nginx configuration

Inside the default server level configuration file Heroku uses during the startup of Nginx, it includes a very simple default application level config file. You can replace this file with your custom configuration. For example, to configure Nginx to use some rewrite rules for your Symfony2 application, you’d create a nginx_app.conf inside your application’s root directory with the following contents:

location / {
    # try to serve file directly, fallback to rewrite
    try_files $uri @rewriteapp;
}

location @rewriteapp {
    # rewrite all to app.php
    rewrite ^(.*)$ /app.php/$1 last;
}

location ~ ^/(app|app_dev|config)\.php(/|$) {
    try_files @heroku-fcgi @heroku-fcgi;
    internal;
}

The internal; directive in the config above ensures that calls to /app.php etc are not allowed if they aren’t coming from rewrites.

You can always use try_files @heroku-fcgi @heroku-fcgi; to delegate handling to the default rule for .php files using the “@heroku-fcgi” named location. This is the easiest and most portable way of invoking PHP. When using fastcgi_pass instead, use “heroku-fcgi” as the destination. It’s an upstream group and configured so that your FastCGI requests will always be handed to the correct backend process.

After you have created a new default configuration you can tell your application to start with this configuration by using the -C argument of the boot script.

For example if you’ve written your own nginx_app.conf you can modify your Procfile to use this configuration file:

web: vendor/bin/heroku-php-nginx -C nginx_app.conf

To understand in which context this configuration file will be included, we recommend you take a look at the server-level configuration for Nginx that Heroku loads right after the system-wide configuration file during startup.

PHP runtime settings

INI settings

Heroku supports several of the methods that PHP offers for changing its php.ini settings.

In addition to using ini_set in your PHP code at runtime, you may also configure Heroku to use an additional configuration file for PHP-FPM that may contain INI directives and will be loaded after the default configuration during startup.

The recommended approach for most cases is a per-directory .user.ini file that PHP-FPM will pick up automatically.

PHP will read settings from any .user.ini file in the same directory as the .php file that is being served. PHP will also read settings from a .user.ini file in any parent directory up to the document root. Heroku recommends using .user.ini files to customize PHP settings. Please refer to the section on .user.ini files in the PHP manual to learn more about this feature and which settings you can control through it.

There is practically no performance penalty associated with this method, as the user_ini.cache_ttl setting is set high enough for PHP to only scan for this file once during the lifetime of a dyno. We recommend setting this to 24 hours.

For example, to change the maximum size for a file uploaded through an HTML form to 5 MB, you would place a .user.ini into your document root with the following contents:

upload_max_filesize = 5M

PHP-FPM configuration include

A small set of PHP.ini configuration directives cannot be modified using .user.ini; for example, always_populate_raw_post_data cannot be changed this way despite its official PHP_INI_PERDIR status.

In this case, you can pass additional configuration settings for PHP at startup time using a custom configuration file include for PHP’s FastCGI Process Manager by using the php_value and php_flag directives as documented here in the PHP manual.

For example, to set always_poplate_raw_post_data to -1 to prevent deprecation notices in PHP 5.6 when accessing php://input, you would add an fpm_custom.conf file to your application with the following contents:

php_value[always_populate_raw_post_data]=-1

After you have created such a new custom FPM configuration include you can tell your application to start with this configuration by using the -F option of the boot script. For the example above, you’d modify your Procfile as follows to use that configuration file (assuming your document root is the sub-directory public/):

web: vendor/bin/heroku-php-nginx -F fpm_custom.conf public/

In addition to php_value and php_flag for php.ini specific settings, any pool specific PHP-FPM configuration directives are valid in that configuration file, so you can use it to fine tune FPM behavior, but be advised that you may easily cause your application to stop working when using the wrong combination of directives, so use with caution.

HHVM runtime settings

INI settings

HHVM allows you to change settings via directives in a php.ini file. In addition to using ini_set in your PHP code at runtime, you may also configure Heroku to use an additional php.ini file that will be loaded after the default configuration during startup.

Additional php.ini file

Using the -I option of the heroku-hhvm-apache2 and heroku-hhvm-nginx boot scripts, you may define additional INI settings that will be loaded without fully replacing the default php.ini used by HHVM.

For example, to set the memory_limit of your application to 64 MB, you would create an hhvm_custom.ini file with the following contents:

memory_limit=64M

After you have created this new custom INI, you can tell your application to start with this configuration by using the -I option of the boot script. For the example above and when running Nginx together with HHVM, you’d modify your Procfile as follows to use that configuration file:

web: vendor/bin/heroku-hhvm-nginx -I hhvm_custom.ini