Customizing Web Server and Runtime Settings for PHP
Last updated July 04, 2024
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 runtime 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 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)
These boot scripts installed by the heroku/heroku-buildpack-php
package are placed on $PATH
, and will use configuration files from vendor/heroku/heroku-buildpack-php/conf/
to launch PHP-FPM 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
file. Then you can launch a local development environment that will work seamlessly on your development machine, provided that you have 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:
$ heroku-php-apache2 --help
Specifying which runtime to use
For full details on supported features and versions, check the PHP reference article.
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 when your dyno starts.
For example, to use Nginx together with PHP, your Procfile
should contain:
web: heroku-php-nginx
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: 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: 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: heroku-php-apache2
Default behavior
Heroku will launch a FastCGI Process Manager (PHP-FPM) 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.
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.
Most modern web frameworks require very little configuration which can be placed into a .htaccess
file in the document root directory.
For example, to configure Apache for a Laravel or Symfony app, create a .htaccess
file in the public/
directory with the following line:
FallbackResource /index.php
Then, your Procfile
command only needs to specify a document root directory:
web: heroku-php-apache2 public/
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 application, you’d create an apache_app.conf
inside your application’s root directory with contents like the following:
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]
Then, in your Procfile
, you can use the -C
argument of the boot script to tell Heroku to include this file for you:
web: 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 Symfony application, you’d create an 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 index.php
rewrite ^(.*)$ /index.php/$1 last;
}
location ~ ^/index\.php(/|$) {
try_files @heroku-fcgi @heroku-fcgi;
# ensure that /index.php isn't accessible directly, but only through a rewrite
internal;
}
The internal;
directive in the config above ensures that calls to /index.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: 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.
The methods below only apply to web
dynos, as worker processes do not use PHP-FPM, but typically invoke the php
executable directly, which allow passing of INI directives using the -d
command line option.
.user.ini
files (recommended)
PHP-FPM will read settings from any .user.ini
file in the same directory as the .php
file that is being served via a web server; if none is found, it will also read settings from a .user.ini
file in any parent directory up to your application’s configured document root.
Heroku recommends using .user.ini
files to customize PHP settings for web traffic. 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.
For example, to change the maximum size for a single file uploaded through an HTML form to 5 MB, and the total POST
size to 20 MB, you would place a .user.ini
into your document root with the following contents:
post_max_size = 20M
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
changeability mode. The same is true for any directive with PHP_INI_SYSTEM
changeability mode.
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: heroku-php-nginx -F fpm_custom.conf public/
PHP-FPM settings
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 PHP-FPM’s behavior.
Incorrect PHP-FPM configuration directives may easily cause your application to stop working or behave unexpectedly, so use with caution.
For example, PHP-FPM can be instructed to log longer running requests that exceed a certain number of seconds using the request_slowlog_timeout
directive, in which case it will print information about the request, including a backtrace, to heroku logs
:
request_slowlog_timeout = 1s
The default value for request_slowlog_timeout
is 3s
.