Skip to content
CitexNode
All guides

PHP and WordPress with nginx

Set up PHP-FPM, install WordPress and get the file permissions right.

5 min read

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:

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):

bash
apt update
apt install -y php-fpm php-mysql php-curl php-gd php-intl php-mbstring php-xml php-zip php-imagick

AlmaLinux 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:

bash
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-zip

On AlmaLinux and Rocky, PHP-FPM runs as the user apache. Switch it to nginx. In /etc/php-fpm.d/www.conf:

ini
user = nginx
group = nginx

Then start it:

bash
systemctl enable --now php-fpm

Where 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

bash
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.gz

Set 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):

bash
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:

bash
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 1

Create wp-config.php

bash
cd /var/www/example.com
cp wp-config-sample.php wp-config.php

Enter database name, user and password in wp-config.php:

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/:

bash
curl -s https://api.wordpress.org/secret-key/1.1/salt/

The file holds the database password, so tighten it:

bash
chmod 640 wp-config.php

nginx 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:

nginx
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:

bash
nginx -t
systemctl reload nginx
certbot --nginx -d example.com -d www.example.com

certbot 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 at https://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 (or php-fpm) and that the socket path in fastcgi_pass is right.
  • The browser downloads the PHP file. The location ~ \.php$ block is missing or not matching. Check nginx -t and the configuration.
  • "Error establishing a database connection". Name, user or password in wp-config.php is wrong. Test them with mariadb -u wordpress -p wordpress.
  • WordPress asks for FTP on plugin updates. The files do not belong to the PHP user. Repeat the chown above.
  • Blank white page. Usually a PHP error. tail -n 50 /var/log/nginx/error.log shows it.
  • Updates fail on AlmaLinux or Rocky. SELinux blocks outgoing connections. setsebool -P httpd_can_network_connect 1.

What next

If you get stuck, open a ticket.