← Start of tutorial

WIUNIX ACADEMY

Install WordPress on Nginx with a privacy-safe FastCGI cache

Page 2 of 8. Review the earlier steps and prerequisites before continuing.

YOUR SYSTEM. YOUR STEPS.

Where are you working?

Choose your OS and version. The prerequisites, commands and troubleshooting follow your environment.

Rhel 10.2Reviewed against official documentation ·

USE YOUR OWN VALUES

Configure this tutorial

Set the example values for your environment; related explanations and copy panels update together. Never enter passwords, private keys or tokens. Values stay in this tab and are not sent to the server or share links. Check actual DNS, certificates and network policy before running commands.

Different IP addresses, domains or ports? Open the settings below, change the values and select “Apply to tutorial”, then copy the updated commands.

Set values for my environment4 editable values
06

LET’S BUILD

Check identity and database exposure

Back in the server shell, enter the password only at the interactive prompt. If 3306 is public, stop and correct MariaDB binding/network policy first; this site needs no public database port.

Record the printed @@socket and compare it with DB_HOST in the selected configuration panel. If they differ, use the actual local socket path there; do not expose TCP to work around a socket mismatch.

Bash

WordPress server, Bash, existing sudo administrator.

mariadb -u wp_site -p wordpress -e 'SELECT DATABASE(), CURRENT_USER();'
sudo mariadb -e 'SELECT @@bind_address, @@socket;'
sudo ss -lntp 'sport = :3306'
How do I know it worked?

Database is wordpress, identity is wp_site@localhost, listener remains local.

07

LET’S BUILD

Verify WordPress before installing files

The archive contains architecture-independent PHP code. HTTPS and both checksums must pass before extraction/copy. SHA1 comes from the official release endpoint; SHA256 was calculated from that validated archive during this review and is not an upstream signature. Neither protects against a compromised publisher. Recheck the official release archive before reusing this pinned version later. Root owns code; only uploads is writable by the dedicated PHP identity.

Bash

WordPress server, Bash, existing sudo administrator.

(
set -euo pipefail
umask 077
work=$(mktemp -d)
cd "$work"
curl --proto '=https' --proto-redir '=https' -fL --connect-timeout 10 --max-time 180 -o wordpress-7.1.4.tar.gz https://wordpress.org/wordpress-7.1.4.tar.gz
curl --proto '=https' --proto-redir '=https' -fL --connect-timeout 10 --max-time 60 -o wordpress.sha1 https://wordpress.org/wordpress-7.1.4.tar.gz.sha1
[ "$(tr -d '\r\n ' < wordpress.sha1)" = b175879c4fb2d35300f9abe1153da9b543a35a2b ]
printf '%s  %s\n' b175879c4fb2d35300f9abe1153da9b543a35a2b wordpress-7.1.4.tar.gz | sha1sum --check --strict -
printf '%s  %s\n' 881e53a4681ff2cd911651b11cccdede5ae477ddf0765af5d6a1a8b219a1d835 wordpress-7.1.4.tar.gz | sha256sum --check --strict -
tar -xzf wordpress-7.1.4.tar.gz
sudo test ! -e /var/www/wordpress
sudo test ! -L /var/www/wordpress
sudo useradd --system --user-group --no-create-home --shell /usr/sbin/nologin wp-php
sudo install -d -o root -g root -m 0755 /var/www/wordpress
sudo rsync -a --chown=root:root wordpress/ /var/www/wordpress/
sudo find /var/www/wordpress -type d -exec chmod 0755 {} +
sudo find /var/www/wordpress -type f -exec chmod 0644 {} +
sudo install -d -o wp-php -g nginx -m 0755 /var/www/wordpress/wp-content/uploads
printf 'Retained verified download: %s\n' "$work"
)
How do I know it worked?

SHA1 and SHA256 report OK, absent destination is populated, verified download retained.

Open the dedicated PHP pool

The stock www pool may remain in this single-site lab; this host never uses its socket. Do not disable another site’s pool. Save the next panel using nano (Ctrl+O, Enter, Ctrl+X).

Bash

WordPress server, Bash, existing sudo administrator.

(
set -euo pipefail
sudo nano /etc/php-fpm.d/wordpress.conf
)
How do I know it worked?

Check succeeds; stop on an error before continuing.

08

LET’S BUILD

Run PHP with a dedicated Unix identity

The dedicated pool uses wp-php and a nginx:nginx 0660 Unix socket at /run/php-fpm/wordpress.sock. Do not copy the stock www pool ACL users into this new pool: listen.owner/group are the intended access control. Validate the complete configuration before first start.

INI · /etc/php-fpm.d/wordpress.conf

File contents; save in the named file, not the shell.

[wordpress]
user = wp-php
group = wp-php
listen = /run/php-fpm/wordpress.sock
listen.owner = nginx
listen.group = nginx
listen.mode = 0660
pm = ondemand
pm.max_children = 5
pm.process_idle_timeout = 10s
pm.max_requests = 500
chdir = /var/www/wordpress
security.limit_extensions = .php
php_admin_value[upload_max_filesize] = 16M
php_admin_value[post_max_size] = 16M
php_admin_value[memory_limit] = 128M
php_admin_flag[display_errors] = off
php_admin_flag[log_errors] = on
How do I know it worked?

Dedicated wp-php pool defines the www-data-owned 0660 socket.

09

LET’S BUILD

Review exposure and host security before starting the site

Keep SELinux enforcing. The exact new content path is read-only httpd_sys_content_t; only uploads is httpd_sys_rw_content_t and the dedicated cache gets httpd_cache_t. Database access uses its local Unix socket; no global httpd network boolean is enabled. The standard FPM runtime path keeps the packaged socket policy. Inspect actual AVCs/process domains if startup or file access fails; do not disable policy.

firewalld must already be active with SSH preserved. Replace public with the actual public-interface zone before running. Inspect broad services/target and upstream ACLs first; add only80/tcp and443/tcp in runtime/permanent state without reload. Record existing versus added rules; remove only your exact new rules when retiring this disposable VM.

Bash

Fresh dedicated Linux VM, existing sudo administrator.

(
set -euo pipefail
getenforce
sudo install -d -o nginx -g nginx -m 0700 /var/cache/nginx/wordpress
sudo python3 - <<'PY'
import re,subprocess
root='/var/www/wordpress';cache='/var/cache/nginx/wordpress'
for pattern,kind in [(re.escape(root)+'(/.*)?','httpd_sys_content_t'),(re.escape(root+'/wp-content/uploads')+'(/.*)?','httpd_sys_rw_content_t'),(re.escape(cache)+'(/.*)?','httpd_cache_t')]:
 subprocess.run(['semanage','fcontext','-a','-t',kind,pattern],check=True)
subprocess.run(['restorecon','-Rv',root,cache],check=True)
PY
sudo firewall-cmd --state
sudo firewall-cmd --get-active-zones
# Replace public with the zone actually attached to the public interface:
zone=public
sudo firewall-cmd --zone="$zone" --list-all
for port in 80/tcp 443/tcp; do
 sudo firewall-cmd --zone="$zone" --add-port="$port"
 sudo firewall-cmd --permanent --zone="$zone" --add-port="$port"
done
sudo firewall-cmd --zone="$zone" --list-all
)
How do I know it worked?

The check succeeds; stop and diagnose any failure.

10

LET’S BUILD

Validate FPM before its first start

This is the first start of the fresh dedicated FPM service. Validate the complete selected-version configuration before unmasking and starting it; record the exact socket owner/mode. Later restarts interrupt all pools of that version.

Bash

WordPress server, Bash, existing sudo administrator.

(
set -euo pipefail
sudo php-fpm -t
sudo systemctl unmask php-fpm.service
sudo systemctl enable --now php-fpm.service
systemctl is-active php-fpm
sudo stat -c '%U:%G %a %n' /run/php-fpm/wordpress.sock
)
How do I know it worked?

FPM validates and starts; socket is nginx:nginx 660.

Explore all tutorials ↗