Installing WordPress from the upstream tarball keeps the site layout, upgrade path, and troubleshooting flow predictable on current dnf-based hosts. That is usually the cleanest fit when the server needs a standard Apache virtual host, a local database, and a filesystem tree that matches WordPress.org instead of a distribution-specific wrapper package.
On current Fedora and RHEL-style systems, httpd serves the site, php-fpm executes PHP over a local Unix socket, and MariaDB stores posts, users, settings, and plugin data. The wp-config.php file connects those layers, while the virtual host and .htaccess support decide whether the installer and later pretty permalinks resolve correctly.
Examples below target current Fedora, CentOS Stream, and Red Hat Enterprise Linux style hosts that use dnf, httpd, and php-fpm. WordPress currently recommends PHP 8.3 or newer, MariaDB 10.6 or newer or MySQL 8.0 or newer, and HTTPS support; current Fedora 42 packaging also uses php-pecl-zip instead of the older php-zip name, and enforcing SELinux still needs a writable label on upload paths before media uploads work cleanly.
$ sudo dnf install -y httpd mariadb-server php php-fpm php-mysqlnd php-xml php-gd php-intl php-mbstring php-pecl-zip curl rsync tar
Current Fedora-family packaging uses php-pecl-zip for ZIP support, so older guides that still list php-zip are stale.
$ sudo systemctl enable --now httpd mariadb php-fpm $ systemctl is-active httpd mariadb php-fpm active active active
Current RHEL 9-style systems run Apache PHP requests through php-fpm rather than mod_php, so a stopped php-fpm service breaks every .php request even when httpd is active.
$ sudo mariadb MariaDB [(none)]> CREATE DATABASE wordpress CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; MariaDB [(none)]> CREATE USER 'wordpress'@'localhost' IDENTIFIED BY 'strong_password_here'; MariaDB [(none)]> GRANT ALL PRIVILEGES ON wordpress.* TO 'wordpress'@'localhost'; MariaDB [(none)]> FLUSH PRIVILEGES; MariaDB [(none)]> EXIT;
sudo mariadb is the safest default on current package installs because the local root account commonly authenticates through the Unix socket instead of a preset password.
Replace strong_password_here with a unique password that is not reused for any other account on the server.
$ sudo mkdir -p /var/www/example.com/public_html
$ cd /tmp $ curl -LO https://wordpress.org/latest.tar.gz $ tar -xzf latest.tar.gz
$ sudo rsync -a /tmp/wordpress/ /var/www/example.com/public_html/
The trailing slashes keep the contents of the extracted wordpress directory in the document root instead of nesting another wordpress directory under it.
$ sudo cp /var/www/example.com/public_html/wp-config-sample.php /var/www/example.com/public_html/wp-config.php $ sudo vi /var/www/example.com/public_html/wp-config.php
define( 'DB_NAME', 'wordpress' ); define( 'DB_USER', 'wordpress' ); define( 'DB_PASSWORD', 'strong_password_here' ); define( 'DB_HOST', 'localhost' );
Keep DB_HOST as localhost when MariaDB runs on the same host.
$ curl -s https://api.wordpress.org/secret-key/1.1/salt/
Paste the returned define(… ) lines over the default salt block before saving the configuration file.
$ sudo chown -R apache:apache /var/www/example.com/public_html
$ sudo find /var/www/example.com/public_html -type d -exec chmod 755 {} \\;
$ sudo find /var/www/example.com/public_html -type f -exec chmod 644 {} \\;
$ sudo chmod 640 /var/www/example.com/public_html/wp-config.php
$ sudo mkdir -p /var/www/example.com/public_html/wp-content/uploads $ getenforce Enforcing $ sudo dnf install -y policycoreutils-python-utils $ sudo semanage fcontext -a -t httpd_sys_rw_content_t '/var/www/example.com/public_html/wp-content/uploads(/.*)?' $ sudo restorecon -Rv /var/www/example.com/public_html/wp-content/uploads
Skip the labeling commands when getenforce returns Permissive or Disabled.
Without a writable SELinux context, the installer can succeed while media uploads later fail with unexplained permission errors.
$ sudo vi /etc/httpd/conf.d/example.com.conf
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/example.com/public_html
<Directory /var/www/example.com/public_html>
AllowOverride All
Require all granted
DirectoryIndex index.php
</Directory>
ErrorLog /var/log/httpd/example.com-error.log
CustomLog /var/log/httpd/example.com-access.log combined
</VirtualHost>
WordPress writes permalink rules to .htaccess, so AllowOverride All must stay enabled for the document root.
Replace the example hostnames with the real DNS names for the site before reloading Apache.
$ sudo httpd -t Syntax OK $ sudo systemctl reload httpd
Related: How to test Apache configuration
Related: How to manage the Apache web server service
$ curl -I http://www.example.com/wp-admin/install.php HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8
If this request fails, confirm the DNS record or local host mapping points to the server, the virtual host name matches the URL, and the php-fpm service is still active.
After the installer finishes, continue with HTTPS and XML-RPC hardening from the related pages.