Thursday, August 6, 2020

WordPress Security via Nginx


This tutorial goes over how to set up basic security rules for hosting a WordPress website behind an Nginx reverse proxy that also serves cached and static content.

One of the biggest advantages of Apache as a web server is its flexibility to implement custom-level access and security rules through its .htaccess file. For example, many security plugins make heavy use of .htaccess to implement their policies.

If you’re using Nginx to serve some content dynamically, or even to serve most content, it makes sense to have these rules implemented at the Nginx level, since Apache will not get to see a large proportion of requests.

We can create security rules in external files, which we can then call from within the Nginx server configuration files. This way we only need to update rules once, and the new rules apply to all sites you host. Let’s create a set of basic rules.

root@system:~# nano /etc/nginx/snippets/secrules.conf

This is just a set I’ve put together from other people have shared. I have modified it to include particular nuisance requests that hit some of my servers:

# secrules.conf, a snippet containing security rules for Nginx hosts

# Block request methods that are unnecessary for serving your content
if ($request_method !~ ^(GET|POST|HEAD)$ ) {
	return 444;
	}

# Block scripts from being executed from your uploads folder. They will be served as text.
# If you don't serve scripts at all, you could block them altogether instead.
location ~* ^/wp-content/uploads/.*.(php|pl|py|jsp|asp|htm|html|shtml|sh|cgi)$ {
	types { }
	default_type text/plain;
	}

# Block attempts to access PHPMyAdmin. If you actually use it, don't include this rule!
location ~* .(administrator|[pP]hp[mM]y[aA]dmin) {
	deny all;
	}

# Disallow common hacks
location ~* .(display_errors|set_time_limit|allow_url_include.*disable_functions.*open_basedir
	|set_magic_quotes_runtime|webconfig.txt.php|file_put_contentssever_root
	|wlwmanifest) {
		deny all;
		}

location ~* .(globals|encode|localhost|loopback|xmlrpc|revslider) {
	deny all;
	}

# Disallow access to sensitive files
## WARNING - The first rule \. interferes with access to the .well-known directory,
## and will disallow LetsEncrypt using webroot. In this case you may want to change
## it to \.ht
location ~ /(\.|wp-config.php|readme.html|license.txt|nginx.conf|wp-config-sample.php
    |readme.txt|dbconfig.php) {
		deny all;
		}

# Disallow scripts
location ~* \.(pl|cgi|py|sh|lua)$ { return 444; }

# Help guard against SQL injection
location ~* .(\;|'|\"|%22).*(request|insert|union|declare|drop)$ {
	deny all;
	}

# Disallow access to parts of wp-includes
# Many sites recommend blocking wp-includes altogether but in my experience this breaks WordPress
location ~* wp-admin/includes { deny all; }
location ~* wp-includes/theme-compat/ { deny all; }
location ~* wp-includes/js/tinymce/langs/.*.php { deny all; }

Full disclosure: This file is modified from content in lamosty.com and geekytuts.net, changing rules that haven’t worked for me in the past (or adding warnings), and including some others.

Now that we have our basic secrules, we can call it from within the Nginx server block. An example configuration following on from our previous tutorial could be like this:

server {
	listen 80;
	server_name example.com www.example.com;
	root /var/www/example.com/html;
	index index.php;

	include snippets/supercache.conf
	include snippets/secrules.conf

	location / {
		try_files $cachefile $uri $uri/ /index.php;
	}

	location ~ \.php$ {
        proxy_pass http://localhost:8080$request_uri;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

	location ~ /\. {
		deny all;
	}

}

Before doing anything else, test the new Nginx configurations:

root@system:~# nginx -t

If all goes well, reload Nginx and enjoy the warm and fuzzy feeling that your server is a little better protected from basic attacks. We can’t really be immune to geting hacked, just try not to be the low-hanging fruit.




Wednesday, July 22, 2020

PHP Parallel / Multithreading Example



Introduction

Parallel is a parallel concurrency extension for PHP 7.2+.

A brief description of the concepts core to parallel follows, more detailed information may be found within this section of the manual.

Runtime

A parallel\Runtime represents a PHP interpreter thread. A parallel\Runtime is configured with an optional bootstrap file passed to parallel\Runtime::__construct(), this is typically an autoloader, or some other preloading routine: The bootstrap file will be included before any task is executed.

After construction the parallel\Runtime remains available until it is closed, killed, or destroyed by the normal scoping rules of PHP objects. parallel\Runtime::run() allows the programmer to schedule tasks for execution in parallel. A parallel\Runtime has a FIFO schedule, tasks will be executed in the order that they are scheduled.

Function API

parallel implements a functional, higher level API on top of parallel\Runtime that provides a single function entry point to executing parallel code with automatic scheduling: parallel\run().

Task

A task is simply a Closure intended for parallel execution. The Closure may contain almost any instruction, including nested closures. However, there are some instructions that are prohibited in tasks:

  • yield
  • use by-reference
  • declare class
  • declare named function

** Note: Nested closures may yield or use by-reference, but must not contain class or named function declarations.

** Note: No instructions are prohibited in the files which the task may include.

Future

The parallel\Future is used to access the return value from the task, and exposes an API for cancellation of the task.

Channel

A task may be scheduled with arguments, use lexical scope variables (by-value), and return a value (via a parallel\Future), but these only allow uni-directional communication: They allow the programmer to send data into and retrieve data from a task, but do not allow bi-directional communication between tasks. The parallel\Channel API allows bi-directional communication between tasks, a parallel\Channel is a socket-like link between tasks that the programmer can use to send and receive data.

Events

The parallel\Events API implements a native feel (Traversable) event loop, and parallel\Events::poll() method. It allows the programmer to work with sets of channels and or futures. The programmer simply adds channels and futures to the event loop, optionally setting the input for writes with parallel\Events::setInput(), and enters into a foreach: parallel will read from and write to objects as they become available yielding parallel\Events\Event objects describing the operations that have occurred.

Requirements

  1. PHP 7.2+
  2. ZTS
  3. <pthread.h>

Installation

From sources :
git clone https://github.com/krakjoe/parallel.git
cd parallel
phpize
./configure --enable-parallel  [ --enable-parallel-coverage ] [ --enable-parallel-dev ]
make
make test
make install

From PECL :
pecl install parallel

Binary distributions :

Microsoft Windows: use zip archive from https://windows.php.net/downloads/pecl/releases/parallel/


Example

<?php
$runtime = new \parallel\Runtime;
$future  = $runtime->run(function(){
    return "World";
});
printf("Hello %s\n", $future->value());
?>

This may output something like (output abbreviated) :

Hello World



Reference :


Wednesday, April 22, 2020

PHP + PThreads (Posix Thread) Installation & Configuration Example


Installation is quite straightforward, although a little more involved than the Simple Windows Installation instructions suggest:
  1. Download a thread safe version of PHP, e.g. php-5.6.38-Win32-VC11-x86.
    • extract the .ZIP file to your hard drive, e.g. C:\php.
  2. Download the matching version of pthreads, e.g php_pthreads-2.0.10-5.6-ts-vc11-x86.
    • extract pthreadVC2.dll into the PHP folder, e.g. C:\php.
    • extract php_pthreads.dll into the extensions folder, e.g. C:\php\ext.
  3. In the PHP folder, copy either php.ini-development or php.ini-production to php.ini.
  4. Edit php.ini and add the line extension=php_pthreads.dll at the end of the file.
At this point pthreads should be working. You can verify this by opening a command prompt in the PHP folder and executing php --ri pthreads:
Screenshot of result
Note that the PHP and pthreads versions I referenced above require you to install the Visual Studio 2012 (VC11) runtime to function.

Edit: For completeness, I downloaded what seem to be the exact versions of PHP and pthreads you are using (the primary difference being x64):
Following the steps above I received the same result (except the pthreads version is reported as 2.0.9 of course).

Reference :

Tuesday, March 31, 2020

Docker + PHP Application Example


We can run php application using docker. In the following steps, we are creating and running php application.
  1. Create a directory
  2. Create a directory to organize files by using following command.
    1. mkdir php-docker-app  
    See, screen shot of the above command.
    Docker Php application 1

  3. Create a Php File
  4. index.php
    1. <?php  
    2.     echo ?Hello, Php?;  
    3. ?>  

  5. Create a DockerFile
  6. Dockefile
    1. FROM php:7.0-apache  
    2. COPY . /var/www/php  
    After that our project has two files like the below screen-shot.
    Docker Php application 2

  7. Create Docker Image
    1. $ docker build -t php-app .  
    In the below screen-shot, we are creating docker image.
    Docker Php application 3Now look for the available images in the docker container.
    Docker Php application 4The above screen-shot shows that the created image php-app is available.

  8. Run the Docker image
  9. Now run the docker image. The following command is used to run docker images.
    1. $ docker run php-app  
    Docker Php application 5We can see that our docker image is running and output is shown to the browser. This image is running on the 172.17.0.2 ip.

    Output:
    Docker Php application 6


Reference:


Saturday, December 7, 2019

FCGI (FastCGI) PHP Apache OPcache



FCGI Overview

FCGI is one of many PHP handlers that Apache can use to interpret PHP code. The handlers are responsible for reading / interpreting PHP code then compiling it and executing the code. Selecting the best PHP handler for your server is critical to overall performance.
Once mod_fcgid is enabled, all PHP processing is done via the FastCGI protocol. mod_fcgid starts up a certain amount of persistent processes that listen for new PHP requests and process them when needed. These processes remain alive even if there are no PHP requests, so you must make sure you configure FCGID to run an appropriate amount of processes, otherwise the server might waste a lot of resources on idle PHP processes. Generally speaking, you should keep a few (2 or 3) idle FCGID processes running to quickly respond to new requests, that way you limit idle resource usage, but still allow for FCGID to spawn more processes if traffic picks up. To determine the maximum FCGID processes you will want to determine how much Memory the average PHP process uses, then divide the available server RAM by this number.
There is no benefit in setting the max process limit too high, otherwise you will risk server instability, especially if it starts to utilize swap space due to lack of RAM. Generally 20 or 30 max FCGID processes is a good place to start.

FCGI Versus suPHP Benchmarks

As you can see from the results. FCGI is generally much faster than suPHP, especially with OPcache on. Notice how suPHP performs worse with opcode caching enabled, mainly because suPHP processes are not persistent, so there is no point in caching something that will never get re-used. DO NOT USE APC or OPCACHE with SUPHP. Only use opcode caching with FCGI.
Test Results below were gathered using a LiquidWeb's 1GB SSD VPS
FCGI SUPHP OPCACHE BENCHMARKS.png

How to enable FCGI PHP handler on cPanel

The first thing to check is whether or not FCGI is listed as an available PHP handler. Run this command to see what the current handler is, and what handlers are available.
/usr/local/cpanel/bin/rebuild_phpconf --current

Available handlers: fcgi cgi none
If you do not see fcgi listed then you will need to run EasyApache and select Mod FastCGI. I also recommend upgrading PHP to at least 5.5 and making sure that Apache is updated to at least 2.4 with the EVENT MPM enabled.
/scripts/easyapache
EasyApache normally takes about 10 minutes or less to finish rebuilding the configuration, once this is done you should be able to log into WHM search for "PHP" and then click on "Configure PHP and suEXEC" and select FCGI as the handler, then save the config just like the image below.
WHM FCGI PHP HANDLER.png

mod_fcgid cpanel file locations

When you activate FCGI as the PHP handler on a cPanel server, there will be a new ifmodule placed in this includes file
vim /usr/local/apache/conf/includes/pre_virtualhost_global.conf
By default, cPanel applies these settings. Obviously this is not ideal, so use the configuration settings listed in the sections below to properly configure and [optimize] fcgi.
<IfModule mod_fcgid.c>
    FcgidMaxRequestLen 1073741824
</IfModule>

FCGI Considerations for Shared Servers

One thing to keep in mind if you plan on using FCGI on a server that has say, 21 + cPanel accounts on it. If the server has 1GB of RAM, you might set FcgidMaxProcesses to 20 to limit memory usage and prevent too many requests from locking up the server due to too much swap usage, or overloading your CPU.
This is all fine in most cases, but what happens if 20 of the sites already has a PHP process running as their own user, and someone tries to view website #21? Nothing happens, the website will hang and try and load for a long time, waiting for one of the occupied 20 FCGI processes to die off and be reborn again so it can serve user #21. In some cases this take be a minute or more. Obviously this is not a good thing, customer #21 is going to be mad their site doesn't load. 
Why does this happen? Because of the way FCGI handles processes and what to do with them once they are done handling requests. For a single website, the ideal FCGI configuration would be to create 10 processes, or however many you want, then set their life time to a large number, like 600 seconds or more. You would do this because of opcode caching, it's best to reuse cached PHP as much as possible to reduce CPU load.
For a shared server though, long running PHP processes are not ideal unless you have enough memory to give every user a single FCGI process, but if you have limited memory and 1000 cpanel accounts, that's not going to be possible since 1000 processes using 32MB each is probably more than 2GB or whatever small amount of memory your server might have.
If you have a server with limited memory and lots of cpanel accounts and you can't set FcgidMaxProcesses to a value higher than the # of users, then your only option is to significantly reduce the timeouts, lifetimes and increase scan intervals to kill off idle processes faster, and create new processes sooner.
While I can't say this would be the best configuration, I will say that it will reduce the chances of a "spinning website" or one that takes 30 seconds to load. By using 5 second intervals the worst case would be a 5 second delay, which sucks, a lot, but it's better than 30 seconds or more. For this to happen, 21 people all over the world would need visit 20 sites on your server within the same 5 seconds, then one other person would need to visit site #21 to run into the issue I described above, but it can happen.
Anyway, if this sounds like the problem you are having, try to use very low idle timeout and process lifetimes, this may reduce performance a but, but FCGI is still WAAAAY better than SuPHP.
<IfModule mod_fcgid.c>
    FcgidMaxRequestLen 1073741824
    FcgidMinProcessesPerClass 0
    FcgidMaxProcesses 20
    FcgidIdleTimeout 3
    FcgidProcessLifeTime 30
    FcgidIdleScanInterval 0
    FcgidErrorScanInterval 0
    FcgidZombieScanInterval 0
    FcgidSpawnScoreUpLimit 7000
    FcgidSpawnScore 1
    FcgidTerminationScore -1
    FcgidTimeScore 3
</IfModule>
I ran some quick tests to try and show how the IdleScanInterval and IdleTimeout settings can effect website load time. The results below were gathered on a cPanel server. I created 15 accounts and installed WordPress on each account. I then configured cPanel to use FCGI as the PHP handler and enabled PHP 5.5's built in OPcache. I limited FcgidMaxProcesses to 10 to replicate the issue. The theory was that after I curl the first 10 test sites, the other 5 sites would take significantly longer to load because the 10 FCGI processes would remain idle until the timeouts / scans decided it was time to kill the process off.
New processes can only be created once one of the existing 10 processes dies. As you can see here the most dramatic improvement happened when I lowered IdleTimeout from 300 (Orange Column) to 30 (Yellow Column). The longest delay between a page load dropped from 1 minute to 35 seconds.
Since IdleTimeout seems to be a huge factor, I lowered it again, from 30 seconds to 3 seconds (Blue Column). As you can see the delay dropped from 35 seconds to 9 seconds. Not bad, but if my website took 9 seconds to load, or even begin to display content I would not be happy. The green column looks much better, with a delay of 3 seconds. If you want to have the green column performance, you can have it! Below this section are some configurations based on various amounts of RAM which will provide good performance and will quickly kill off idle FCGI processes so that new ones can be created.
FCGI max processes website loading delay hangup.png
This is not an issue that will happen very often. The script I used is requesting 15 webpages as fast as it can. For the same situation to happen in the real world, 15 people would need to request 15 sites on your server within the same 6 seconds to notice a slight delay. On servers with a lot of traffic, this might be a common thing to have happen, but I'm testing out these settings for a 1GB VPS server, which is rather small. If I had 2GB of memory I would have raised FcgidMaxProcesses to at least 20, and none of this would be an issue.

FCGI Settings for Servers with only a few cPanel Accounts

  • Use Apache 2.4 Event
  • Use FCGI or PHP-FPM to handle PHP
  • Use PHP 5.5 or newer
  • Enable Zend OPcache for PHP

1GB VPS / cPanel Server

If the server already has PHP 5.5+, enable Zend OPcache. If it does not have PHP 5.5 then you should consider upgrading PHP. Otherwise you can still use fcgi, but you won't be able to use the built in opcache.so. You can still use Xcache or APC or whatever you prefer with PHP 5.4 so don't worry too much about upgrading to 5.5, but still, in general you want to run the latest version of PHP.
To enable opcache for PHP, modify the server's php.ini file
vim /usr/local/lib/php.ini
If cPanel is using PHP version 5.5 or later, the opcache.so extension should already exist. All you need to so is paste this at the bottom of php.ini, once you save the file, restart apache and you should see Zend OPcache listed when you run php -m
zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20121212/opcache.so
opcache.memory_consumption=64
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=7963
opcache.revalidate_freq=0
opcache.fast_shutdown=1
opcache.enable_cli=1
opcache.enable=1

The next step is to make sure we are using Apache with the Event MPM. Don't worry too much about messing around with Apache settings, the ones I have below are the default setting. Don't add this to the main httpd.conf file, instead add this to pre_virtualhost_global.conf, or use the WHM includes editor.
vim /usr/local/apache/conf/includes/pre_virtualhost_global.conf
##Apache Event Default Settings (overrides main httpd.conf)
KeepAlive               On
KeepAliveTimeout        2
MaxKeepAliveRequests    500
<IfModule event.c>
ThreadsPerChild         25
ServerLimit             16
MaxRequestWorkers       400
StartServers            6
MinSpareThreads         150
MaxSpareThreads         400
MaxRequestsPerChild     0
</IfModule>

<IfModule mod_fcgid.c>
FcgidMaxRequestLen                      1073741824
FcgidOutputBufferSize                   1073741824
FcgidMinProcessesPerClass               0
FcgidMaxRequestsPerProcess              0
FcgidInitialEnv PHP_FCGI_MAX_REQUESTS   0
FcgidInitialEnv PHP_FCGI_CHILDREN       0
FcgidMaxProcesses                       20
FcgidMaxProcessesPerClass               10
FcgidFixPathinfo                        1
FcgidIdleTimeout                        3
FcgidBusyTimeout                        300
FcgidProcessLifeTime                    300
FcgidIOTimeout                          300
FcgidIdleScanInterval                   1
FcgidErrorScanInterval                  1
FcgidZombieScanInterval                 1
</IfModule>
Go ahead and stop, then start apache, don't just restart it. I find that sometimes if you change start server settings, or other directives a restart does not always apply the changes, not sure if this is a cpanel thing or what.
service httpd stop; service httpd start

2GB VPS / cPanel Server

vim /usr/local/lib/php.ini
zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20121212/opcache.so
opcache.memory_consumption=64
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=7963
opcache.revalidate_freq=0
opcache.fast_shutdown=1
opcache.enable_cli=1
opcache.enable=1
vim /usr/local/apache/conf/includes/pre_virtualhost_global.conf
##Apache Event Default Settings (overrides main httpd.conf)
KeepAlive               On
KeepAliveTimeout        2
MaxKeepAliveRequests    500
<IfModule event.c>
ThreadsPerChild         25
ServerLimit             16
MaxRequestWorkers       400
StartServers            6
MinSpareThreads         150
MaxSpareThreads         400
MaxRequestsPerChild     0
</IfModule>

<IfModule mod_fcgid.c>
FcgidMaxRequestLen                      1073741824
FcgidOutputBufferSize                   1073741824
FcgidMinProcessesPerClass               0
FcgidMaxRequestsPerProcess              0
FcgidInitialEnv PHP_FCGI_MAX_REQUESTS   0
FcgidInitialEnv PHP_FCGI_CHILDREN       0
FcgidMaxProcesses                       40
FcgidMaxProcessesPerClass               20
FcgidFixPathinfo                        1
FcgidIdleTimeout                        3
FcgidBusyTimeout                        300
FcgidProcessLifeTime                    300
FcgidIOTimeout                          300
FcgidIdleScanInterval                   1
FcgidErrorScanInterval                  1
FcgidZombieScanInterval                 1
</IfModule>
service httpd stop; service httpd start

4GB VPS / cPanel Server

vim /usr/local/lib/php.ini
zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20121212/opcache.so
opcache.memory_consumption=64
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=7963
opcache.revalidate_freq=0
opcache.fast_shutdown=1
opcache.enable_cli=1
opcache.enable=1
vim /usr/local/apache/conf/includes/pre_virtualhost_global.conf
##Apache Event Default Settings (overrides main httpd.conf)
KeepAlive               On
KeepAliveTimeout        2
MaxKeepAliveRequests    500
<IfModule event.c>
ThreadsPerChild         25
ServerLimit             16
MaxRequestWorkers       400
StartServers            6
MinSpareThreads         150
MaxSpareThreads         400
MaxRequestsPerChild     0
</IfModule>

<IfModule mod_fcgid.c>
FcgidMaxRequestLen                      1073741824
FcgidOutputBufferSize                   1073741824
FcgidMinProcessesPerClass               0
FcgidMaxRequestsPerProcess              0
FcgidInitialEnv PHP_FCGI_MAX_REQUESTS   0
FcgidInitialEnv PHP_FCGI_CHILDREN       0
FcgidMaxProcesses                       80
FcgidMaxProcessesPerClass               40
FcgidFixPathinfo                        1
FcgidIdleTimeout                        3
FcgidBusyTimeout                        300
FcgidProcessLifeTime                    300
FcgidIOTimeout                          300
FcgidIdleScanInterval                   1
FcgidErrorScanInterval                  1
FcgidZombieScanInterval                 1
</IfModule>
service httpd stop; service httpd start

8GB VPS / cPanel Server

vim /usr/local/lib/php.ini
zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20121212/opcache.so
opcache.memory_consumption=64
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=7963
opcache.revalidate_freq=0
opcache.fast_shutdown=1
opcache.enable_cli=1
opcache.enable=1
vim /usr/local/apache/conf/includes/pre_virtualhost_global.conf
##Apache Event Default Settings (overrides main httpd.conf)
KeepAlive               On
KeepAliveTimeout        2
MaxKeepAliveRequests    500
<IfModule event.c>
ThreadsPerChild         25
ServerLimit             16
MaxRequestWorkers       400
StartServers            6
MinSpareThreads         150
MaxSpareThreads         400
MaxRequestsPerChild     0
</IfModule>

<IfModule mod_fcgid.c>
FcgidMaxRequestLen                      1073741824
FcgidOutputBufferSize                   1073741824
FcgidMinProcessesPerClass               0
FcgidMaxRequestsPerProcess              0
FcgidInitialEnv PHP_FCGI_MAX_REQUESTS   0
FcgidInitialEnv PHP_FCGI_CHILDREN       0
FcgidMaxProcesses                       160
FcgidMaxProcessesPerClass               80
FcgidFixPathinfo                        1
FcgidIdleTimeout                        3
FcgidBusyTimeout                        300
FcgidProcessLifeTime                    300
FcgidIOTimeout                          300
FcgidIdleScanInterval                   1
FcgidErrorScanInterval                  1
FcgidZombieScanInterval                 1
</IfModule>
service httpd stop; service httpd start

mod_fcgid configuration settings

FcgidMaxRequestLen

If the size of the request body exceeds this amount, the request will fail with 500 Server Error. By default this is set to 131072 Bytes. For cPanel the default is 1073741824, which is about 1GB.
<IfModule mod_fcgid.c>
    FcgidMaxRequestLen 1073741824
</IfModule>

FcgidMaxProcessesPerClass

This directive sets the maximum number of processes that can be started for each process class, turns out that class means PER USER. If you have multiple users, or have a cPanel server please DO NOT USE THIS. Use FcgidMaxProcesses which sets the global limit. If you set the per class value, it gets multiplied by the amount of users. By default this value is set to 100. If you do not have lots of RAM, lower this to around 20 or so, assuming your app uses about 30MB per process.
<IfModule mod_fcgid.c>
    FcgidMaxProcessesPerClass 100 (default)
</IfModule>

FcgidMaxProcesses

This sets the global / server limit for how many PHP processes can run. Unlike the FcgidMaxProcessesPerClass directive, which is a per user limit, this directive is for all users. This is basically MaxClients but for PHP. If you have more users than processes, you may run into an issue where some websites appear to hang up for well over a minute. Read on to find out how to get around this issue by lowering various timeout and scan interval settings.
<IfModule mod_fcgid.c>
    FcgidMaxProcesses 20
</IfModule>

FcgidMaxRequestsPerProcess

FastCGI application processes will be terminated after handling the specified number of requests. By default this is set to 0 which means there is no limit / check.
<IfModule mod_fcgid.c>
    FcgidMaxRequestsPerProcess 0
</IfModule>

FcgidConnectTimeout

This is the maximum period of time the module will wait while trying to connect to a FastCGI application on Windows. (This directive is not respected on Unix, where AF_UNIX defaults will apply.)

FcgidIOTimeout

This is the maximum period of time the module will wait while trying to read from or write to a FastCGI application. If you have a slow application you may want to raise this, however 40 seconds is plenty of time. IF you site / app takes 40 seconds to load then odds are no one will ever want to visit your site again.

FcgidIdleTimeout

Application processes which have not handled a request for this period of time will be terminated, if the number of processses for the class exceeds FcgidMinProcessesPerClass. A value of 0 disables the check. The default value is 300 seconds, which means that a FCGI process could serve 1 request, and then remain idle for up to 1 minute or more. This can be a huge waste of resources if you have lots of sites on your server.
<IfModule mod_fcgid.c>
    FcgidIdleTimeout 300 (default)
</IfModule>
If you notice that some websites are hanging up when you try to load them, it could be because all the FCGI processes are active and assigned to a user. If you have more users than FCGI processes then lowing FcgidIdleTimeout to a smaller value like 30, or even 3 will significantly reduce the chances of this happening.
<IfModule mod_fcgid.c>
    FcgidIdleTimeout 3
</IfModule>

FcgidMinProcessesPerClass

This directive sets the minimum number of processes that will be retained in a process class after finishing requests. By default the value is 3.
<IfModule mod_fcgid.c>
    FcgidMinProcessesPerClass 3
</IfModule>

FcgidProcessLifeTime

Idle application processes which have existed for greater than this time will be terminated, if the number of processses for the class exceeds FcgidMinProcessesPerClass. A value of 0 disables the check. By Default this is set to 3600, if you want to save RAM you can set this to something like 30 to kill off unused processes
<IfModule mod_fcgid.c>
    FcgidProcessLifeTime 60
</IfModule>

FcgidIdleScanInterval

This is the interval at which the module will search for processes which have exceeded FcgidIdleTimeout or FcgidProcessLifeTime. The default is 120 seconds, you can lower this if you want shorter living processes. If you notice that on occasion, some websites seem to hang for over a minute or more when you try to load the page, you will want to lower this setting. If all FCGI processes are in use, and you have more users than the process limit, lowering this to 5 or even 0 will cause processes to clear out much faster instead of hanging around. Setting this to 0 is a valid way to go. You will need to lower some other scan intervals besides this one to make sure that processes are not hanging around wasting space.
<IfModule mod_fcgid.c>
    FcgidIdleScanInterval 120 (default)
</IfModule>

FcgidTerminationScore

Lowing the FcgidTerminationScore value increases the allowed spawn rate, the value can be negative which can be useful for allowing process replacement without increasing the score. I did notice that lowering FcgidTerminationScore to 1 did seem to slightly boost performance while running Apache Benchmark, a basic Wordpress blog was requested 100 times and with a score of 2 the server pumped out 23 request/s. With a score of 1 the server was able to reach 29 request/s. Values that were lower than 0 seemed to reduce performance slightly, but every server / website will perform differently so you might want to test the differences for yourself.
<IfModule mod_fcgid.c>
FcgidTerminationScore 2 (default)
</IfModule>

FcgidTimeScore

The value set for FcgidTimeScore determines the amount subtracted from the activity score of a FCGI process each second. Higher values increase the spawn rate, usually this means faster response times, and potentially higher amounts of CPU usage under large amounts of traffic. I've found that raising FcgidTimeScore to 3 seems to help with clearing old processes out and quickly creating new processes to handle new user requests.
<IfModule mod_fcgid.c>
FcgidTimeScore 1 (default)
</IfModule>

Zend opcache

Zend OPcache Links

opcache.max_accelerated_files

opcache.max_accelerated_files - Controls how many PHP files can be held in memory at the same time. Should be set to a Prime number for better performance. { 223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987 }
To find out how many files you have, you can use a command like this.
find . -type f -print | grep php | wc -l
I set this to 7963 for my 1GB server and this value seems to work well.
opcache.max_accelerated_files = 7963

opcache.memory_consumption

opcache.memory_consumption - The default is 64MB, if you have a ton of sites or large amounts of PHP code in general you might want to raise this. For a 1GB server I set this to 64MB.
opcache.memory_consumption = 64

opcache.interned_strings_buffer

opcache.interned_strings_buffer - PHP uses string interning to improve performance. If you have the string "mahstring" 100 times in your code, PHP will store 1 variable for the string and will use a pointer to it for the other 99 times it gets used. This value is in MB. Raising this value too high causes each PHP process to use more RAM, or at least appear to. When this was set to 16MB each PHP process was around 48MB. When set to 4MB each PHP process used around 32MB RAM each. This is shared RAM so the usage may not matter much, but just keep that in mind when you tune this and use high values.
opcache.interned_strings_buffer = 4

opcache.revalidate_freq

opcache.revalidate_freq How often in seconds should the code cache expire and check if your code has changed. 0 means it checks your PHP code every single request which can hurt performance. If you don't update PHP code all that often then setting this to 120 seconds could help with performance and still get refreshed every 2 minutes.
opcache.revalidate_freq=120

Using a Custom / Local php.ini with FCGI

cd /home/$user/public-html/cgi-bin
cp /usr/local/lib/php.ini /home/$user/public_html/cgi-bin/
vim /home/$user/public_html/cgi-bin/php.fcgi

##Create the file with this##

#!/bin/sh
export PHP_FCGI_CHILDREN=0
export PHP_FCGI_MAX_REQUESTS=0
exec /usr/local/cpanel/cgi-sys/php5
Save the file, then make sure it is executable
chmod +x /home/$user/public_html/cgi-bin/php.fcgi
chown -R $user:$user /home/$user/public_html/cgi-bin/
Add this to the user's .htaccess file
vim /home/$user/public_html/.htaccess

##Add to top##

AddHandler php5-fastcgi .php
Action php5-fastcgi /cgi-bin/php.fcgi
Now it's time to modify the php.conf file, but before you do, please back up the original one.
cp -a /usr/local/apache/conf/php.conf /usr/local/apache/conf/php.conf.`date +%F.%H.%M`
vim /usr/local/apache/conf/php.conf 
##Make sure these two lines are in the conf##

Action php5-fastcgi /cgi-bin/php.fcgi
AddType application/x-httpd-php .php
Save the file and distill the apache conf and restart apache
/usr/local/cpanel/bin/apache_conf_distiller --update
/scripts/rebuildhttpdconf
/etc/init.d/httpd restart
Create a php info file to test out the changes
vim /home/$user/public_html/phpinfo.php

##Add this##
<?php
phpinfo();
?>
Make sure the php info file has the correct ownerships
chown $user:$user /home/$user/public_html/phpinfo.php
Near the top of the page you should see that the custom path / php.ini file is in use
Loaded Configuration File /home/user/public_html/cgi-bin/php.ini

Additional Information

Video: Scaling PHP in the Real World - Dustin Whittle


Links


Reference: