Nginx is a high performance web server thatβs free to use. Nginx is designed for speed and scalability with capabilities of reverse proxy and load balancing to a number of backend servers both with HTTP, TCP and UDP protocols. This website is powered by WordPress and Nginx and the performance is really good. Nginx has small memory footprint as compared to Apache, handling same number of concurrent connections. Below you’ll install Nginx with PHP-FPM on Ubuntu, serve a PHP page through it, and confirm the PHP build is patched against PHP2Shell. Every command except the reboot was run as a sudo user on fresh Ubuntu 24.04 and 22.04 systems in October 2026, including an upgrade from the pre-fix PHP builds.
Key Features of Nginx
- Content Cache: Cache static and dynamic content
- Load Balancing: HTTP, TCP, and UDP load balancing with Layer 7 request routing using URI, cookie, args, and more.
- Reverse proxy multiple protocols: HTTP, gRPC, memcached, PHPβFPM, SCGI, uwsgi
- Handle hundreds of thousands of clients simultaneously
- Stream HTTP video: FLV, HDS, HLS, MP4
- HTTP/2 gateway
- Dualβstack RSA/ECC SSL/TLS offload
- Monitoring plugins: AppDynamics, Datadog, Dynatrace plugβins
This guide has been created to help users running Ubuntu server to install Nginx web server and configure PHP-FPM (FastCGI Process Manager).
Step 1: Update Ubuntu server
Before you begin, you should have a running Ubuntu server that has been updated and upgraded to the latest available packages.
sudo apt update && sudo apt -y full-upgrade
[ -f /var/run/reboot-required ] && sudo reboot
The second line reboots only when the upgrade installed something that needs it, such as a new kernel.
Step 2: Install Nginx on Ubuntu
Apache and Nginx can’t both listen on port 80. If Apache is already installed on this server, stop and disable it before you install Nginx, because Nginx cannot start while Apache holds the port, and apt still finishes without an error, so the failure is easy to miss. On a fresh server the command prints Unit file apache2.service does not exist, which you can ignore.
sudo systemctl disable --now apache2
With port 80 free, install the Nginx package from the Ubuntu archive.
sudo apt install -y nginx
The package enables and starts the service on its own. Check the first lines of its status.
systemctl status nginx --no-pager | head -n 3
On our Ubuntu 24.04 server it reports enabled and active (running):
β nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
Active: active (running) since Thu 2026-10-22 19:48:46 UTC; 1s ago
UFW firewall can be configured to allow ports 80 and 443:
sudo ufw allow proto tcp from any to any port 80,443
UFW answered Rules updated for IPv4 and IPv6 on both test servers. The rule only filters traffic once UFW itself is enabled.
Step 3: Install PHP-FPM on Ubuntu
Nginx does not run PHP itself, so it hands PHP requests to PHP-FPM over a Unix socket. Install PHP-FPM with the common extensions, and leave out the plain php metapackage. It pulls in php8.3 on 24.04 (php8.1 on 22.04), whose first listed alternative is Apache’s PHP module, libapache2-mod-php8.3, so it brings in Apache, which then fails to start because Nginx already owns port 80.
sudo apt update
sudo apt install -y php-fpm php-cli php-json php-pdo php-mysql php-zip php-gd php-mbstring php-curl php-xml php-pear php-bcmath
Ubuntu 24.04 installs PHP 8.3 and 22.04 installs PHP 8.1. The service name and config paths carry that branch number, so save it in a variable for the rest of this guide.
PHPV=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')
echo "$PHPV"
On 24.04 the variable holds the PHP 8.3 branch:
8.3
If you set this server up with an earlier version of this guide, the php metapackage may have left Apache and its PHP module behind. Check with dpkg -l apache2, and if it’s listed and you don’t use Apache, purge both. PHP-FPM, the php metapackage and Nginx stay installed and running:
sudo apt purge -y apache2 libapache2-mod-php${PHPV}
PHP-FPM is a service of its own, and the package starts it on install. Check it the same way as Nginx.
systemctl status php${PHPV}-fpm --no-pager | head -n 3
The unit is enabled, so it comes back after a reboot, and it is running:
β php8.3-fpm.service - The PHP 8.3 FastCGI Process Manager
Loaded: loaded (/usr/lib/systemd/system/php8.3-fpm.service; enabled; preset: enabled)
Active: active (running) since Thu 2026-10-22 19:49:30 UTC; 78ms ago
PID and Socket file are located in the directory:
ls /run/php/
Besides the versioned socket, Ubuntu keeps a php-fpm.sock link that always points at the default PHP branch:
php-fpm.sock
php8.3-fpm.pid
php8.3-fpm.sock
On 22.04 the same files carry php8.1 in their names, and the link works the same way, which is why the Nginx config below points at php-fpm.sock.
Step 4: Configure PHP-FPM with Nginx
Edit FPM default www pool configuration file
sudo vim /etc/php/${PHPV}/fpm/pool.d/www.conf
Set the listen line to the versionless socket path, so the Nginx side does not change when you move to another PHP branch:
listen = /run/php/php-fpm.sock
PHP-FPM reads the pool file only at start, so restart it. Afterwards /run/php/php-fpm.sock is the pool’s real socket instead of a link:
sudo systemctl restart php${PHPV}-fpm
Next, keep the FastCGI settings in one snippet that every site can include.
sudo vim /etc/nginx/php_fastcgi.conf
Add the following. It includes fastcgi.conf rather than fastcgi_params on purpose: on Ubuntu only fastcgi.conf sets SCRIPT_FILENAME, and without it PHP-FPM logs Primary script unknown and Nginx returns 404 for every PHP page, which is exactly what we got with fastcgi_params.
# 404
try_files $fastcgi_script_name =404;
# fastcgi_params plus SCRIPT_FILENAME
include fastcgi.conf;
# fastcgi settings
fastcgi_pass unix:/run/php/php-fpm.sock;
fastcgi_index index.php;
fastcgi_buffers 8 16k;
fastcgi_buffer_size 32k;
fastcgi_hide_header X-Powered-By;
fastcgi_hide_header X-CF-Powered-By;
The snippet does nothing until a server block includes it. For the default site, open its config.
sudo vim /etc/nginx/sites-available/default
Replace its contents with this server block, which adds index.php to the index list and sends .php requests through the snippet. Your own application’s server block takes the same location block.
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.php index.html index.htm index.nginx-debian.html;
server_name _;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include php_fastcgi.conf;
}
}
Test the configuration before reloading, so a typo does not take the site down.
sudo nginx -t
Both lines should report success, and on an error Nginx names the file and line it choked on:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Reload Nginx to apply it.
sudo systemctl reload nginx
Now drop a small test page into the web root and request it through Nginx:
echo '<?php echo PHP_VERSION, " ", PHP_SAPI, PHP_EOL;' | sudo tee /var/www/html/info.php
curl -s http://127.0.0.1/info.php
After tee echoes the PHP line back, curl prints the PHP version and fpm-fcgi, which proves Nginx passed the request to PHP-FPM instead of serving the file as text:
8.3.6 fpm-fcgi
Keep the test page for the PHP2Shell check in the next step, and delete it there.
Step 5: Check PHP-FPM for PHP2Shell (CVE-2026-109618, CVE-2026-109619)
PHP2Shell is a pair of critical remote code execution bugs in PHP itself, CVE-2026-109618 and CVE-2026-109619, scored CVSS v4.0 9.2 in php.net’s 20 October pre-announcement. PHP fixed them upstream on 21 October 2026 in 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Ubuntu backported the fix without changing the upstream version, into 8.3.6-0ubuntu0.24.04.13 on 24.04 and 8.1.2-1ubuntu2.28 on 22.04, so a fresh install from this guide is already patched. The PHP security advisory has the details.
Because the version number does not move, php -v proves nothing here. The package changelog does, since it names the CVEs the build fixes. Every command in this step uses PHPV, and in a new shell (or on a server you set up earlier) that variable is empty, which turns php${PHPV}-common into the unrelated php-common package and makes the version check below print a false patched. Set it again first; the second line stops with an error if it is still empty:
PHPV=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')
: "${PHPV:?set PHPV first}"
With the branch set, read the changelog:
zgrep -E 'CVE-2026-10961[89]' /usr/share/doc/php${PHPV}-common/changelog.Debian.gz
A patched build prints these three lines on both releases. No output means the installed package is older than the fixed build:
- debian/patches/CVE-2026-109618_109619.patch: Avoid state corruption via
- CVE-2026-109618
- CVE-2026-109619
Minimized Ubuntu images strip /usr/share/doc, so there’s no changelog to read. This comparison checks the package version instead. It’s written for 24.04; on 22.04 put 8.1.2-1ubuntu2.28 in place of the 24.04 version. If your PHP comes from ppa:ondrej/php instead of Ubuntu, compare against the PPA build for your branch instead (8.5.12, 8.4.27, 8.3.36 or 8.1.34-9), because a PPA version always sorts above Ubuntu’s and would read as patched.
dpkg --compare-versions "$(dpkg-query -W -f='${Version}' php${PHPV:?set PHPV first}-common)" ge 8.3.6-0ubuntu0.24.04.13 && echo patched || echo VULNERABLE
It printed VULNERABLE on our test server while the older 24.04 release build was installed, and this after the upgrade:
patched
A fresh install can skip to removing the test page. On an existing server where either check fails, upgrade every package of the PHP branch. The Apache module starts with lib, so the glob misses it and it’s named separately. APT prints a Skipping ... it is not installed line for each name the server does not have, which is harmless.
sudo apt update
sudo apt install -y --only-upgrade "php${PHPV}*" "libapache2-mod-php${PHPV}"
Restart PHP-FPM yourself so no worker keeps the old code in memory. If Apache with mod_php is also active on the server, restart apache2 as well, and restart long-running PHP CLI processes such as queue workers kept alive by supervisor or a systemd unit.
sudo systemctl restart php${PHPV}-fpm
To prove the restart picked up the new build, compare two times. First find when the PHP packages were last upgraded, including rotated logs.
zgrep -hE " (install|upgrade) php${PHPV}-(common|fpm):" /var/log/dpkg.log* | sort | tail -1
Our 24.04 server shows the jump from the release build to the fixed one:
2026-10-22 19:50:12 upgrade php8.3-common:amd64 8.3.6-0maysync1 8.3.6-0ubuntu0.24.04.13
Then ask systemd when PHP-FPM last came up. A service that started before the upgrade is still running the old code, whatever the package database says.
systemctl show -p ActiveEnterTimestamp php${PHPV}-fpm
The timestamp must be later than the dpkg line, and here it is:
ActiveEnterTimestamp=Thu 2026-10-22 19:50:39 UTC
The test page from Step 4 helps only on 22.04. There the patched build reports its package revision in PHP_VERSION, so the page printed a bare 8.1.2 before the upgrade and this after it. On 24.04 the page says 8.3.6 before and after the fix, so rely on the timestamp there.
8.1.2-1ubuntu2.28 fpm-fcgi
Remove the test page once you’ve read it, since it tells anyone which PHP build you run:
sudo rm /var/www/html/info.php
Ubuntu 20.04 has no free fix. Its php7.4 patch (7.4.3-4ubuntu2.29+esm5) ships only through Ubuntu Pro, and the Ondrej PPA no longer publishes focal builds. Without Pro, move the site to Ubuntu 24.04 and follow this guide there, since its PHP 8.3 already carries the fix.
Check other Nginx articles:
- Nginx as a web server and reverse proxy on Ubuntu and Debian
- WordPress with Nginx on Ubuntu
- Install Nginx on Ubuntu 26.04 LTS with Let’s Encrypt SSL
Nginx Books to read: