Ravindra BagaleCourses & study guides

Chapter 11: Application Hosting — WordPress, Flask and Express

11.1 WordPress

Why and what

WordPress is an open-source content management system (CMS) written in PHP that uses MySQL/MariaDB. It powers a very large share of all websites — blogs, college department sites, small business and news portals. Hosting it yourself teaches the complete LAMP/LEMP workflow.

 Browser ─► Apache/Nginx :80 ─► PHP (mod_php / PHP-FPM) ─► WordPress PHP files (/var/www/wordpress)
                                                                    │
                                                                    ▼
                                                   MariaDB/MySQL database "wordpress"

Requirements: PHP 7.4+ (8.x recommended), MySQL 8.0+ or MariaDB 10.5+, a web server with URL rewriting, ~1 GB RAM (a t3.micro works for learning; add swap if needed).

Step 1 — Install the stack

Chala, aata WordPress install karuya. Choose your OS and follow along — don't skip any step.

Ubuntu (Apache):

sudo apt update
sudo apt install -y apache2 mysql-server php libapache2-mod-php php-mysql \
  php-curl php-gd php-mbstring php-xml php-intl php-zip php-soap wget unzip
sudo service apache2 start
sudo service mysql start
sudo systemctl enable apache2 mysql

Amazon Linux 2023 (Apache):

sudo yum install -y httpd mariadb105-server php php-fpm php-mysqlnd \
  php-gd php-mbstring php-xml php-intl php-opcache wget
sudo service httpd start
sudo service mariadb start
sudo service php-fpm start
sudo systemctl enable httpd mariadb php-fpm

CentOS Stream 9 (Apache):

sudo yum install -y httpd mariadb-server php php-fpm php-mysqlnd \
  php-gd php-mbstring php-xml php-intl php-opcache wget tar
sudo service httpd start
sudo service mariadb start
sudo service php-fpm start
sudo systemctl enable httpd mariadb php-fpm

Then secure the database: sudo mysql_secure_installation (see Chapter 10).

Step 2 — Create the WordPress database

sudo mysql <<'EOF'
CREATE DATABASE wordpress DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wpuser'@'localhost' IDENTIFIED BY 'Wp@Pass123';
GRANT ALL PRIVILEGES ON wordpress.* TO 'wpuser'@'localhost';
FLUSH PRIVILEGES;
EOF

Step 3 — Download and extract WordPress

cd /tmp
wget https://wordpress.org/latest.tar.gz
tar -xzf latest.tar.gz
sudo mv wordpress /var/www/wordpress
ls /var/www/wordpress

Step 4 — Configure wp-config.php

cd /var/www/wordpress
sudo cp wp-config-sample.php wp-config.php
sudo sed -i "s/database_name_here/wordpress/" wp-config.php
sudo sed -i "s/username_here/wpuser/" wp-config.php
sudo sed -i "s/password_here/Wp@Pass123/" wp-config.php
grep DB_ wp-config.php

Add unique security keys (salts). Get a fresh set from the official generator:

curl -s https://api.wordpress.org/secret-key/1.1/salt/
sudo nano /var/www/wordpress/wp-config.php

In nano, delete the eight lines that contain put your unique phrase here and paste the eight lines printed by curl. Save (Ctrl+O, Enter) and exit (Ctrl+X).

Step 5 — Ownership and permissions

# Ubuntu:
sudo chown -R www-data:www-data /var/www/wordpress
# Amazon Linux 2023 / CentOS Stream 9:
sudo chown -R apache:apache /var/www/wordpress

sudo find /var/www/wordpress -type d -exec chmod 755 {} \;
sudo find /var/www/wordpress -type f -exec chmod 644 {} \;
sudo chmod 640 /var/www/wordpress/wp-config.php

CentOS Stream 9 only — SELinux: allow WordPress to write uploads/plugins and to make outgoing connections (updates, plugin downloads):

sudo yum install -y policycoreutils-python-utils
sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/wordpress/wp-content(/.*)?"
sudo restorecon -Rv /var/www/wordpress
sudo setsebool -P httpd_can_network_connect 1

Step 6a — Apache virtual host

Ubuntu (/etc/apache2/sites-available/wordpress.conf):

sudo tee /etc/apache2/sites-available/wordpress.conf > /dev/null <<'EOF'
<VirtualHost *:80>
    ServerName blog.local
    DocumentRoot /var/www/wordpress
    <Directory /var/www/wordpress>
        AllowOverride All
        Require all granted
    </Directory>
    ErrorLog  ${APACHE_LOG_DIR}/wordpress_error.log
    CustomLog ${APACHE_LOG_DIR}/wordpress_access.log combined
</VirtualHost>
EOF
sudo a2ensite wordpress.conf
sudo a2dissite 000-default.conf
sudo a2enmod rewrite
sudo apache2ctl configtest && sudo service apache2 reload

Amazon Linux 2023 / CentOS Stream 9 (/etc/httpd/conf.d/wordpress.conf):

sudo tee /etc/httpd/conf.d/wordpress.conf > /dev/null <<'EOF'
<VirtualHost *:80>
    ServerName blog.local
    DocumentRoot /var/www/wordpress
    DirectoryIndex index.php index.html
    <Directory /var/www/wordpress>
        AllowOverride All
        Require all granted
    </Directory>
    ErrorLog  /var/log/httpd/wordpress_error.log
    CustomLog /var/log/httpd/wordpress_access.log combined
</VirtualHost>
EOF
sudo apachectl configtest && sudo service httpd reload

(mod_rewrite is enabled by default on AL2023/CentOS. AllowOverride All lets WordPress's .htaccess create pretty permalinks.)

Step 6b — Or use Nginx instead of Apache (LEMP)

Install nginx and PHP-FPM instead of Apache (sudo apt install -y nginx php-fpm ... on Ubuntu; stop/disable Apache first). Server block:

server {
    listen 80;
    server_name YOUR_SERVER_NAME;                 # public IP or domain
    root /var/www/wordpress;
    index index.php index.html;
    client_max_body_size 64M;                     # allow bigger media uploads

    location / {
        try_files $uri $uri/ /index.php?$args;    # pretty permalinks
    }
    location ~ \.php$ {
        try_files $uri =404;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/run/php-fpm/www.sock;  # Ubuntu: unix:/run/php/php-fpm.sock
    }
    location ~* \.(css|js|png|jpg|jpeg|gif|svg|webp|ico)$ {
        expires 30d;
        access_log off;
    }
    location ~ /\.ht { deny all; }
}

PHP-FPM user with Nginx

On AL2023/CentOS PHP-FPM runs as user apache by default (even with Nginx), so chown -R apache:apache is still correct. On Ubuntu both Nginx and PHP-FPM run as www-data.

Ravindra Bagale's Tip

Attach an Elastic IP before you open the WordPress installer. WordPress saves the site URL in its database, and if the public IP changes later, every link and CSS file points to the old IP. Two minutes of planning saves an hour of fixing.

Step 7 — Finish installation in the browser

Open http://<PUBLIC_IP>/ → choose language → enter Site Title, admin Username, strong Password, Email → Install WordPress → log in at http://<PUBLIC_IP>/wp-admin.

WordPress and changing IPs

WordPress stores the site URL in the database. If the public IP changes (stop/start without an Elastic IP), the site redirects to the old IP and breaks. Attach an Elastic IP before installing (see section 14.1), or use a domain (Chapter 9). To fix after the fact, add define('WP_HOME','http://NEW_IP'); define('WP_SITEURL','http://NEW_IP'); to wp-config.php.

WordPress hardening checklist

  • Strong admin password; don't use the username admin.
  • Keep WordPress core, themes and plugins updated; delete unused ones.
  • wp-config.php permission 640; disable file editing in the dashboard: add define('DISALLOW_FILE_EDIT', true);.
  • Point a domain to the server (Chapter 9) and enable HTTPS with certbot (sections 7.13 and 9.11).
  • Back up the database (mysqldump wordpress) and wp-content regularly; take EBS snapshots.
  • Increase PHP upload limits if needed: upload_max_filesize and post_max_size in /etc/php.ini (AL2023/CentOS) or /etc/php/8.x/apache2/php.ini / /etc/php/8.x/fpm/php.ini (Ubuntu), then restart PHP-FPM/Apache.