PHP logo

RHEL 7 provides the Apache HTTP Server version 2.4 and PHP version 5.4.

The most common configuration for Apache httpd and PHP uses, but this has some limitations and drawbacks:

  • a single PHP version of mod_php can be used
  • mod_php run in the httpd process, without any isolation
  • mod_phpis only supported for the prefork MPM

This article will explain how to configure Apache httpd to delegate PHP scripts execution to a backend using the FastCGI protocol, how to use a more recent PHP version, how to run multiple PHP versions, and how to improve Apache httpd performance.

The Apache httpd package available in RHEL provides all features needed to use such configuration.

1. Switching to php-fpm

1.1. Remove mod_php

It is recommended to remove or disable mod_php to reduce the memory footprint of each httpd process.

You can either remove the php package, which only provides this module:

 yum remove php

or simply disable it by commenting out the LoadModule directive in /etc/httpd/conf.modules.d/10-php.conf.

 # disabled # LoadModule php5_module modules/libphp5.so

1.2. Install php-fpm

You now can install the php-fpm and enable its service.

 yum install php-fpm
 systemctl start php-fpm
 systemctl enable php-fpm

Notice: php-fpm package is available in the optional channel, which has to be enabled.

To configure PHP scripts execution, edit or create the /etc/httpd/conf.d/php.conf file:

The following lines prevent .user.ini files from being viewed by Web clients.

  <Files ".user.ini">
    Require all denied
  </Files>

Allow php to handle Multiviews:

  AddType text/html .php

Add index.php to the list of files that will be served as directory indexes:

  DirectoryIndex index.php

Following line, enable the http authorization headers:

  SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1

Redirect the PHP scripts execution to the FPM backend.

  <FilesMatch \.php$>
    SetHandler "proxy:fcgi://127.0.0.1:9000"
  </FilesMatch>

If you have some php_value directive in this file, you need to remove them, they are only for mod_php.

Now you can (re)start the web server and a simple PHP test page with;

<?php phpinfo();

it will show you are now running PHP through the FatCGI backend.

PHP Version 5.4.16
Server API= FPM/FastCGI

1.3. PHP tuning

The main FPM configuration file is /etc/php-fpm.conf, which have a lot of comments explaining each option.

FPM can run various pools, each one running PHP scripts with possible different options, the default pool (www) configuration file is /etc/php-fpm.d/www.conf, which also have lot of comments.

1.3.1. php_value, php-flag

PHP Options can be set using the php_value, php_admin_value, php_flag and php_admin_flag directives:

  • with mod_php,  in the Apache httpd configuration files.
  • with FPM, in the pool configuration files.

1.3.2. .htaccess

Additional options can be set in a specific directory:

  • with mod_php, using a .htaccess file.
  • with FPM, using a .user.ini file (php_* keywords are not needed).

1.3.3. Process tuning

FPM run as a daemon and launch various processes to be able to handle various requests simultaneously, and offers various modes:

  • pm = ondemand, child is only started when a connection is open and stopped when idle, suitable for development environment
  • pm = dynamic, a set of idle processes is always running, more processes can be started if needed, suitable for production
  • pm = static, a fixed set of processes is always running, suitable for production, can be better for performance

1.4. Apache HTTP Server tuning

1.4.1. Threaded MPM

By default, the Apache HTTP Server uses a set of processes to manage incoming requests (prefork MPM).

As we now don't use mod_php we can switch to a threaded MPM (worker of an event) so a set of threads will manage the requests, reducing the number of running processes and the memory footprint, and improving performance, especially when a lot of static files are served.

Switch the used MPM in the /etc/httpd/conf.modules.d/00-mpm.conf configuration files.

  # disabled # LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
  # disabled # LoadModule mpm_worker_module modules/mod_mpm_worker.so
  LoadModule mpm_event_module modules/mod_mpm_event.so

1.4.2. Unix Domain Socket

By default, FPM listens for incoming requests on a network socket but can use a Unix Domain Socket, which can slightly improve performance.

In FPM pool configuration:

  listen = /run/php-fpm/www.sock
  listen.owner = apache
  listen.mode = 0660

In Apache httpd configuration:

  SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost"

1.4.2. Separate frontend and backend servers

By default, FPM listens for incoming requests on a local network socket. Of course, it can run on a separate server, another virtual machine or a container (docker instance)

In FPM pool configuration:

  listen = 10.0.0.2:9000
  listen.allowed_clients = 10.0.0.1

In Apache httpd configuration:

  SetHandler "proxy:fcgi://10.0.0.2:9000"

1.4.3 Multiple php backends

To be able to handle more simultaneous requests, we may want to balance the load between various PHP backends, which is easy.

Apache httpd configuration sample, with 3 backends:

  # Load balancer creation
  <Proxy balancer://phpfpmlb>
    BalancerMember fcgi://10.0.0.2:9000
    BalancerMember fcgi://10.0.0.3:9000
    BalancerMember fcgi://10.0.0.4:9000
  </Proxy>
  # Redirect PHP execution to the balancer
  <FilesMatch \.php$>
    SetHandler "proxy:balancer://phpfpmlb"
 </FilesMatch>

2. Running a recent PHP version

RHEL provides PHP version 5.4 which was the current version when RHEL-7 was released, but which can be too old for some recent projects.

PHP versions 5.6 and 7.0 are supported on RHEL today as part of Red Hat Software Collections (RHSCL), and PHP version 7.1 is currently in Beta testing with the 3.0 update of RHSCL.

In the example below, we'll use version 7.0, but it can be used for the others available versions.

2.1. Installation

Install the Software Collection, after having enabled the RHSCL channel:

  yum install rh-php70

Install the FPM service for this version:

  yum install rh-php70-php-fpm

Install any needed additional extensions:

  yum install rh-php70-php-mbstring rh-php70-php-pgsql rh-php70-php-opcache

Tips: compare the list of available extensions to ensure everything needed is available.

  php --modules | tee /tmp/54
  scl enable rh-php70 'php --modules' | tee /tmp/70
  diff /tmp/54 /tmp/70

Tips: never rely on the package name, but prefer extension name (e.g. php-mysqli or rh-php70-php-simplexml), as the package layout may change across versions.

2.2. Switch to newer PHP versions

When running FPM, this is as simple as stopping the old version service and starting the new one:

  systemctl stop  php-fpm
  systemctl start rh-php70-php-fpm

2.3. Additional packages

The Software Collections provide the same set of PHP extensions than standard packages in RHEL.

As users are used to finding some additional extensions,  in the EPEL repository, an additional extension can be found in the community centos-sclo-sclo repository, for more information search sclo-php on https://www.softwarecollections.org/.

3. Running multiple versions of PHP

As PHP execution is redirected to the FastCGI service using the SetHandler directive, this can be set per vhost, project or directory.

In the example below, we will run both PHP version 5.4 from base system (for some legacy applications, already configured above) and PHP version 7.1 simultaneously.

3.1. Installation

Install the Software Collection, after having enabled the RHSCL beta channel:

  yum install rh-php71 rh-php71-php-fpm rh-php71-php-mbstring rh-php71-php-opcache ...

Configure FPM to listen to a different port than the one used by the default php-fpm service, in /etc/opt/rh/rh-php71/php-fpm.d/www.conf.

  listen = 127.0.0.1:9071

Ensure this port is not blocked by SELinux:

  semanage port -a -t http_port_t -p tcp 9071

Start the service:

  systemctl start rh-php71-php-fpm

Now, it is possible to choose PHP version for each directory, from Apache httpd configuration file.

Example:

  # Use PHP 7.1 by default
  <FilesMatch \.php$>
    SetHandler "proxy:fcgi://127.0.0.1:9071"
  </FilesMatch>
  # Some legacy application use PHP 5.4
  <Directory /var/www/html/old>
    <FilesMatch \.php$>
      SetHandler "proxy:fcgi://127.0.0.1:9000"
    </FilesMatch>
  </Directory>

4. Conclusion

I hope this small article has shown the various benefits of switching to FPM for your PHP scripts:

  • process isolation between frontend (httpd) and backend (fpm)
  • performance improvement
  • running modern PHP version
  • running multiple PHP versions

 

Last updated: November 2, 2023