12. Install PHP

by Double Bastion - Updated January 10, 2023

To generate dynamic content, Nginx will need PHP. Since Nginx doesn’t process PHP scripts by itself, you will also need to install PHP-FPM. FPM stands for ‘FastCGI process manager’. PHP-FPM allows Nginx to act as a proxy, passing all requests with the php file extension to the PHP interpreter. Nginx has to be configured to pass PHP requests to PHP-FPM for processing. You will also need additional helper packages. First run:

apt-get update && apt-get dist-upgrade

Then run:

apt-get install ca-certificates apt-transport-https lsb-release

Then install php7.4 and some additional packages which will be necessary:

apt-get install php7.4 php7.4-fpm php7.4-json php7.4-cli php7.4-common php7.4-opcache php7.4-curl php7.4-mbstring php7.4-mysql php7.4-zip php7.4-xml php7.4-intl php7.4-gd php7.4-readline php7.4-imap php7.4-ldap php7.4-bcmath php7.4-gmp imagemagick php-imagick

Now you have the PHP components installed, but you need to make additional configuration changes to make your setup more secure.

First, make a copy of the original /etc/php/7.4/fpm/pool.d/www.conf file:

cp /etc/php/7.4/fpm/pool.d/www.conf /etc/php/7.4/fpm/pool.d/www.conf_orig

Open /etc/php/7.4/fpm/pool.d/www.conf:

nano /etc/php/7.4/fpm/pool.d/www.conf

Search for the security.limit_extensions parameter and configure it like this:

security.limit_extensions = .php

Also confirm that the listen directive is set as follows:

listen = /run/php/php7.4-fpm.sock

Uncomment ;clear_env = no to make it look like this:

clear_env = no

Next, make a copy of the original /etc/php/7.4/fpm/php.ini file, which is the main php-fpm configuration file:

cp /etc/php/7.4/fpm/php.ini /etc/php/7.4/fpm/php.ini_orig

Open /etc/php/7.4/fpm/php.ini:

nano /etc/php/7.4/fpm/php.ini

Change upload_max_filesize and post_max_size to the size you prefer. You can set them as follows:

upload_max_filesize = 800M

...

post_max_size = 800M

Change max_input_vars to make it look like this:

max_input_vars = 10000

Also change memory_limit to 512M like this:

memory_limit = 512M

It’s also recommended to change the session.gc_maxlifetime default time span from 24 minutes (1440 seconds) to something larger, like 4 hours (14400 seconds), so as to avoid being logged out automatically from applications like Dolibarr every 24 minutes when inactive. So make it look like this:

session.gc_maxlifetime = 14400

Restart the PHP processor by running:

systemctl restart php7.4-fpm

12.1. Short presentation of website caching

There are 5 main types of caching that can be implemented for websites/web applications in order to improve page loading speed and web server throughput. The first 4 are instances of server-side caching and the last one is client-side caching, which means it’s done in the visitor’s browser:

1. Opcode caching – is a type of caching that involves compiling the plain PHP code into machine code (opcode) and storing it in the RAM memory. This means that PHP doesn’t have to run the compile step on every request, saving time. Eg: PHP OPcache, APCU, etc.

2. Object caching – involves caching data objects which usually include the results of database queries. Instead of running the database queries again, the next time those results are needed, they are served from the cache. Eg: Memcached, Redis, etc.

3. Page caching – involves caching entire web pages in the RAM memory or on the hard drive. Nginx allows to automatically cache static HTML versions of web pages using the FastCGI module. Any subsequent requests for those pages will receive the cached versions without reaching the PHP interpreter or the database server.

4. CDN caching– involves caching of static files (such as CSS, JavaScript or media files) by Content Delivery Networks (CDNs) which are networks of servers distributed around the world, so that web pages can be delivered faster to visitors located at great distance from the host server. However, if the host server is fast and its physical location is properly chosen, the need for a CDN disappears. Some of the disadvantages of using CDNs are that they bring about security concerns (especially because of externally hosted JavaScript libraries), that CDN response times can be unpredictable, that it makes diagnosing connection problems more difficult, etc. If the host server is of good quality and powerful enough to serve all the websites that it hosts and if it’s physically located in the middle of the user base, there is no need to use CDN caching. For example, if the intended user/visitor base of your websites is North America + Europe, a powerful enough and properly configured server located on the east cost of the US would provide fast enough page loading speed for all the visitors, thus removing the need for a CDN. If your intended audience is the entire world, it’s better to use about 4 servers located in data centers from different parts of the world to build your own customized CDN using Nginx, as we’ll explain in a future article.

5. Browser caching – involves caching web pages in the visitor’s browser. Nginx can be configured so that a web page loaded into a visitor’s browser can be cached on their machine, and when next visited, if the cache hasn’t expired, it will be served from the browser’s cache. This results in faster page loading and a decreased number of HTTP requests hitting the web server.

Client-side caching or browser caching is beyond dispute and should be implemented whenever it’s possible by using the appropriate Nginx directives in the server configuration file, as we will show in this guide.

With server-side caching things are not that clear. If you read about others’ experience with server-side caching software alternatives, you will notice that all recommandations converge towards using only PHP OPcache for opcache, Memcached or Redis for object caching, and FastCGI cache for page caching. We already mentioned that CDN caching is to be avoided.

FastCGI cache even removes the need for Varnish (as our tests proved), since it offers the same performance gain, without having to install and maintain an additinal application, which consumes the server’s resources, represents an additional point of failure and in many situations is difficult to configure. For best performance, FastCGI cache must be configured to cache pages in the RAM memory and not on the hard drive. Adding other cashing components, not only that won’t improve website performance but can create page loading problems and drastically decrease loading speed.

As about Memcached versus Redis for object caching, we can say that Memcached is better for the setup described in this guide (and certainly for many others). In general, Memcached is slightly faster, it handles memory better, it’s multithreaded, it’s more lightweight and it has a long history of successful, efficient usage. Memcached does exactly what we need in this setup, complementing page caching, achieved with FastCGI cache.

Page caching can be implemented for the majority of web pages but there are pages that should be excluded from caching. For example, if you use WooCommerce, the cart, checkout and the WooCommerce ‘my account’ pages should be unique for each visitor. You wouldn’t want a visitor to see the content of another visitor’s shopping cart or see other visitor’s products on the checkout page.

Also, if you sell products or services to customers outside your country/state/province, and you use the ‘Geolocate’ option of WooCommerce to automatically change the displayed price of products/services according to the location of the visitor by adding the corresponding sales tax, you wouldn’t want a visitor from a country to see the cached product/store page of a visitor from a different country, since the sales tax would be different and hence the total price of the product or service would be different. Yet, single product pages and store pages should be cached (using FastCGI page caching), because it’s extremely important to offer visitors the fastest loading store and product pages possible, otherwise, they can leave the site after a few clicks. For this situation, you would want to place a prominent notice on single product pages, near the product price, informing the visitor that different sales/VAT/GST taxes may apply, depending on their location. In this way, if the visitor adds the product to the cart and goes to checkout, they will not be surprised to see on the checkout page, the real price of the product, slightly different from what they saw on the product page, since the checkout page will not be cached and therefore it will display the taxes applicable to the visitor’s detected location.

We describe how to enable geolocation in WooCommerce further down below, in the How to enable geolocation in WooCommerce chapter.

The pages that you have to exclude from FastCGI page caching can still benefit from object caching if you use Memcached for object caching, as we’ll explain below. In this way, the database server will receive fewer requests and this will increase the server’s throughput for concurent visits. The page loading speed will also increase significantly, since data will be read from RAM.

Needless to say that WordPress caching plugins (like ‘WP Super Cache’, ‘W3 Total Cache’, etc.) can’t be even compared with server-side caching. For a VPS, cloud server or dedicated server, where all the caching can be properly configured on the server, there is absolutely no need to add any caching plugin to WordPress. WordPress caching plugins only make sense for shared hosting environments, where the website administrator doesn’t have access to the underlying server and has to configure caching inside WordPress.

To be noted that in order to purge the Nginx cache when pages are changed, so that the visitors can see the new page versions and not the old cached versions, you will need to install in WordPress the ‘Nginx Cache’ plugin. This is a lightweight plugin that only does cache purging.

We’ll explain below how to enable PHP OPcache, how to tune PHP-FPM, how to install the Memcached server for object caching and then, when we’ll describe how to install and configure Nginx, we’ll also explain how to configure FastCGI cache.

12.2. Enable PHP OPcache

Make a copy of the original /etc/php/7.4/fpm/conf.d/10-opcache.ini file:

cp /etc/php/7.4/fpm/conf.d/10-opcache.ini /etc/php/7.4/fpm/conf.d/10-opcache.ini_orig

Open the /etc/php/7.4/fpm/conf.d/10-opcache.ini file:

nano /etc/php/7.4/fpm/conf.d/10-opcache.ini

Add the following lines at the end of the file:

opcache.enable=1
opcache.enable_cli=1
opcache.interned_strings_buffer=32
opcache.max_accelerated_files=10000
opcache.memory_consumption=256
opcache.save_comments=1
opcache.revalidate_freq=1

12.3. Tune PHP-FPM


Edit the /etc/php/7.4/fpm/pool.d/www.conf file:

nano /etc/php/7.4/fpm/pool.d/www.conf

Edit the following parameters to make them look like this:

pm = dynamic
pm.max_children = 16
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.max_requests = 500

Now, restart the PHP processor by running:

systemctl restart php7.4-fpm

12.4. Install Memcached for object caching

Memcached is an in-memory caching system that stores key-value pairs in the RAM memory. As mentioned, for our setup, and certainly in many other situations, it’s better than Redis.

The default WordPress object cache saves transients in the database, which makes reading them quite slow. Memcached is an object cache server that stores the transients in RAM.

To install the Memcached server and the other related packages which will be needed, run:

apt-get update
apt-get install memcached libmemcached-tools php-memcached

After installation, Memcached is started automatically. You can check that it’s running with:

systemctl status memcached

The output will look similar to this:

● memcached.service - memcached daemon
   Loaded: loaded (/lib/systemd/system/memcached.service; enabled; vendor preset: enabled)
   Active: active (running) since ...
...

The Memcached server can be used to communicate with all the applications by using TCP connections on port 11211. Yet, it’s much better to configure Memcached to use Unix sockets, since such connections lead to much higher data transfer speeds and page loading speeds. To configure Memcached to use Unix sockets instead of TCP connections, open the /etc/memcached.conf file:

nano /etc/memcached.conf

Increase the maximum amount of RAM usable by Memcached from the default 64 MB to 512 MB. If your server has more than 2 GB of total RAM memory, you can increase this value even further:

# Start with a cap of 64 megs of memory. It's reasonable, and the daemon default
# Note that the daemon will grow to this size, but does not start out holding this much
# memory
#-m 64
-m 512

Comment out the line with the default connection port, to make it look like this:

# Default connection port is 11211
#-p 11211

Comment out the line of the local IP address on which Memcached listens by default, to make it look like this:

#-l 127.0.0.1

Then, right below this line add the following lines, to configure Memcached to use a Unix socket file instead of a TCP connection:

# Configure Memcached to use a Unix socket file and change permissions for this file
-s /var/run/memcached/memcached.sock
-a 770

Increase the limit to the number of simultaneous connections to 2048, like this:

# Limit the number of simultaneous incoming connections. The daemon default is 1024
# -c 1024
-c 2048

The next thing to do is to change the ownership for the /var/run/memcached directory. By default, this directory is owned by the memcache user and by the memcache group, and the socket file created by Memcached upon restart and placed inside this directory will be also owned by the memcache user and the memcache group, with 700 permissions. This means that the web server user, www-data, will not have read and write access to the Unix socket file, which is required for caching to work. We need to change ownership and permissions for /var/run/memcached, in order to allow www-data to have read and write access to the Unix socket file. To do this run:

cd /var/run
chown -R memcache:www-data memcached
chmod 2750 memcached

From now on, whenever you restart Memcached, it will create the socket file as being owned by the memcache user and by the www-data group, with 770 permissions (configured in /etc/memcached.conf), therefore the www-data user will have read, write and execute access to this file.

Yet there remains a problem: when you will restart the server, Memcached will recreate the /var/run/memcached directory with the default ownership and permissions, which will remove the changes from above, so the cache won’t work again. To make the changes from above persist after reboot, you have to create a small script:

nano /srv/scripts/change-memcached-perm

Add the following lines inside this file:

#!/bin/bash

sleep 7
chown -R memcache:www-data /var/run/memcached
chmod 2750 /var/run/memcached
chmod 700 /var/cache/nginx

Change permissions for this script:

chmod 700 /srv/scripts/change-memcached-perm

Then, you will need to set up a cron job to run the script on startup. Open the crontab file:

crontab -e

If you run this command for the first time, you will see the following message:

no crontab for root - using an empty one

Select an editor.  To change later, run 'select-editor'.
  1. /bin/nano        <---- easiest
  2. /usr/bin/vim.tiny

Choose 1-2 [1]:

Type 1 to choose nano as the editor, then press Enter. The crontab file will be opened for editing. Add the following lines at the end of the file:

# Change ownership and permissions for the /var/run/memcached directory after every reboot
@reboot /srv/scripts/change-memcached-perm

Now you can restart Memcached:

systemctl restart memcached

You will also need to install the php-memcache PHP extension, needed by WordPress to interface with the Memcached server. To install it run:

apt-get install php-memcache

The Memcached server is now installed. Please note that although the /etc/memcached.conf file specifies that the log file will be /var/log/memcached.log, Memcached won’t write to it. If you want to see the logged data of Memcached run:

journalctl -u memcached.service

Please also note that in order to be able to use Memcached in conjunction with a WordPress website, you will also need the ‘Memcached Object Cache’ plugin, which is in fact a drop-in file that has to be copied inside the /var/www/example.com/wp-content directory, when installing each WordPress website, as we’ll explain in the Connect your WordPress website to the Memcached server to enable object caching chapter, further down below.

12.5. Upgrading PHP

Since PHP has been installed from the official Debian repository, to upgrade it, all you need to do is to run apt-get update && apt-get dist-upgrade with a specific frequency, as described in the Maintenance steps chapter. This command will upgrade PHP if there is a new version available. During these upgrades, the configuration changes implemented as described above, will be preserved. Please note that installing a PHP version that is newer than the one in the official Debian stable repository is not recommended. Apart from the issues that they can create when you upgrade them, in general, newer versions of PHP are not considered mature enough.

You can send your questions and comments to: