WordPress Installation & Configuration Guide

Complete NGINX Server Setup with Security Hardening

angelscript

Table of Contents

1. Overview 2. Download WordPress 3. Configure wp-config.php 4. Complete Installation 5. Security Hardening 6. Optimization 7. Maintenance Mode

1. Overview

This guide covers the complete process of installing WordPress on an NGINX server with PHP-FPM, including security hardening, optimization, and best practices for hosting additional domains.

Installation Process Flow

1Download WordPress
2Extract & Configure
3Copy to Document Root
4Set Permissions
5Complete Browser Installation
6Harden Security
7Optimize Performance

2. Download WordPress

2.1 Download Latest Version

Navigate to your home directory and download the latest WordPress package:

cd wget https://wordpress.org/latest.tar.gz ls
Info: The URL https://wordpress.org/latest.tar.gz always points to the most recent WordPress version.

2.2 Extract the Archive

Use the tar command to extract the downloaded file:

tar xf latest.tar.gz ls

This creates a directory called wordpress containing all WordPress files and directories.

2.3 Clean Up and Navigate

rm latest.tar.gz cd wordpress/ ls ls -l
Note: All files are initially owned by your user account, so no sudo is needed for editing at this stage.

3. Configure wp-config.php

3.1 Rename Configuration File

Rename the sample configuration file:

mv wp-config-sample.php wp-config.php ls

3.2 Edit Configuration

Open the configuration file in nano editor:

nano wp-config.php

3.3 Database Configuration

Update the following database settings (obtained from running the database creation script):

Setting Description Example
DB_NAME Database name example_com
DB_USER Database username jvJRsUEEhWwq
DB_PASSWORD Database password RandomGeneratedPass123
Important: When copying and pasting credentials, ensure you paste between the two apostrophes, preserving the exact format.

3.4 Security Keys (Salts)

Replace the placeholder salts with unique keys generated from the WordPress API:

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

Copy and paste the generated salts into wp-config.php, replacing the existing placeholder values.

3.5 Table Prefix

Change the default table prefix for security:

$table_prefix = 'wp_';

Change to a random prefix (generated from the database creation script):

$table_prefix = 'xyz4_';

3.6 WordPress Constants

Add the following constants before the "That's all, stop editing!" comment:

/** Allow Direct Updating Without FTP */

define('FS_METHOD', 'direct');

/** Disable Editing of Themes and Plugins Using the Built In Editor */
define('DISALLOW_FILE_EDIT', 'true');

/** TURN OFF AUTOMATIC UPDATES */
define('WP_AUTO_UPDATE_CORE', 'false');
angelscript
Configuration Complete: Save and close nano (Ctrl+X, then Y, then Enter).

4. Complete Installation

4.1 Copy Files to Document Root

Return to home directory and use rsync to copy files:

cd sudo rsync -artv wordpress/ /var/www/example.com/public_html/
Explanation of rsync flags:
  • -a : Archive mode (preserves permissions, timestamps, etc.)
  • -r : Recursive (copies directories and their contents)
  • -t : Preserves modification times
  • -v : Verbose output

4.2 Set Initial Permissions

Navigate to the site directory and set web server ownership:

cd /var/www/example.com/ ls -l sudo chown -R www-data:www-data public_html/ ls -l
cd public_html/ ls -l

4.3 Browser Installation

Complete the installation using your web browser:

  1. Navigate to: http://www.example.com or http://example.com
  2. Select language (English US)
  3. Click "Continue"
  4. Enter site title
  5. Enter admin username (use generated credentials)
  6. Enter admin password (use generated credentials)
  7. Enter your email address
  8. Click "Install WordPress"
  9. Save credentials to your browser if prompted
  10. Click "Log In"
Installation Complete! You will receive a confirmation message that your WordPress site has been successfully set up.

4.4 Initial Configuration

Settings → General

Verify the WordPress Address URL and Site Address URL. Note whether www is included or not.

Settings → Permalinks

Set to: Post name

Posts, Pages & Comments

Delete default content:

Plugins

Remove default plugins:

Themes

Keep only the active theme, delete unused themes.

Users → Profile

Settings → Discussion

Disable comments site-wide:

Note: For existing posts, edit each post individually and close comments in the Discussion section.

5. Security Hardening

5.1 PHP-FPM Pool Configuration

Create Dedicated User

sudo useradd username sudo usermod -a -G username www-data sudo usermod -a -G www-data username sudo usermod -a -G $USER username

Create Pool Configuration

cd /etc/php/8.3/fpm/pool.d/ ls sudo cp www.conf example.com.conf sudo nano example.com.conf

Pool Configuration Settings

; pool name

[example]

; Username and Group
user = username
group = username

; Path and Unix Socket Filename - unique to this pool
listen = /run/php/php8.3-fpm-example.com.sock

; Set open file descriptor rlimit.
rlimit_files = 15000

; Set max core size rlimit.
rlimit_core = 100

; PHP POOL CONFIGURATION
php_flag[display_errors] = off
php_admin_value[error_log] = /var/log/fpm-php.example.log
php_admin_flag[log_errors] = on
angelscript

Create Log File

sudo touch /var/log/fpm-php.example.log sudo chown username:www-data /var/log/fpm-php.example.log sudo chmod 660 /var/log/fpm-php.example.log

5.2 NGINX Configuration

Update Server Block

sudo nano /etc/nginx/sites-available/example.com.conf

Modify the fastcgi_pass directive to use the new socket:

fastcgi_pass unix:/run/php/php8.3-fpm-example.com.sock;

Test and Reload

sudo nginx -t sudo systemctl reload nginx sudo systemctl reload php8.3-fpm

5.3 Ownership and Permissions

Standard Permissions (Development)

cd /var/www/example.com/ sudo chown -R username:username public_html/ tmp/ sudo chmod 770 public_html/ sudo chmod 770 tmp/ sudo find /var/www/example.com/public_html/ -type d -exec chmod 770 {} \; sudo find /var/www/example.com/public_html/ -type f -exec chmod 660 {} \; ls -l

Hardened Permissions (Production)

sudo find /var/www/example.com/public_html/ -type d -exec chmod 550 {} \; sudo find /var/www/example.com/public_html/ -type f -exec chmod 440 {} \; sudo find /var/www/example.com/public_html/wp-content/ -type d -exec chmod 770 {} \; sudo find /var/www/example.com/public_html/wp-content/ -type f -exec chmod 660 {} \;
Permission Explanation:
  • 550 - Read and execute for owner/group (directories)
  • 440 - Read-only for owner/group (files)
  • 770 - Read, write, execute for owner/group (wp-content directories)
  • 660 - Read and write for owner/group (wp-content files)

5.4 Disable PHP Functions

Add to pool configuration to disable dangerous PHP functions:

; ENABLED FUNCTIONS

; disk_free_space

; DISABLED FUNCTIONS
php_admin_value[disable_functions] = shell_exec, opcache_get_configuration, opcache_get_status, disk_total_space, diskfreespace, dl, exec, passthru, pclose, pcntl_alarm, pcntl_exec, pcntl_fork, pcntl_get_last_error, pcntl_getpriority, pcntl_setpriority, pcntl_signal, pcntl_signal_dispatch, pcntl_sigprocmask, pcntl_sigtimedwait, pcntl_sigwaitinfo, pcntl_strerror, pcntl_waitpid, pcntl_wait, pcntl_wexitstatus, pcntl_wifcontinued, pcntl_wifexited, pcntl_wifsignaled, pcntl_wifstopped, pcntl_wstopsig, pcntl_wtermsig, popen, posix_getpwuid, posix_kill, posix_mkfifo, posix_setpgid, posix_setsid, posix_setuid, posix_uname, proc_close, proc_get_status, proc_nice, proc_open, proc_terminate, show_source, system
angelscript
sudo systemctl reload php8.3-fpm

5.5 Open Basedir Restriction

Add to pool configuration:

php_admin_value[upload_tmp_dir] = /var/www/example.com/tmp/

php_admin_value[sys_temp_dir] = /var/www/example.com/tmp/
php_admin_value[open_basedir] = /var/www/example.com/public_html/:/var/www/example.com/tmp/
angelscript

5.6 SSL/TLS Configuration

Obtain SSL Certificate

sudo certbot certonly --webroot -w /var/www/example.com/public_html/ -d example.com -d www.example.com

Create SSL Configuration File

sudo nano /etc/nginx/ssl/ssl_certs_example.com.conf
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; 

ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

SSL STAPLING
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

Update Server Block for HTTPS

server {
listen 80;
server_name example.com www.example.com;

# 301 Permanent Redirect to HTTPS
return 301 https://example.com$request_uri;

}

server {
listen 443 ssl;
http2 on;

gradle
listen 443 quic;
http3 on;

server_name example.com www.example.com;

root /var/www/example.com/public_html;
index index.php;

# Include SSL configurations
include /etc/nginx/ssl/ssl_certs_example.com.conf;
include /etc/nginx/ssl/ssl_all_sites.conf;

# ... rest of configuration

}
angelscript

Test and Reload

sudo nginx -t sudo systemctl reload nginx

Verify Redirects

curl -I http://example.com curl -I http://www.example.com curl -I https://www.example.com curl -I https://example.com
Test Your SSL Configuration:

5.7 Security Headers

Add HTTP security headers by including the configuration file:

# Place ABOVE the php processing location block

include /etc/nginx/includes/http_headers.conf;

5.8 WordPress Security Directives

# Place ABOVE the php processing location block

include /etc/nginx/includes/wp_security.conf;
angelscript

5.9 Rate Limiting

Create Rate Limiting Configuration

cd /etc/nginx/includes/ sudo cp rate_limiting_example.com.conf rate_limiting_example.net.conf sudo nano rate_limiting_example.net.conf
location = /wp-login.php {
limit_req zone=wp burst=20 nodelay;
limit_req_status 444;
include snippets/fastcgi-php.conf;
fastcgi_param HTTP_HOST $host;
fastcgi_pass unix:/run/php/php8.3-fpm-example.com.sock;
include /etc/nginx/includes/fastcgi_optimize.conf;

}

location = /xmlrpc.php {
limit_req zone=wp burst=20 nodelay;
limit_req_status 444;
include snippets/fastcgi-php.conf;
fastcgi_param HTTP_HOST $host;
fastcgi_pass unix:/run/php/php8.3-fpm-example.com.sock;
include /etc/nginx/includes/fastcgi_optimize.conf;
}

Include in server block:

# Rate Limiting Include

include /etc/nginx/includes/rate_limiting_example.net.conf;
angelscript

5.10 Database Privileges Hardening

Check Database Credentials

sudo grep DB_NAME /var/www/example.com/public_html/wp-config.php sudo grep DB_USER /var/www/example.com/public_html/wp-config.php

Revoke and Grant Limited Privileges

REVOKE ALL PRIVILEGES ON database_name.* FROM 'username'@'localhost';

GRANT SELECT, INSERT, UPDATE, DELETE ON database_name.* TO 'username'@'localhost';
FLUSH PRIVILEGES;
angelscript
Important: This removes dangerous privileges like DROP, CREATE, and ALTER. The database user can only read and modify data, not structure.

6. Optimization

6.1 WordPress Post Revisions

Add to wp-config.php to disable post revisions:

define('WP_POST_REVISIONS', 'false');

6.2 WordPress Memory Limit

Update PHP Pool Configuration

cd /etc/php/8.3/fpm/pool.d/ sudo nano example.com.conf

Change memory_limit from 32M to 256M:

php_admin_value[memory_limit] = 256M

Update wp-config.php

/** MEMORY LIMIT */

define('WP_MEMORY_LIMIT', '256M');

6.3 Disable WP-Cron

Add to wp-config.php

/** DISABLE WP-CRON */

define('DISABLE_WP_CRON', true);
angelscript

Setup System Cron

crontab -e

Add the following line to run every 15 minutes:

*/15 * * * * wget -q -O - https://example.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1

6.4 OPcache Configuration

Development Server Settings

; OPCACHE CONFIGURATION - DEVELOPMENT SERVER

php_admin_value[opcache.memory_consumption] = 256
php_admin_value[opcache.interned_strings_buffer] = 32
php_admin_value[opcache.max_accelerated_files] = 20000
php_admin_flag[opcache.validate_timestamps] = 1
php_admin_value[opcache.revalidate_freq] = 2
php_admin_flag[opcache.validate_permission] = 1

Production Server Settings

; OPCACHE CONFIGURATION - PRODUCTION SERVER

php_admin_value[opcache.memory_consumption] = 256
php_admin_value[opcache.interned_strings_buffer] = 32
php_admin_value[opcache.max_accelerated_files] = 20000
php_admin_flag[opcache.validate_timestamps] = 0
php_admin_flag[opcache.validate_permission] = 1
angelscript
Key Difference: Production sets validate_timestamps to 0, which disables checking for file changes on every request, improving performance significantly.

6.5 FastCGI Caching

Add to nginx.conf

cd /etc/nginx sudo nano nginx.conf
### FASTCGI CACHING

fastcgi_cache_path directive - PATH & NAME must be unique for each site
fastcgi_cache_path /var/run/SITE levels=1:2 keys_zone=NAME:100m inactive=60m;
fastcgi_cache_path /var/run/SITE2 levels=1:2 keys_zone=NAME2:100m inactive=60m;

Applied to all sites
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout invalid_header http_500;
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
angelscript

Update Server Block

cd /etc/nginx/sites-available/ sudo nano example.com.conf
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_param HTTP_HOST $host;
fastcgi_pass unix:/run/php/php8.3-fpm-example.com.sock;
include /etc/nginx/includes/fastcgi_optimize.conf;

# FastCGI caching directives
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache NAME;
fastcgi_cache_valid 60m;

}

include /etc/nginx/includes/fastcgi_cache_excludes.conf;
add_header X-FastCGI-Cache $upstream_cache_status;

Cache purge location
location ~ /purge(/.*) {
fastcgi_cache_purge NAME "$scheme$request_method$host$1";
}
angelscript
Verification: Check response headers for X-FastCGI-Cache values:
  • HIT - Content served from cache
  • MISS - Content not in cache, will be cached
  • BYPASS - Cache bypassed for this request

7. Maintenance Mode

7.1 Install Maintenance Mode Plugin

  1. Navigate to: Plugins → Add New Plugin
  2. Search for "Maintenance Mode"
  3. Click "Install Now"
  4. Click "Activate"

7.2 Configure Plugin

  1. Go to Settings → Maintenance Mode
  2. Remove background image (optional)
  3. Disable front-end login form
  4. Save changes
Site Setup Complete! Your WordPress installation is now secured, optimized, and ready for use. Remember to regularly update WordPress core, themes, and plugins, and maintain backups.

8. Permission Management Scripts

8.1 Loosen Permissions Script

Use before updating WordPress, themes, or plugins through the dashboard:

cd ~/wp_bash_scripts/ bash 5.loosen_permissions.sh

8.2 Tighten Permissions Script

Use after completing updates to restore security:

cd ~/wp_bash_scripts/ bash 6.tighten_permissions.sh
Important: Always tighten permissions after making changes through the WordPress dashboard to maintain security.

9. Monitoring Scripts

9.1 Pool Memory Usage

Monitor memory usage by PHP-FPM pool:

bash pool_list_usage.sh

9.2 OPcache File Count

Check OPcache configuration and PHP file counts:

bash opcache_files.sh
Optimization Tip: Ensure opcache.max_accelerated_files is higher than the number of PHP files in your WordPress installation for optimal performance.

10. Best Practices Summary

Category Best Practice Benefit
Security Use dedicated PHP-FPM pools Site isolation and security
Security Harden file permissions Prevent unauthorized modifications
Security Limit database privileges Reduce impact of SQL injection
Security Implement rate limiting Prevent brute force attacks
Performance Enable OPcache Faster PHP execution
Performance Configure FastCGI caching Reduced server load
Performance Use HTTP/2 and HTTP/3 Faster page loads
Maintenance Disable automatic updates Control update timing
Maintenance Use system cron Reliable scheduled tasks

Conclusion

This comprehensive guide covers the complete process of installing and securing WordPress on an NGINX server with PHP-FPM. By following these steps, you've created a high-performance, secure WordPress installation with proper isolation and optimization.

Key Achievements:
  • ✓ WordPress installed with secure configuration
  • ✓ Dedicated PHP-FPM pool for site isolation
  • ✓ SSL/TLS encryption with HTTP/2 and HTTP/3
  • ✓ Hardened file permissions
  • ✓ Database privilege restrictions
  • ✓ Performance optimization with caching
  • ✓ Rate limiting for security
  • ✓ Automated maintenance scripts

Remember to regularly backup your site, monitor performance, and keep all software updated for optimal security and performance.