Important!
My tech articles—especially Linux ones—are some of the most-viewed on The Z-Issue. If this one has helped you, please consider a small donation to The Parker Fund by using the top widget at the right. Thanks!Recently, I’ve spent a huge amount of time working on Apache and PHP-FPM in order to allow for a threaded Apache MPM whilst still using PHP and virtual hosts (vhosts). As this article is going to be rather lengthy, I’m going to split it up into sections below. It’s my hope that after reading the article, you’ll be able to take advantage of Apache’s newer Event MPM and mod_proxy_fcgi in order to get the best performance out of your web server.
1. Definitions:
Before delving into the overarching problem, it might be best to have some definitions in place. If you’re new to the whole idea of web servers, programming languages used for web sites, and such, these definitions may help you become more acquainted with the problem that this article addresses. If you’re familiar with things like Apache, PHP, threading, preforking, and so on, feel free to skip this basic section.
- Web server – on a physical server, the web server is an application that actually hands out (or serves) web pages to clients as they request them from their browser. Apache and nginx are two popular web servers in the Linux / UNIX world.
- Apache MPM – a multi-processing module (or MPM) is the pluggable mechanism by which Apache binds to network ports on a server, processes incoming requests for web sites, and creates children to handle each request.
- Prefork – the older Apache MPM that isn’t threaded, and deals with each request by creating a completely separate Apache process. It is needed for programming languages and libraries that don’t deal well with threading (like some PHP modules).
- Event – a newer Apache MPM that is threaded, which allows Apache to handle more requests at a time by passing off some of the work to threads, so that the master process can work on other things.
- Virtual host (vhost) – a method of deploying multiple websites on the same physical server via the same web server application. Basically, you could have site1.com and site2.com both on the same server, and even using the same IP address.
- PHP – PHP is arguably one of the most common programming languages used for website creation. Many web content management systems (like WordPress, Joomla!, and MediaWiki) are written in PHP.
- mod_php – An Apache module for processing PHP. With this module, the PHP interpreter is essentially embedded within the Apache application. It is typically used with the Prefork MPM.
- mod_proxy_fcgi – An Apache module that allows for passing off operations to a FastCGI processor (PHP can be interpreted this way via PHP-FPM).
- PHP-FPM – Stands for PHP FastCGI Process Manager, and is exactly what it sounds like: a manager for PHP that’s being interpreted via FastCGI. Unlike mod_php, this means that PHP is being interpreted by FastCGI, and not directly within the Apache application.
- UNIX socket – a mechanism that allows two processes within the same UNIX/Linux operating system to communicate with one another.
Defining threads versus processes in detail is beyond the scope of this article, but basically, a thread is much smaller and less resource-intense than a process. There are many benefits to using threads instead of full processes for smaller tasks.
2. Introduction:
Okay, so now that we have a few operational definitions in place, let’s get to the underlying problem that this article addresses. When running one or more busy websites on a server, it’s desirable to reduce the amount of processing power and memory that is needed to serve those sites. Doing so will, consequently, allow the sites to be served more efficiently, rapidly, and to more simultaneous clients. One great way to do that is to switch from the Prefork MPM to the new Event MPM in Apache 2.4. However, that requires getting rid of mod_php and switching to a PHP-FPM backend proxied via mod_proxy_fcgi. All that sounds fine and dandy, except that there have been several problems with it in the past (such as .htaccess files not being honoured, effectively passing the processed PHP file back to Apache, and making the whole system work with virtual hosts [vhosts]). Later, I will show you a method that addresses these problems, but beforehand, it might help to see some diagrams that outline these two different MPMs and their respective connections to PHP process.
The de facto way to use PHP and Apache has been with the embedded PHP processor via mod_php:
Apache with the Prefork MPM and embedded mod_php
Click to enlarge
This new method involves proxying the PHP processing to PHP-FPM:
Apache with the Event MPM offloading to PHP-FPM via mod_proxy_fcgi
Click to enlarge
3. The Setup:
On the majority of my servers, I use Gentoo Linux, and as such, the exact file locations or methods for each task may be different on your server(s). Despite the distro-specific differences, though, the overall configuration should be mostly the same.
3a. OS and Linux kernel:
Before jumping into the package rebuilds needed to swap Apache to the Event MPM and to use PHP-FPM, a few OS/kernel-related items should be confirmed or modified: 1) epoll support, 2) the maximum number of open file descriptors, and 3) the number of backlogged sockets.
Epoll support
The Apache Event MPM and PHP-FPM can use several different event mechanisms, but on a Linux system, it is advisable to use epoll. Even though epoll has been the default since the 2.6 branch of the Linux kernel, you should still confirm that it is available on your system. That can be done with two commands (one for kernel support, and one for glibc support):
Kernel support for epoll (replace /usr/src/linux/.config with the actual location of your kernel config):
# grep -i epoll /usr/src/linux/.config
CONFIG_EPOLL=y
glibc support for epoll
# nm -D /lib/libc.so.6 | grep -i epoll
00000000000e7dc0 T epoll_create
00000000000e7df0 T epoll_create1
00000000000e7e20 T epoll_ctl
00000000000e7a40 T epoll_pwait
00000000000e7e50 T epoll_wait
Max open file descriptors
Another aspect you will need to tune based on your specific needs is the maximum number of open file descriptors. Basically, since there will be files opened for the Apache connections, the UNIX socket to PHP-FPM, and for the PHP-FPM processes themselves, it is a good idea to have a high maximum number of open files. You can check the current kernel-imposed limit by using the following command:
# cat /proc/sys/fs/file-max
3291373
Though that number is usually very high, you also need to check the limits based on user. For sake of ease, and seeing as servers running many vhosts may frequently add and modify user accounts, I generally set the max file descriptors pretty high for *all* users.
# grep nofile /etc/security/limits.conf
# - nofile - max number of open file descriptors
* soft nofile 16384
* hard nofile 16384
The specifics of modifying the limits.conf
file are outside of the scope of this article, but the above changes will set the soft and hard limits to 16,384 open files for any user on the system.
Backlogged socket connections
The last OS configuration that you might want to initially configure is the maximum number of backlogged socket connections. This number is important because we will be proxying Apache to PHP-FPM via UNIX sockets, and this value sets the maximum number of connections per socket. It should be noted that this is the maximum number per socket, so setting this kernel parameter to some outlandish value doesn’t really make a lot of sense. As you will see later in this tutorial, I create a socket for each virtual host, so unless you plan on having more than 1024 simultaneous connections to PHP-FPM per site, this value is sufficient. You can change it permanently in /etc/sysctl.conf
:
# grep somaxconn /etc/sysctl.conf
net.core.somaxconn = 1024
and remember to run sysctl -p
thereafter in order to make the changes active.
3b. Package rebuilds:
Now that you’ve checked (and possibly modified) some OS-related configurations, a few packages needed to be recompiled in order to change from the Apache Prefork MPM to the Event MPM and to use PHP-FPM. Here’s a list of the changes that I needed to make:
- In
/etc/portage/make.conf
- Change the MPM to read
APACHE2_MPMS="event"
- Ensure you have at least the following
APACHE2_MODULES
enabled:cgid proxy proxy_fcgi
- Per the mod_cgid documentation you should enable
mod_cgid
and disablemod_cgi
when using the Event MPM - In
/etc/portage/package.use
- Apache:
www-servers/apache threads
- PHP must include at least:
dev-lang/php cgi fpm threads
- Note that you do not need to have the ‘apache2’ USE flag enabled for PHP since you’re not using Apache’s PHP module
- However, you will still need to have the MIME types and the DirectoryIndex directives set (see subsection 3d below)
- Eselect for PHP must have:
app-eselect/eselect-php fpm
Now that those changes have been made, and you’ve recompiled the appropriate packages (Apache, and PHP), it’s time to start configuring the packages before reloading them. In Gentoo, these changes won’t take effect until you restart the running instances. For me, that meant that I could compile everything ahead of time, and then make the configuration changes listed in the remaining 3.x subsections before restarting. That procedure allowed for very little downtime!
3c. Apache:
Within the configurations for the main Apache application, there are several changes that need to be made due to switching to a threaded version with the Event MPM. The first three bullet points for httpd.conf
listed below relate to the proxy modules, and the fourth bullet point relates to the directives specific to the Event MPM. For more information about the MPM directives, see Apache’s documentation.
- In
/etc/apache2/httpd.conf
- The proxy and proxy_fcgi modules need to be loaded:
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so - Comment out
LoadModule cgi_module modules/mod_cgi.so
so it does not get loaded - Replace that with the one for mod_cgid:
LoadModule cgid_module modules/mod_cgid.so
- Update the MPM portion for Event:
<IfModule event.c>
StartServers 2
ServerLimit 16
MinSpareThreads 75
MaxSpareThreads 250
ThreadsPerChild 25
MaxRequestWorkers 400
MaxConnectionsPerChild 10000
</IfModule>
You will also need to make sure that Apache loads certain modules at runtime. In Gentoo, that configuration is done in /etc/conf.d/apache
, and needs to contain at least the two modules listed below (note that your configuration will likely have other modules loaded as well):
- In
/etc/conf.d/apache
APACHE2_OPTS="-D PHP5 -D PROXY"
3d. Apache PHP module:
The last change that needs to be made for Apache is to the configuration file for how it handles PHP processing (in Gentoo, this configuration is found in /etc/apache2/modules.d/70_mod_php5.conf
. I would suggest making a backup of the existing file, and making changes to a copy so that it can be easily reverted, if necessary.
70_mod_php5.conf BEFORE changes
<IfDefine PHP5>
<IfModule !mod_php5.c>
LoadModule php5_module modules/libphp5.so
</IfModule>
<FilesMatch "\.(php|php5|phtml)$">
SetHandler application/x-httpd-php
</FilesMatch>
<FilesMatch "\.phps$">
SetHandler application/x-httpd-php-source
</FilesMatch>
DirectoryIndex index.php index.phtml
</IfDefine>
70_mod_php5.conf AFTER changes
<IfDefine PHP5>
## Define FilesMatch in each vhost
<IfModule mod_mime.c>
AddHandler application/x-httpd-php .php .php5 .phtml
AddHandler application/x-httpd-php-source .phps
</IfModule>
DirectoryIndex index.php index.phtml
</IfDefine>
Basically, you’re getting rid of the LoadModule
reference for mod_php, and all the FileMatch
directives. Now, if you’re not using virtual hosts, you can put the FilesMatch
and Proxy
directives from the vhosts section (3e) below in your main PHP module configuration. That being said, the point of this article is to set up everything for use with vhosts, and as such, the module should look like the example immediately above.
As mentioned in section 3b above, the reason you still need the 70_mod_php5.conf
at all is so that you can definite the MIME types for PHP and the DirectoryIndex. If you would rather not have this file present, (since you’re not really using mod_php), you can just place these directives (the AddHandler
and DirectoryIndex
ones) in your main Apache configuration—your choice.
3e. vhosts:
For each vhost, I would recommend creating a user and corresponding group. How you manage local users and groups on your server is beyond the scope of this document. For this example, though, we’re going to have site1.com and site2.com (very creative, I know ๐ ). To keep it simple, the corresponding users will be ‘site1’ and ‘site2’, respectively. For each site, you will have a separate vhost configuration (and again, how you manage your vhosts is beyond the scope of this document). Within each vhost, the big change that you need to make is to add the FilesMatch
directive. Here’s the generic template for the vhost configuration additions:
Generic template for vhost configs
<FilesMatch "\.php$">
SetHandler "proxy:unix:///var/run/php-fpm/$pool.sock|fcgi://$pool/"
</FilesMatch>
In that template, you will replace $pool with the name of each PHP-FPM pool, which will be explained in the next subsection (3f). In my opinion, it is easiest to keep the name of the pool the same as the user assigned to each site, but you’re free to change those naming conventions so that they make sense to you. Based on my site1.com / site2.com example, the vhost configuration additions would be:
site1.com vhost config
<FilesMatch "\.php$">
SetHandler "proxy:unix:///var/run/php-fpm/site1.sock|fcgi://site1/"
</FilesMatch>
site2.com vhost config
<FilesMatch "\.php$">
SetHandler "proxy:unix:///var/run/php-fpm/site2.sock|fcgi://site2/"
</FilesMatch>
3f. PHP-FPM:
Since you re-compiled PHP with FPM support (in step 3b), you need to actually start the new process with /etc/init.d/php-fpm start
and add it to the default runlevel so that it starts automatically when the server boots (note that this command varies based on your init system):
rc-update add php-fpm default
(Gentoo with OpenRC)
Now it’s time to actually configure PHP-FPM via the php-fpm.conf
file, which, in Gentoo, is located at /etc/php/fpm-php$VERSION/php-fpm.conf
. There are two sections to this configuration file: 1) global directives, which apply to all PHP-FPM instances, and 2) pool directives, which can be changed for each pool (e.g. each vhost). For various non-mandatory options, please see the full list of PHP-FPM directives.
At the top of php-fpm.conf
are the global directives, which are:
[global]
error_log = /var/log/php-fpm.log
events.mechanism = epoll
emergency_restart_threshold = 0
These directives should be fairly self-explanatory, but here’s a quick summary:
- [global] – The intro block stating that the following directives are global and not pool-specific
- error_log – The file to which PHP-FPM will log errors and other notices
- events.mechanism – What should PHP-FPM use to process events (relates to the epoll portion in subsection 3a)
- emergency_restart_threshold – How many PHP-FPM children must die improperly before it automatically restarts (0 means the threshold is disabled)
Thereafter are the pool directives, for which I follow this template, with one pool for each vhost:
;; $SITE.$TLD
[$USER]
listen = /var/run/php-fpm/$USER.sock
listen.owner = $USER
listen.group = apache
listen.mode = 0660
user = $USER
group = apache
pm = dynamic
pm.start_servers = 3
pm.max_children = 100
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.max_requests = 10000
request_terminate_timeout = 300
It may look a bit intimidating at first, but only the $SITE, $TLD, and $USER variables change based on your vhosts and corresponding users. When the template is used for the site1.com / site2.com example, the pools look like:
;; site1.com
[site1]
listen = /var/run/php-fpm/site1.sock
listen.owner = site1
listen.group = apache
listen.mode = 0660
user = site1
group = apache
pm = dynamic
pm.start_servers = 3
pm.max_children = 100
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.max_requests = 10000
request_terminate_timeout = 300
;; site2.com
[site2]
listen = /var/run/php-fpm/site2.sock
listen.owner = site2
listen.group = apache
listen.mode = 0660
user = site2
group = apache
pm = dynamic
pm.start_servers = 3
pm.max_children = 100
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.max_requests = 10000
request_terminate_timeout = 300
Since the UNIX sockets are created in /var/run/php-fpm/
, you need to make sure that that directory exists and is owned by root. The listen.owner
and listen.group
directives will make the actual UNIX socket for the pool owned by $USER:apache, and will make sure that the apache group can write to it (which is necessary).
The following directives are ones that you will want to change based on site traffic and server resources:
- pm.start_servers – The number of PHP-FPM children that should be spawned automatically
- pm.max_children – The maximum number of children allowed (connection limit)
- pm.min_spare_servers – The minimum number of spare idle PHP-FPM servers to have available
- pm.max_spare_servers – The maximum number of spare idle PHP-FPM servers to have available
- pm.max_requests – Maximum number of requests each child should handle before re-spawning
- pm.request_terminate_timeout – Maximum amount of time to process a request (similar to max_execution_time in
php.ini
These directives should all be adjusted based on the needs of each site. For instance, a really busy site with many, many, simultaneous PHP connections may need 3 servers each with 100 children, whilst a low-traffic site may only only need 1 server with 10 children. The thing to remember is that those children are only active whilst actually processing PHP, which could be a very short amount of time (possibly measured in milliseconds). Contrapositively, setting the numbers too high for the server to handle (in terms of available memory and processor) will result in poor performance when sites are under load. As with anything, tuning the pool requirements will take time and analysis to get it right.
Remember to reload (or restart) PHP-FPM after making any changes to its configuration (global or pool-based):
/etc/init.d/php-fpm reload
4. Verification:
After you have configured Apache, your vhosts, PHP, PHP-FPM, and the other components mentioned throughout this article, you will need to restart them all (just for sake of cleanness). To verify that things are as they should be, you can simply browse to one of the sites configured in your vhosts and make sure that it functions as intended. You can also verify some of the individual components with the following commands:
Apache supports threads, and is using the Event MPM
# apache2 -V | grep 'Server MPM\|threaded'
Server MPM: event
threaded: yes (fixed thread count)
Apache / vhost syntax
# apache2ctl configtest
* Checking apache2 configuration ... [ ok ]
5. Troubleshooting:
So you followed the directions here, and it didn’t go flawlessly?! Hark! ๐ฎ In all seriousness, there are several potential points of failure for setting up Apache to communicate with PHP-FPM via mod_proxy_fcgi, but fortunately, there are some methods for troubleshooting (one of the beauties of Linux and other UNIX derivatives is logging).
By default, mod_proxy and mod_proxy_fcgi will report errors to the log that you have specified via the ErrorLog directive, either in your overall Apache configuration, or within each vhost. Those logs serve as a good starting point for tracking down problems. For instance, before I had appropriately set the UNIX socket permission options in php-fpm.conf
, I noticed these errors:
[Wed Jul 08 00:03:26.717538 2015] [proxy:error] [pid 2582:tid 140159555143424] (13)Permission denied: AH02454: FCGI: attempt to connect to Unix domain socket /var/run/php-fpm/site1.sock (*) failed
[Wed Jul 08 00:03:26.717548 2015] [proxy_fcgi:error] [pid 2582:tid 140159555143424] [client 52350] AH01079: failed to make connection to backend: httpd-UDS
That error in bold text indicated that Apache didn’t have permission to write to the UNIX socket. Setting the permissions accordingly—see the listen.owner
and listen.group
directives portion of subsection 3f—fixed the problem.
Say that the errors don’t provide detailed enough information, though. You can set the LogLevel directive (again, either in your overall Apache configuration, or per vhost) to substantially increase the verbosity—to debug or even trace levels. You can even change the log level just for certain modules, so that you don’t get bogged down with information that is irrelevant to your particular error. For instance:
LogLevel warn proxy:debug proxy_fcgi:debug
will set the overall LogLevel to “warn,” but the LogLevel for mod_proxy and mod_proxy_fcgi to “debug,” in order to make them more verbose.
6. Conclusion:
If you’ve stuck with me throughout this gigantic post, you’re either really interested in systems engineering and the web stack, or you may have some borderline masochistic tendencies. ๐ I hope that you have found the article helpful in setting up PHP to proxy PHP interpretation to PHP-FPM via mod_proxy_fcgi, and that you’re able to see the benefits of this type of offloading. Not only does this method of processing PHP files free up resources so that Apache can serve more simultaneous connections with fewer resources, but it also more closely adheres to the UNIX philosophy of having applications perform only one task, and do so in the best way possible.
If you have any questions, comments, concerns, or suggestions, please feel free to leave a comment.
Cheers,
Zach
32 comments
Skip to comment form
Hi Zach,
Please forgive me, I don’t truly understand most of this, I’m just barely smart enough to get Apache working for my own Nextcloud instance on Gentoo. I’m forced to switch to using mod_proxy_fcgi because when I went to upgrade my Apache server I found I can no longer use HTTP2 with the prefork MPMS.
I’m stuck on connecting to the socket. I don’t have different users for different vhosts, the only thing I have set up is Nextcloud (and then some proxypass lines for various services like Matrix, Plex, etc). I set the listen.owner, listen.group, user, and group to “apache” in the php-fpm.conf file. Is this correct or no?
Also, I use systemd as my init system and I do not see a php-fpm service to start. “systemctl –all | grep php” is not returning any results. Does systemd take care of this on its own or am I supposed to be creating my own .service file for this? That would be way over my head.
Author
Hello,
First of all, you don’t give yourself enough credit! These things are definitely complicated and intricate. Second of all, I’d be glad to help, but would really need to see your setup in order to troubleshoot. If you would like to have me as a consultant for your exact situation, please reply here with your preferred email address (I’ll make sure it’s not public), and we can discuss details.
Cheers,
Zach
Great Tutuorial!
My bad I’m using ubuntu and being a newbie have no clue how to get it work there. I wish I could find a tutorial as good as this for ubuntu.
What I plan to do is setup apache 2.4 with php-fpm and mod_proxy_fcgi to use with event mpm. Will be hosting multiple wordpress sites in seperate vhosts.
Please let me know if you know of such articles.
Thanks
Atiqul
Author
Hello Atiqul,
I don’t now of any Ubuntu-specific tutorials. However, the process should be essentially the same (regardless of distribution), just with potentially different files to edit. If you would like me to serve as a consultant for your project, please let me know and we can discuss accordingly.
Cheers,
Zach
Hi Zach,
Sorry to bother you but I inherited a web server setup Apache+php-fpm that uses your setup and I cannot figure out how to enable vhosts to load on secure http with certificates. Your feedback is much appreciated.
Sam
Author
Hello Sam,
If you are wanting to use HTTPS (with valid security certificates), then you need separate IPs for each vhost as SSL terminates beforehand. Though the following post applies to IIS, it is the same for Apache:
https://blogs.iis.net/thomad/ssl-certificates-on-sites-with-host-headers
Hope that helps.
Cheers,
Zach
Thanks much for the post, but I did want to suggest one potentially important concern.
As with other people, I experienced an issue where the website would work fine until I did an HTTP POST. That would consistently trigger Apache to start doing all sorts of wacky stuff and returning the wrong responses for the URLs and/or a white screen with no errors. For example, I’d randomly see CSS in the 200 response and then I’d see HTML from a page I visited 10 minutes earlier, and then I’d see the correct HTML. It was all very strange.
I removed the Proxy line with enablereuse=on and restarted PHP-FPM and Apache the issue immediately went away. If I turned reuse back on I could consistently trigger the bad behavior again.
One of the mod_h2 developers explains a similar issue triggered by a file upload.
He also points out that the Apache documentation explicitly says “UDS does not support connection reuse”.
I’m using Apache version 2.4.23. Unless there’s a more recent workaround, it seems using PHP-FPM + unix domain sockets + mod_fcgi + enablereuse=on is not supported and can lead to terribly unstable results.
As an aside, it seems Nginx users also ran into a similar FPM socket and connection reuse issue which required disabling Nginx’s fastcgi_keep_conn
Author
Hello,
You’re completely right regarding the
enablereuse=on
portion in the Proxy. I was planning on updating the post but just got bogged down with other projects, so thank you for the reminder. I have updated it based on the findings that UDS is not compatible with enablereuse and max.Cheers,
Zach
I did wonder if the should stay, just without the enablereuse. It seems to work fine either way, but I ask because the documentation says You can also force a request to be handled as a reverse-proxy request, by creating a suitable Handler pass-through” and For performance reasons, you will want to define a worker representing the same fcgi:// backend” .
So, would there be a benefit to a defined prox worker, even without the reuse turned on?
Also, I saw someone using retry=0 in the Proxy and wondered if there’s any value in changing the mod_proxy retry that from the default of 60 seconds.
Author
Personally, I don’t see a reason to keep the
Proxy
directive if it is empty. If you do need to customise the Proxy, then yes, keep it there with your other modifiers. Otherwise, I don’t see a point. I haven’t seen any evidence to the contrary, but if I do, I will certainly test it out.Also, I’m planning on writing up a new article addressing the UDS and
enablereuse=on
conflict so that it is more readily found when searching (based on the error messages from Apache and mod_proxy. I’ll add an update link here once it is ready. Thanks again for reminding me. ๐Cheers,
Zach
Hi everybody,
I am really interested in knowing how to reproduce the weird behavior described by allella (“..That would consistently trigger Apache to start doing all sorts of wacky stuff ..) since as far as the Apache httpd development community is aware there is nothing that prevents mod_proxy_fcgi to reuse UDS with enablereuse=on.
During the past days the documentation was updated to reflect this (removing the “UDS does not support connection reuseโ comment) so it would be really great to know if the issue is on httpd or elsewhere.
Thanks in advance!
Luca
Hi Zach,
Great article. We have similar setup with Apache VHost setting like this:
SetHandler “proxy:fcgi://127.0.0.1:9093”
SetHandler “proxy:fcgi://127.0.0.1:9093”
My challenge is that I can’t seem to get the PHP-FPM status page to work. Inside the fpm.conf settings I’ve already enabled pm.status_path like this:
pm.status_path = /fpm-status
There’s no easy way to debug what is wrong but all I get is a 404 page (php-fpm, httpd have all been restarted multiple times. Any help is appreciated!
Author
Hi Howard,
Thanks for your question. If your setup is like mine, and you have a similar code block in your Apache vhost configuration (or single site configuration):
## See /etc/php/fpm-php$VERSION/php-fpm.conf for pool configurations
<FilesMatch "\.php$">
SetHandler "proxy:unix:///var/run/php-fpm/site.sock|fcgi://site/"
</FilesMatch>
<Proxy fcgi://site/ enablereuse=on max=10>
</Proxy>
then you must have the
.php
extension in thepm.status_path
. For example:pm.status_path = /fpm-status.php
The extension is how the
FilesMatch
knows to pass the file to PHP-FPM, so without the extension, the page won’t exist.As with any status page, if your site is accessible from the world, you’ll want to protect it by limiting to certain IPs, or using
BasicAuth
, et cetera so that unauthorised people can’t access it.Hope that helps, but please let me know if it doesn’t, and we’ll troubleshoot further. ๐
Cheers,
Zach
Wow! Thanks for this Article! It Works Perfectly ๐
Author
You’re welcome, Marco. I’m glad that it worked out well for you. ๐
Cheers,
Zach
Hi Zach,
I have problem with LDAP connect through Apache 2.4.12, Mod_proxy_fcgi, PHP-FPM y PHP 5.5.12. Otherwise, if I use mod_php with Apache 2.4.12 and PHP 5.5.12 its work. Only LDAP connect fail, the other functions PHP works. Is it posible thats may be neccesary some PHP-FPM Module or other PHP module? any suggestions?
PHP-FPM config:
listen = /opt/php-5.5.12/var/run/php-fpm/www.sock
Virtual-Host config:
# PHP-FPM Server - PHP 5.5.12
SetHandler "proxy:unix:/opt/php-5.5.12/var/run/php-fpm/www.sock|fcgi://site1/"
Thanks and congratulations for fantastic article!
Author
Hi Alexei,
Glad that you found the article useful. For LDAP connections, there shouldn’t be any problems with PHP-FPM as it has been used with nginx for a long time. Apache shouldn’t really be any different. Can you provide a little bit more information? Are you having trouble with Apache’s mod_ldap or with the PHP function of ldap_connect? What errors are you seeing?
Cheers,
Zach
Hi Zack,
After many days and hours, workaround…Yesterday…”Eureka!!” ;-), remember some time ago, I compiled PHP with Oracle LDAP Library (don’t ask me why). This library work correctly with MOD_PHP but not work trough PHP-FPM. So, I compiled PHP with OpenLDAP LDAP Library and work.
Thanks for all.
Author
Glad that you were able to get it figured out, and thank you for sharing the solution!
Cheers,
Zach
Zach – truly an excellent article and a ton of work to put together I’m sure. Thank you!
I have 2 quick dimwit questions ๐
1. As I understand, a side benefit of migrating to this model is that event MPM allows Apache to better deal with SlowLoris:
https://en.wikipedia.org/wiki/Slowloris_%28computer_security%29
Do you have any thoughts on this possibly?
2. I selected and have stuck with Apache prefork till now because of this:
http://php.net/manual/en/faq.installation.php#faq.installation.apache2
But re-reading the content now, I see this at the bottom:
“If you want to use a threaded MPM, look at a FastCGI configuration where PHP is running in its own memory space. ”
This is the first time I have been exposed to the technique you explain above to above and would really appreciate if you could confirm whether this technique satisfies the above criteria?
Thanks again for a fantastic article!
Author
Hello Pancho,
Thanks for your kind words, and I’m glad that you liked the article. To answer your questions: 1) yes, that is a benefit of using the Event MPM, but please know that it doesn’t solve the Slowloris problem with Apache. It helps, but doesn’t completely mitigate the possibility of that DoS attack. 2) Yes, using php-fpm is allowing PHP to run in its own memory space, so you don’t really have to worry about threading. I have been using this setup for quite some time now, and not run into any problems with PHP applications. Could some problems exist with older, poorly-coded frameworks or platforms? Sure, but I would highly doubt it would be due to the threading problem you referenced. Passing the PHP code execution to PHP-FPM for processing should eliminate that risk.
Your questions were not “dimwit” at all, and I hope that I was able to answer them appropriately for you.
Thanks for reading, and again, glad that the article helped.
Cheers,
Zach
Thanks so much Zach. To be truthful, I stumbled across your article in my efforts to find out more about possibilities to prevent SlowLoris against Apache 2.4. It started because we are preparing for a penetration test and as part of the prep I ran https://freescan.qualys.com – which said “Your Apache 2.x is exposed to SlowLoris”.
So we applied RequestReadTimeout, retried the test …and failed again. I then tried to install mod_qos, only to be told on apache startup “this doesn’t work with prefork – sorry” ๐ …so started looking at other options such as event MPM. Which is when I came across your article. While I’m sorry to hear that it won’t prevent SlowLoris so I remain effectively “up the creek…” at this point and am really not sure how we’re going to pass, I’m extremely pleased to have come across your article, and as soon as this test is behind us, I’m going to be looking at giving the approach some serious field trials.
Thanks again and keep the great articles coming!
Pancho
Author
Hello again Pancho,
I wouldn’t say that you’re “up the creek.” Using the Event MPM and proxying to PHP-FPM will definitely help mitigate your problem, and I think that it’s worth a shot trying the Qualys penetration test again thereafter. I generally think that those types of tests are good starting points, but overly generic and vague. The provide helpful insights, but shouldn’t be considered the “be all, end all” in terms of security or scrutiny.
If I can be of any further assistance, let me know. ๐
Cheers,
Zach
Next question, security ๐ How to get this working with the chroot directive of FPM? Wouldn’t that be the icing on the cake?
Author
Personally, I don’t see much benefit in running it inside of a chroot, but if you wanted to, I don’t know why you couldn’t just add:
prefix = /your/chroot/directory
chroot = $prefix
to your
php-fpm.conf
Cheers,
Nathan Zachary
That’s a very explanatory and well written article for us Gentoo users. Thank you!
Have you done any benchmarks between the old and the new setup? Would it pay to make the jump to Apache 2.4, PHP fpm, etc.?
Author
Hi Vasilis,
You’re very welcome; I’m glad that you found the article useful. As for benchmarks, I haven’t done any extensive tests, but overall sites feel more responsive (not very scientific, and completely anecdotal). Also, what I’ve noticed is that the difference in resource usage is MUCH more drastic on busy servers. For instance, I’ve seen resource utilisation drop 35-40% on very busy servers, whereas switching to this setup only saved ~3-4% of processor and memory on less active servers. That being said, I’m very pleased with the switch, and would recommend it.
Hope that helps.
Cheers,
Zach
It appears https://ma.ttias.be/apache-2-4-proxypass-for-php-taking-precedence-over-filesfilesmatch-in-htaccess/ has the answer. ProxyPassMatch has several disadvantages to it compared to the FilesMatch/SetHandler approach.
Thanks for the writeup, I converted over to PHP-FPM.
But for vhost configuration, what’s the downside of following https://wiki.apache.org/httpd/PHP-FPM vs yours
ProxyPassMatch ^/(.*\.php(/.*)?)$ unix:/path/to/socket.sock|fcgi://127.0.0.1:9000/path/to/your/documentroot/
I went with the shorter version of Apache wiki and everything seems to work nicely.
Author
Hi Leho,
The biggest reason is that the
ProxyPassMatch
won’t honour.htaccess
restrictions like theSetHandler
approach will.Cheers,
Zach
Hi Zach,
I’m using SetHandler approach with PS 1.7.6.2 and got an unstable website – sometimes having errors like a “[proxy_fcgi:error] [pid 4858:tid 34382047232] [client 192.168.2.249:50863] AH01071: Got error ‘PHP message: PHP Fatal error: require(): Failed opening required ‘/usr/local/www/apache24/data/var/cache/dev/Container49j2qtk getRouting_LoaderService.php’ (include_path=’/usr……………………………..” But it’s enough to hit F5 in th browser to finish request successfuly, what can be the cause?
Httpd.conf contains
SetHandler proxy:fcgi://127.0.0.1:9000
Author
Hello,
Based on the error message that you provided (and the path that it references), it looks like it is probably a caching conflict with PHP-FPM. You will likely need to disable that caching within the application.
Cheers,
Zach