PHP (5.4)

From Hexwiki
Jump to: navigation, search

Packages and Extensions

Assuming you are using Debian, you will likely want the following for certain:

  • php-apc php-pear php5-cgi php5-mysqlnd php5-dev php5-fpm php5-dbg
    • if using dotdeb, remember to use php5-apc instead of php-apc

These may be desirable depending on what software you are using.

  • php5-curl php5-gd php5-geoip php5-enchant php5-imagick php5-imap php5-memcached php5-xmlrpc php5-pspell aspell-en php5-imap php5-mapscript spawn-fcgi

You can run 'spawn-fcgi' alongside fpm. In practice, chances are if you have a large enough site that merits its own fastcgi master process, you should probably make an fpm configuration for it specifically. I'm describing it for posterity, anyhow.

/etc/php5/cgi/php.ini

The following is a list of key settings to change from the default.

Security

  • Set engine = off
    • Prefer to be explicit. We should not under any circumstance be running under apache
  • short_open_tag = Off
    • Less relevant lately than it used to be. Short open tags and asp tags can cause issues with scanners looking for attempts to upload php code.
  • expose_php = Off
    • No reason for it to be on. Security, obscurity, and all that - there is no reason to give attackers help.
  • cgi.force_redirect = 1
    • Just like to be explicit with some things
  • cgi.fix_pathinfo = 0
    • Security. Otherwise php is perfectly happy to execute things that shouldn't be.
  • pcre.recursion_limit=100000
    • Not giving any specific suggestions for this, but be aware of ReDOS attacks when using regular expressions.
  • mail.add_x_header = Off
    • While I do try to move everything off of the mail function, just in case, I turn this off.
  • mysql.allow_local_infile = Off
    • No. Just no.
  • mysqli.allow_local_infile = Off
    • ---- this setting too.
  • session.cookie_httponly = 1
    • This not being the default ought to be reported as a security bug
  • url_rewriter.tags = ""
    • My hat of php sessions know no limit. A lot of assumptions that programmers often make about php sessions are not actually true.
  • open_basedir = /home:/tmp
    • Colon-delimited list of directories php programs are allowed to access.
  • upload_tmp_dir = /home/ptmp
  • soap.wsdl_cache_dir = /home/ptmp
    • If for some reason you don't want php playing around in /tmp, you can mount a temporary directory inside /home and use that.

Performance

  • realpath_cache_size = 256k
    • Another optimization.
  • Consider:
    • ignore_user_abort = On
    • Depending on what CMS you are running and its magnitude. It may be necessary for some programs that have long-running maintenance tasks which do not call the function.
  • max_execution_time = 300
  • max_input_time = 300
    • Generally just to permit longer-running tasks. Consider the software you are using.
  • memory_limit = 512M
    • PHP uses a ridiculous amount of memory for some things, though it has improved in this arena. This is only a limit - it's not how much it uses. Generally you do this for very large uploads.
  • variables_order = "EGPCS"
    • Some broken software (phpBB, I'm looking at you...) requires $_ENV
  • post_max_size = 32M
    • Or however large of uploads you wish to allow. Make sure max memory size is four times this.
  • upload_max_filesize = 32M
    • See above.
  • default_socket_timeout = 6
    • Don't waste people's time, if we have a problem.
  • date.timezone = America/New_York
  • date.timezone = America/Chicago
  • date.timezone = America/Los_Angeles
    • Some software complains if this is not set, so may as well. Obviously, pick whatever is appropriate for you.
  • mysqlnd.net_cmd_buffer_size = 4096
    • Some queries are going to be large, and there's little benefit to allocating less than a page.
  • mysqlnd.net_read_buffer_size = 262144
    • Good for any server hosting communities.

Development

  • error_reporting = E_ALL
    • Yay, all finally means all!
  • log_errors_max_len = 4096
    • Haven't had many cutoffs, but just in case
  • ignore_repeated_errors = On
    • This is just being sane.
  • track_errors = On
    • Even if you don't do development, I find having access to this useful for some of php's more borked functions.
  • html_errors = Off
    • We're not showing this crap to the user...
  • error_log = php_errors.log
    • We're going to be setting this per-user in their .user.ini anyhow
  • mysqlnd.collect_memory_statistics = On
    • Handy!

FPM

Best to make a script to automate adding sites to the common pool, in my opinion. The following helps facilitate this.

cd /etc/php5/fpm
mv php.ini php.ini.old
ln -sv ../cgi/php.ini php.ini   
mv pool.d/www.conf /root/fpmpool.conf

Or wherever you want to put your pool template.

fpmpool.conf

  • Pool name is USERNAME
    • Will be replaced via sed in our setup script
  • user = $pool
  • group = $pool
  • listen = /var/run/$pool-fpm.sock
  • listen.backlog = 2048
    • 128 is an insanely low limit. If you have an active site, you -will- hit this.
    • 2048 is just a placeholder, here, though I've rarely passed 256, even for a large forum.
  • listen.owner = $pool
  • listen.group = www-data
  • listen.mode = 0660
  • pm.max_children = 4
  • pm.start_servers = 2
  • pm.min_spare_servers = 2
  • pm.max_spare_servers = 4
    • Tune these as per necessary, but 2 is enough for most sites.
  • pm.max_requests = 20000
    • Don't set this below 10,000 - php's fastcgi implementation is extremely efficient, but restarting processes never is. In basic benchmarks, the original default (500) cut performance by more than half. That is, PHP was sometimes able to serve over 500 pages in less time than it takes to restart the process.
  • ping.path = /ping
    • Easy uptime monitoring.
  •  ;access.log = /home/$pool/logs/php-access.log
    • Recomment leaving this commented out save for testing, especially on a large site.
  • access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"
  • slowlog = /home/$pool/logs/php-slow.log
  • request_slowlog_timeout = 3s
    • Your mileage may vary, but at least on a server with SSDs, I get very few hits here, unless something is going wrong.
  • php_admin_value[error_log] = /home/$pool/logs/php-errors.log
  • php_admin_flag[log_errors] = on
    • Again, if you know your software has issues, you may wish to disable this on a large site.

spawn-fcgi

FPM is great and all, but unfortunately all pools run under the same process use the same shared memory. If you run a lot a communities, this can be a major problem, if you don't want to be arsed with setting up a unique config and spawn for each.

You'll need to edit the below init script as you add/remove sites from this. It has been rock-solid reliable for me, however, even for very large communities. FPM is quickly obsoleting it, but the shared memory issue may still be a dealbreaker for you. As mentioned, however, you can setup multiple master fpm processes instead, and generally only larger sites are going to need their own shared memory pools.

In my own case, I have largely been phasing this out.

chmod 755 /etc/init.d/spawn-php-fcgi 
insserv spawn-php-fcgi

/etc/init.d/spawn-php-fcgi

#! /bin/sh
### BEGIN INIT INFO
# Provides:          php-fastcgi
# Required-Start:    $remote_fs $network
# Required-Stop:     $remote_fs $network
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts the php fastcgi daemons
# Description:       starts a set of php daemons
### END INIT INFO
# In order to make sense of Apparmor, don't look at symlinks.
PHP_CGI=/usr/bin/php5-cgi
KPHP=php5-cgi
# Each of these is a space separated list, based on username
BIGUSERS=""
MIDUSERS=""
TINUSERS=""
umask 022
export PHP_FCGI_MAX_REQUESTS=20000
startbig () {
   echo -n "Starting Large PHP FastCGI Processes: \n"
   export CHILDREN=16
   for USER in $BIGUSERS
   do
       /usr/bin/env - /usr/bin/spawn-fcgi -u $USER -C $CHILDREN -s /var/run/$USER.fcgi.sock -- $PHP_CGI
       chgrp www-data /var/run/$USER.fcgi.sock
       chmod 660 /var/run/$USER.fcgi.sock
   done
}
startmid () {
   echo -n "Starting Midsized PHP FastCGI Processes: \n"
   export CHILDREN=4
   for USER in $MIDUSERS
   do
       /usr/bin/env - /usr/bin/spawn-fcgi -u $USER -C $CHILDREN -s /var/run/$USER.fcgi.sock -- $PHP_CGI
       chgrp www-data /var/run/$USER.fcgi.sock
       chmod 660 /var/run/$USER.fcgi.sock
   done
}
starttin () {
   echo -n "Starting Tiny PHP FastCGI Processes: \n"
   export CHILDREN=2
   for USER in $TINUSERS
   do
       /usr/bin/env - /usr/bin/spawn-fcgi -u $USER -C $CHILDREN -s /var/run/$USER.fcgi.sock -- $PHP_CGI
       chgrp www-data /var/run/$USER.fcgi.sock
       chmod 660 /var/run/$USER.fcgi.sock
   done
}
stopbig () {
   echo -n "Stopping Large PHP FastCGI Processes: \n"
   for USER in $BIGUSERS
   do
       /usr/bin/killall -u $USER -v -w -- $KPHP
   done
}
stopmid () {
   echo -n "Stopping Midsized PHP FastCGI Processes: \n"
   for USER in $MIDUSERS
   do
       /usr/bin/killall -u $USER -v -w -- $KPHP
   done
}
stoptin () {
   echo -n "Stopping Tiny PHP FastCGI Processes: \n"
   for USER in $TINUSERS
   do
       /usr/bin/killall -u $USER -v -w -- $KPHP
   done
}
case "$1" in
   start)
     startbig
     startmid
     starttin
 ;;
   stop)
     stopbig
     stopmid
     stoptin
 ;;
   restart|reload)
     stopbig
     startbig
     stopmid
     startmid
     stoptin
     starttin
 ;;
   *)
     echo "Usage: php-fastcgi {start|stop|restart}"
     exit 1
 ;;
esac
exit 0