PHP-FPM on Apache with split configuration per site
While configuring this very server I kept in mind that I would want to host multiple sites on it. I wanted to be able to configure PHP settings on a per site basis instead of modifying the global PHP configuration for the entire server. I also wanted to make sure that every site/user had its own permissions and thus wasn’t able to screw things up on my server in case of a successful break-in attempt. And of course I also kept the performance of my server in mind.
Therefor I chose PHP-FPM (over mod_php) which I configured with an application pool per site that has its own configuration file and handler. Just follow the next steps if you would like to duplicate my configuration.
Installation
First of all we install the required packages
~$ apt-get install apache2-mpm-worker libapache2-mod-fastcgi php5-fpm |
and enable the needed apache modules.
~$ a2enmod actions alias fastcgi |
Configure PHP-FPM
Now we have to create the configuration file /etc/apache2/conf.d/php5-fpm with the following content:
# Configure everything needed to use PHP-FPM as FastCGI # Set handlers for PHP files. # application/x-httpd-php phtml pht php # application/x-httpd-php3 php3 # application/x-httpd-php4 php4 # application/x-httpd-php5 php <FilesMatch ".+\.ph(p[345]?|t|tml)$"> SetHandler application/x-httpd-php # application/x-httpd-php-source phps <FilesMatch ".+\.phps$"> SetHandler application/x-httpd-php-source # Deny access to raw php sources by default # To re-enable it is recommended to enable access to the files # only in specific virtual host or directory Order Deny,Allow Deny from all # Deny access to files without filename (e.g. '.php') <FilesMatch "^\.ph(p[345]?|t|tml|ps)$"> Order Deny,Allow Deny from all # Define Action and Alias needed for FastCGI external server. Action application/x-httpd-php /fcgi-bin/php5-fpm virtual Alias /fcgi-bin/php5-fpm /fcgi-bin-php5-fpm # here we prevent direct access to this Location url, # env=REDIRECT_STATUS will let us use this fcgi-bin url # only after an internal redirect (by Action upper) Order Deny,Allow Deny from All Allow from env=REDIRECT_STATUS FastCgiExternalServer /fcgi-bin-php5-fpm -socket /var/run/php5-fpm.sock -pass-header Authorization |
A handler will be set for every PHP file and for every handler a virtual Action is defined. Virtual means that there will be no check that the file exists.
The Alias was created because we will need this to run PHP-FPM as another user than the default www-data user.
Separate configuration per user
Let’s start by creating a new user and adding www-data into its group. Then create the home dir for this user, correct the permissions and link the new home dir to this user.
~$ adduser --disabled-login dimitri ~$ adduser www-data dimitri ~$ mkdir /var/www/dimitrieu ~$ chown -R dimitri:dimitri /var/www/dimitrieu ~$ chmod 750 /var/www/dimitrieu |
We can now create the PHP-FPM configuration file for this user. Just copy the default file /etc/php5/fpm/pool.d/www.conf to /etc/php5/fpm/pool.d/dimitri.conf and modify the following lines.
; Start a new pool named 'dimitri'. ; the variable $pool can we used in any directive and will be replaced by the ; pool name ('dimitri' here) [dimitri] ; Unix user/group of processes ; Note: The user is mandatory. If the group is not set, the default user group ; will be used. user = dimitrigroup = dimitri ; The address on which to accept FastCGI requests. ; Valid syntaxes are: ; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific address on ; a specific port; ; 'port' - to listen on a TCP socket to all addresses on a ; specific port; ; '/path/to/unix/socket' - to listen on a unix socket. ; Note: This value is mandatory. listen = /var/run/php5-fpm-dimitri.sock |
Now restart PHP-FPM
~$ /etc/init.d/php5-fpm restart |
and verify if the new PHP-FPM instances are running.
~$ ps -efH | grep php-fpm root 30258 30162 0 12:01 pts/0 00:00:00 grep php-fpm root 10409 1 0 Sep27 ? 00:00:22 php-fpm: master process (/etc/php5/fpm/php-fpm.conf) www-data 10415 10409 0 Sep27 ? 00:00:00 php-fpm: pool www www-data 10416 10409 0 Sep27 ? 00:00:00 php-fpm: pool www 1000 18944 10409 0 Oct07 ? 00:02:13 php-fpm: pool dimitri1000 18995 10409 0 Oct07 ? 00:02:03 php-fpm: pool dimitri1000 19142 10409 0 Oct07 ? 00:01:38 php-fpm: pool dimitri |
We can now modify our apache vhost for this site (/etc/apache2/sites-enabled/dimitrieu)
ServerAdmin webmaster@dimitri.eu ServerName dimitri.eu ServerAlias www.dimitri.eu DocumentRoot /var/www/dimitrieu/public_html Options Indexes FollowSymLinks MultiViews AllowOverride All Order allow,deny allow from all Alias /fcgi-bin/php5-fpm /fcgi-bin-php5-fpm-dimitri FastCgiExternalServer /fcgi-bin-php5-fpm-dimitri -socket /var/run/php5-fpm-dimitri.sock -pass-header Authorization ErrorLog /var/log/apache2/dimitrieu/error.log # Possible values include: debug, info, notice, warn, error, crit, # alert, emerg. LogLevel warn CustomLog /var/log/apache2/dimitrieu/access.log combined |
and restart apache.
~$ /etc/init.d/apache2 restart |
Small test
We’re done, just create a test.php script in the document root for this site to verify the correct PHP-FPM user. Be aware that the test.php script should have the correct ownership on the server.
<!--?php system('whoami'); phpinfo(); ?--> |
- Varnish config for WordPress
- How to purge the Varnish cache
Great Stuff Dimitry.
Does this work if i have Nginx as a revere proxy sitting in front of Apache?
I gueess it must work right?
This should indeed work as Nginx just sends the requests to Apache which will, in its turn, let PHP-FPM take care of the PHP pages through the configured handlers.
When an unexisting php file is called I correctly get a 404 and see “File not found” but the apache error.log is populated with error:
FastCGI: server “/fcgi-bin-php5-fpm” stderr: Primary script unknown
is this expected?
How can I have the default “File does not exist: file.php” error?
Hi, from what I can see I have these same messages in my apache logs and they seem to be “normal behaviour”.
I can find some workarounds for Nginx but not for Apache. I’ll look some further and update you when I find out more about this.
Can you please update the blog post to fix the code escaping?
it’s pretty difficult with things like:
<FilesMatch “^\.ph(p[345]?|t|tml|ps)$”>
wow, your site is actually escaping the html for me.
this smells like a huge security hole, if it’s not escaping user input.
I will resist sticking a javascript block with alert.
in any case, if you look at the actual post, you will see that the code is escaped there which makes it rather difficult to read.
Hi, thanks for the correction. This post has been updated with the changes.
Thanks for fixing it.
I ended up using something like:
ProxyPassMatch ^/(.*\.php(/.*)?)$ unix:/var/run/php5-fpm-omry.sock|fcgi://127.0.0.1/path_to_php_files/
Any thought about this approach?
Did you manage to get things to work with a chroot per user?
I am not sure what is the best way to setup a chroot that will be shared (read only) between users were each user also gets their own vhosts available.
If you want to chat about this please send me an email (I didn’t a notification on your response here).
I did everything of the above manual, but my .php nevertheless run as www-data instead of confirgured correct user.
# ps -efH | grep php-fpm
root 5248 4317 0 17:16 pts/1 00:00:00 grep php-fpm
root 4502 1 0 17:04 ? 00:00:00 php-fpm: master process (/etc/php5/fpm/php-fpm.conf)
theses 4504 4502 0 17:04 ? 00:00:00 php-fpm: pool theses
theses 4505 4502 0 17:04 ? 00:00:00 php-fpm: pool theses
www-data 4506 4502 0 17:04 ? 00:00:00 php-fpm: pool www
www-data 4507 4502 0 17:04 ? 00:00:00 php-fpm: pool www
So fpm part seems to work. It is probably wrong Apache config (despite I’ve checked everything).
# cat theses.conf
ServerName theses.portonvictor.org
SuexecUserGroup theses theses
ServerAdmin webmaster@localhost
DocumentRoot /var/www/theses/web
Options Indexes SymlinksIfOwnerMatch
AllowOverride All
Order allow,deny
allow from all
#ScriptAlias /cgi-bin/ /var/www/homepage/cgi-bin
#
# AllowOverride None
# Options +ExecCGI -MultiViews
# Order allow,deny
# Allow from all
#
ErrorLog ${APACHE_LOG_DIR}/error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/access.log combined
AddType application/x-httpd-fastphp5 .php
Action application/x-httpd-fastphp5 /php5-fcgi virtual
Alias /php5-fcgi /usr/lib/cgi-bin/php5-fcgi-theses
FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi-theses -socket /var/run/php5-fpm-theses.sock -pass-header Authorization
#
# Require all granted
#
What is wrong?!!
Note that `sudo a2dismod php5` makes 500 Internal Server Error when trying to load a .php file from my server.
Thus it looks like that FPM is not used in some reason. I wonder, why it is not used, it looks like that I configured everything correctly.
Could you check your main Apache errorlog because if Apache fails to start you should find something in your logs.
You may post the error if you don’t immediately find an answer.
Very nice post, I’ve been looking into this for quite a lot of time, at the end having the proper parts highlighted showed me the right track. Thanks a lot
My brother recommended I might like this web site.
He was entirely right. This post actually made my day.
You can not imagine simply how much time I had spent for this
information! Thanks!
Look at my website – Info,
Very helpful. Thank you.
I think you may have a small mistake:
Alias /fcgi-bin/php5-fpm /fcgi-bin-php5-fpm-dimitrieu
FastCgiExternalServer /fcgi-bin-php5-fpm-dimitri -socket /var/run/php5-fpm-dimitri.sock -pass-header Authorization
The last 2 chars ‘eu’ in the first line don’t make sense. Thanks again.
Hi, thanks for noticing this, I have it correct on my server but apparently I made a typo in this post.
I have modified this config.
Pingback: How to purge the Varnish cache | Dimitri.eu
Nice config. Seems more concise than others I’ve seen.
Just wanted to note while getting this to work on Apache 2.4., you just need to change the require directives to the new syntax (
Require all granted
orRequire all denied
). The REDIRECT_STATUS one can be changed toRequire env REDIRECT_STATUS
.Have you tried using the new mod_proxy_fcgi with 2.4?
Hi, thank you for the information, I didn’t install apache 2.4 so my answer would be no :). But this will be helpful for people visiting my site and even for me when I switch over to apache 2.4 in the future.