PHP and WordPress with nginx
Set up PHP-FPM, install WordPress and get the file permissions right.
5 min read
On this page
This guide installs PHP-FPM, connects it to nginx and sets up WordPress.
Covers Debian 12 and 13, Ubuntu 22.04 and 24.04, AlmaLinux 9 and Rocky Linux 9. All commands run as root. We use example.com as an example.
You need first:
- nginx and a domain pointing to the server: Set up a web server with HTTPS (option A with nginx).
- A database with its own user, e.g.
wordpress: Set up MariaDB.
Install PHP-FPM
Debian and Ubuntu ship a current PHP version (Debian 12: 8.2, Debian 13: 8.4, Ubuntu 22.04: 8.1, Ubuntu 24.04: 8.3):
apt update
apt install -y php-fpm php-mysql php-curl php-gd php-intl php-mbstring php-xml php-zip php-imagickAlmaLinux and Rocky Linux default to PHP 8.0. Enable a newer version. dnf module list php shows which exist; take the highest, here 8.2:
dnf module reset -y php
dnf module enable -y php:8.2
dnf install -y php-fpm php-mysqlnd php-gd php-intl php-mbstring php-xml php-pecl-zipOn AlmaLinux and Rocky, PHP-FPM runs as the user apache. Switch it to nginx. In /etc/php-fpm.d/www.conf:
user = nginx
group = nginxThen start it:
systemctl enable --now php-fpmWhere PHP-FPM listens
nginx talks to PHP-FPM through a socket. Find its path:
- Debian and Ubuntu:
ls /run/php/, e.g./run/php/php8.2-fpm.sock(the number is your PHP version). - AlmaLinux and Rocky Linux:
/run/php-fpm/www.sock.
Download WordPress
cd /tmp
curl -LO https://wordpress.org/latest.tar.gz
tar xzf latest.tar.gz
mkdir -p /var/www/example.com
cp -a wordpress/. /var/www/example.com/
rm -rf wordpress latest.tar.gzSet file permissions
The files belong to the user PHP runs as. Only then can WordPress install plugins and updates itself. Folders get 755, files 644.
Debian and Ubuntu (user www-data):
chown -R www-data:www-data /var/www/example.com
find /var/www/example.com -type d -exec chmod 755 {} +
find /var/www/example.com -type f -exec chmod 644 {} +AlmaLinux and Rocky Linux (user nginx), plus SELinux: WordPress may write to wp-content and connect out for updates:
chown -R nginx:nginx /var/www/example.com
find /var/www/example.com -type d -exec chmod 755 {} +
find /var/www/example.com -type f -exec chmod 644 {} +
dnf install -y policycoreutils-python-utils
semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/example.com/wp-content(/.*)?"
restorecon -Rv /var/www/example.com
setsebool -P httpd_can_network_connect 1Create wp-config.php
cd /var/www/example.com
cp wp-config-sample.php wp-config.phpEnter database name, user and password in wp-config.php:
define( 'DB_NAME', 'wordpress' );
define( 'DB_USER', 'wordpress' );
define( 'DB_PASSWORD', 'YOUR-PASSWORD' );
define( 'DB_HOST', 'localhost' );Also replace the block from AUTH_KEY to NONCE_SALT with fresh keys from https://api.wordpress.org/secret-key/1.1/salt/:
curl -s https://api.wordpress.org/secret-key/1.1/salt/The file holds the database password, so tighten it:
chmod 640 wp-config.phpnginx configuration
Replace the configuration from the web server guide, /etc/nginx/conf.d/example.com.conf, with this one. Adjust the path in fastcgi_pass to your socket:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com;
index index.php;
client_max_body_size 64M;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~ /\. {
deny all;
}
}Test, reload and let certbot add the certificate again:
nginx -t
systemctl reload nginx
certbot --nginx -d example.com -d www.example.comcertbot asks whether to reinstall the existing certificate; choose that.
If you upload files larger than 2 MB, raise upload_max_filesize and post_max_size in the PHP configuration (Debian/Ubuntu: /etc/php/<version>/fpm/php.ini, Alma/Rocky: /etc/php.ini) and restart PHP-FPM (systemctl restart php8.2-fpm or systemctl restart php-fpm).
Set up WordPress
Open https://example.com in your browser. The installer asks for language, site title, admin user and password. Do not use admin as the username.
Check that it worked
- The home page loads over
https://, and so does the dashboard athttps://example.com/wp-admin/. - Under Tools → Site Health, WordPress reports missing PHP modules and update problems.
- Install a plugin as a test. If it works without asking for FTP credentials, the permissions are right.
Common errors
- 502 Bad Gateway. nginx cannot reach PHP-FPM. Check
systemctl status php8.2-fpm(orphp-fpm) and that the socket path infastcgi_passis right. - The browser downloads the PHP file. The
location ~ \.php$block is missing or not matching. Checknginx -tand the configuration. - "Error establishing a database connection". Name, user or password in
wp-config.phpis wrong. Test them withmariadb -u wordpress -p wordpress. - WordPress asks for FTP on plugin updates. The files do not belong to the PHP user. Repeat the
chownabove. - Blank white page. Usually a PHP error.
tail -n 50 /var/log/nginx/error.logshows it. - Updates fail on AlmaLinux or Rocky. SELinux blocks outgoing connections.
setsebool -P httpd_can_network_connect 1.
What next
- Keep WordPress, plugins and themes up to date; most attacks target old plugins.
- Back up files and database regularly: Set up backups.
- Contact forms need outgoing mail: Sending mail from your VPS.
If you get stuck, open a ticket.