There are plenty of tutorials on the web. Tons and tons.
However when I had to create working configuration and put together Virtualmin template most of these instruction just didn’t work properly. After some time I’ve came up with my own working configuration and proper Virtualmin apache template for CentOS.
As a bonus we’ll have per-user php.ini and apache2 worker MPM which is a little bit faster and less resource hungry then traditional prefork MPM.
First, I turned for instructions to HowTo Forge, which is proven to be an excellent resource, but unfortunately, instructions they provided are a little bit incomplete. Below is my instructions collected from various sources and tested numerous times.
- Install rpms – httpd, php-common, mod_fcgid, additional php modules you might need.
- Open /etc/httpd/conf.d/php.conf in your favorite editor and comment out everything. As an alternative you can rename php.conf into php.conf.bak which will prevent apache from loading this config file.
- Here is my variant of /etc/httpd/conf.d/fcgid.conf – your can customize it as you see fit
12345678910111213141516171819202122232425LoadModule fcgid_module modules/mod_fcgid.so# Use FastCGI to process .fcg .fcgi & .fpl scripts# Don't do this if mod_fastcgi is present, as it will try to do the same thing<IfModule mod_fcgid.c>PHP_Fix_Pathinfo_Enable 1IPCCommTimeout 180MaxProcessCount 150DefaultMaxClassProcessCount 15DefaultMinClassProcessCount 5AddType application/x-httpd-php .phpAddHandler php-fcgi .phpAction php-fcgi /fcgi-bin/php-fcgiFCGIWrapper /var/www/cgi-bin/php-fcgi .phpAlias /fcgi-bin/ /var/www/cgi-bin/<Location /fcgi-bin/>SetHandler fcgid-scriptOptions +ExecCGI</Location>DirectoryIndex index.php# Sane place to put sockets and shared memory fileSocketPath run/mod_fcgidSharememPath run/mod_fcgid/fcgid_shm</IfModule> - Lets create example virtual host web1.com (manually for now – later on I will explain how to do it automatically with virtualmin). We have system user web1(503) in group web1(503). First we have to create proper fcgi wrapper for php – suExec executes scripts from /var/www/cgi-bin while usually user virtual hosts contents are located in /home.
# mkdir -p /var/www/cgi-bin/web1
# cat > /var/www/cgi-bin/web1/php-web1
#!/bin/sh
export PHPRC=/home/web1
export PHP_FCGI_MAX_REQUESTS=5000
export PHP_FCGI_CHILDREN=1
exec /usr/bin/php-cgi
^D
# chmod 755 /var/www/cgi-bin/web1/php-web1
# chown -R web1.web1 /var/www/cgi-bin/web1
We will also need to create default fcgi wrapper, specified in fcgid.conf.
# cat > /var/www/cgi-bin/php-fcgi
#!/bin/sh
export PHPRC=/etc
export PHP_FCGI_MAX_REQUESTS=5000
export PHP_FCGI_CHILDREN=1
exec /usr/bin/php-cgi
^D
# chmod 755 /var/www/cgi-bin/php-fcgi
This part is finished. - Now, apache virtual host config file in /etc/httpd/conf.d/web1.conf
1234567891011121314151617181920212223242526272829<VirtualHost xx.xx.xx.xx:80>ServerName web1.comServerAlias www.web1.comDocumentRoot /home/web1/public_htmlErrorLog /home/web1/logs/error_logCustomLog /home/web1/logs/access_log combinedScriptAlias /cgi-bin/ /home/web1/cgi-bin/DirectoryIndex index.html index.htm index.php index.php4 index.php5<IfModule mod_fcgid.c>Alias /fcgi-bin/ /var/www/cgi-bin/<Location /fcgi-bin/>SetHandler fcgid-scriptOptions +ExecCGI</Location>SuexecUserGroup "#503" "#503"<Directory /home/web1/public_html>Options -Indexes IncludesNOEXEC FollowSymLinks +ExecCGIAddHandler php-fcgi .phpAction php-fcgi /fcgi-bin/web1/php-web1FCGIWrapper /var/www/cgi-bin/web1/php-web1 .phpallow from allAllowOverride All</Directory></IfModule><Directory /home/web1/cgi-bin>Options ExecCGIAllow from all</Directory></VirtualHost> - Copy php.ini file into /home/web1.
# cp /etc/php.ini /home/web1/
# chown web1.web1 /home/web1/php.ini
Restart apache. In /home/web1/public_html create simple test script and chown it to web1.web1
1234567891011121314<?echo "I am running as user=";system("whoami");echo "<P>I can write here= ";system("pwd");$fname="write_test.txt";if (file_exists($fname)) {echo "<P> I found file $fname and will delete it";unlink($fname);echo "...Done";}echo "<P>I'm writing new file $fname";system ("touch $fname");?>
If you will see this
1234567I am running as user=web1I can write here= /home/web1/public_htmlI found file write_test.txt and will delete it...DoneI'm writing new file write_test.txt
Then virtual host is configured correctly - Finishing touches:
- On CentOS session information is stored in
/var/lib/php/session
, that is owned by apache.apache. Since suEXEC’ed PHP creates session files owned by user that owns the script, session dir permissions should be adjusted:
# chown 1777 /var/lib/php/session
- Since we are not running thread-unsafe mod_php now we can swtich apache to worker MPM:
# sed -i '/worker/s/^#//' /etc/sysconfig/httpd
# service httpd restart
- After switching to PHP FCGI mode, all “php_” options in .htaccess files will result in 500 server error, because without mod_php apache doesn’t understand them. Majority of this configuration options could be done via user php.ini file.
# find /home -name .htaccess -exec grep -nH php_ {} \;
- Switching PHP into CGI/FCGI mode breaks some php scripts that do basic http auth, phpMyAdmin in particular. This could be fixed with the followinng .htaccess file in phpMyAdmin directory
12RewriteEngine OnRewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization},L]
- On CentOS session information is stored in
- And last – Virtualmin apache virtual host template.
123456789101112131415161718192021222324252627ServerName ${DOM}ServerAlias *.${DOM}DocumentRoot ${HOME}/public_htmlErrorLog ${HOME}/logs/error_logCustomLog ${HOME}/logs/access_log combinedScriptAlias /cgi-bin/ ${HOME}/cgi-bin/<IfModule mod_fcgid.c>Alias /fcgi-bin/ /var/www/cgi-bin/<Location /fcgi-bin/>SetHandler fcgid-scriptOptions +ExecCGI</Location>DirectoryIndex index.html index.htm index.php index.php4 index.php5SuexecUserGroup "#${UID}" "#${GID}"<Directory ${HOME}/public_html>Options +ExecCGI -Indexes IncludesNOEXEC FollowSymLinksAllowOverride AllAddHandler php-fcgi .phpAction php-fcgi /fcgi-bin/${USER}/php-${USER}Order allow,denyAllow from all</Directory></IfModule><Directory ${HOME}/cgi-bin>Options ExecCGIAllow from all</Directory>
Note – Suexec option in virtualmin should be disabled.
Along with this template we need to add post-install custom script that will create fcgi wrapper and copy/chown php.ini into the user homedir.
12345678910111213141516171819202122232425#!/bin/bashcgi_path="/var/www/cgi-bin"# creating php-fcgi wrapper for apache vhostsif [ "$VIRTUALSERVER_ACTION" = "CREATE_DOMAIN" ]; thenif [ ! -d ${cgi_path}/${VIRTUALSERVER_USER} ]; thenmkdir ${cgi_path}/${VIRTUALSERVER_USER}ficd ${cgi_path}/${VIRTUALSERVER_USER}cat > php-${VIRTUALSERVER_USER} <<EOP#!/bin/shexport PHPRC=${VIRTUALSERVER_HOME}export PHP_FCGI_MAX_REQUESTS=5000export PHP_FCGI_CHILDREN=0exec /usr/bin/php-cgiEOPchmod 755 php-${VIRTUALSERVER_USER}chown ${VIRTUALSERVER_USER}.${VIRTUALSERVER_GROUP} -R .cp /etc/php.ini ${VIRTUALSERVER_HOME}/chown -R ${VIRTUALSERVER_USER}.${VIRTUALSERVER_GROUP} ${VIRTUALSERVER_HOME}elseexit 0fi
Copy this script into /usr/local/sbin/ and in Virtualmin add it to “script run after creation of virtual host” settings field.
Try to create test virtual host and verify that all structure was created properly and has proper permissions /ownership. In case of problems refer to server logs (/var/log/httpd/suexec.log could be very useful).
This is excellent! Thank you so very much!
Since I’ve switched off shared hosting to VPS, I’ve frustrated myself entirely over this issue. Everything I’ve seen always suggests your users are setup in /var/www/vhost(etc) – which I’ve despised. One little misstep and you could be in trouble. I never got my setup working 100% correctly with suPHP either, as it would mess up my “system” stuff like phpmyadmin and Zarafa (or fail spectacularly).
You are welcome. I’ve had my share of trial and error on this and finally got it nailed. There are still some nuances in this configuration.