1. NGINX Logs Management
NGINX logs are essential for monitoring server activity, troubleshooting issues, and analyzing traffic
patterns. Logs are typically stored in the /var/log/nginx directory.
Viewing Log Files
Navigate to the NGINX logs directory:
cd /var/log/nginx
List all log files:
ls
View the entire log file:
sudo cat log_file_name.log
View log file with pagination (recommended for large files):
sudo less log_file_name.log
Tip: Use less for large log files as it allows you to navigate through the
file efficiently. Press 'q' to quit, 'Space' to scroll down, and '/' to search.
2. PHP-FPM Pool Configuration
PHP-FPM (FastCGI Process Manager) pools allow you to run multiple PHP applications with different
configurations and security settings on the same server.
PHP-FPM Pool Architecture
NGINX Web Server
↓
PHP-FPM Master Process
↓
Pool 1 (example.com)
Pool 2 (site2.com)
Pool 3 (site3.com)
User and Group Management
Create a new user for the PHP pool:
sudo useradd username
Add users to groups (multiple variations):
sudo usermod -a -G username_group www-data
sudo usermod -a -G www-data username_group
sudo usermod -a -G username_group $USER
Creating a New PHP Pool
Navigate to the PHP-FPM pool directory:
cd /etc/php/8.3/pool.d/
List existing pools:
ls
Copy the default pool configuration:
sudo cp www.conf example.com.conf
Edit the new pool configuration:
sudo nano example.com.conf
Pool Configuration File
; Start a new pool named 'example'
[example]
; Unix user/group of the child processes
user = username
group = username
; The address on which to accept FastCGI requests
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 configuration flags
php_flag[display_errors] = off
php_admin_value[error_log] = /var/log/fpm-php.www.log
php_admin_flag[log_errors] = on
;php_admin_value[memory_limit] = 32M
angelscript
Creating the FPM Log File
Create the log file:
sudo touch /var/log/fpm-php.example.com.log
Set ownership:
sudo chown user:www-data /var/log/fpm-php.example.com.log
Set permissions:
sudo chmod 660 /var/log/fpm-php.example.com.log
Enabling the New Pool
Reload PHP-FPM to apply changes:
sudo systemctl reload php8.3-fpm
Verify the socket configuration:
sudo grep "listen = /" example.conf
Configuring NGINX to Use the Pool
Edit your NGINX site configuration:
sudo nano /etc/nginx/sites-available/example.com.conf
Update the FastCGI pass directive:
fastcgi_pass unix:/run/php/php8.3-fpm-example.com.sock;
Test NGINX configuration:
sudo nginx -t
Reload NGINX:
sudo systemctl reload nginx
Setting File Permissions
Set ownership recursively:
sudo chown -R username:username public_html/
Set directory permissions (770):
sudo find /var/www/example.com/public_html/ -type d -exec chmod 770 {} \;
List files to verify:
ls -l
Set file permissions (660):
sudo find /var/www/example.com/public_html/ -type f -exec chmod 660 {} \;
Testing PHP Configuration
Create a PHP info file:
cd public_html/
sudo nano public_html/filename.php
<?php
phpinfo();
?>
angelscript
PHP Configuration Options
Enable URL file opening:
php_admin_flag[allow_url_fopen] = on
Reload PHP-FPM after changes:
sudo systemctl reload php8.3-fpm
Disabling Dangerous 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
3. Open Base Dir Security Configuration
Security Notice: Open Base Dir is a critical security measure that restricts PHP file
access to specific directories, preventing unauthorized access to sensitive system files.
Understanding Open Base Dir
Open Base Dir is a PHP security feature that:
- Restricts file access to specific directory trees
- Prevents PHP scripts from accessing files outside designated areas
- Reduces risks of unauthorized access and exploitation
- Is essential for shared hosting environments
Open Base Dir Access Control
✓ Allowed Directories
- /var/www/example.com/public_html/
- /var/www/example.com/tmp/
✗ Blocked Directories
- /etc/
- /tmp/
- /var/log/
- Other system directories
The Problem with /tmp Directory
Security Risk: Including the system /tmp directory in Open Base Dir can
expose sensitive information and allow malicious code execution.
Risks of Including System /tmp:
- Exposure of temporary files from other system processes
- Potential for arbitrary code execution through uploaded files
- Risk of privilege escalation attacks
- Possible server compromise or data loss
The Solution: Custom Temporary Directories
Create site-specific temporary directories instead of using the system /tmp:
Step 1: Create Custom tmp Directory
Navigate to your site directory:
cd /var/www/example.com/
Check current directory structure:
ls -l
Create the tmp directory:
sudo mkdir tmp/
Set ownership:
sudo chown username:username tmp/
Set permissions:
sudo chmod 770 tmp/
Verify the changes:
ls -l
Step 2: Configure PHP Upload and Temp Directories
Navigate to PHP pool configuration:
cd /etc/php/8.3/fpm/pool.d/
Edit your site's pool configuration:
sudo nano example.com.conf
Add the following directives at the end of the file:
; Custom upload and temporary directories
php_admin_value[upload_tmp_dir] = /var/www/example.com/tmp/
php_admin_value[sys_temp_dir] = /var/www/example.com/tmp/
; Open Base Dir restriction
php_admin_value[open_basedir] = /var/www/example.com/public_html/:/var/www/example.com/tmp/
angelscript
Configuration Explanation:
upload_tmp_dir: Specifies where PHP stores uploaded files temporarily
sys_temp_dir: Defines where PHP stores temporary files during script execution
open_basedir: Restricts PHP access to only the specified directories (separated by
colons)
Step 3: Apply Configuration
Reload PHP-FPM to enable the changes:
sudo systemctl reload php8.3-fpm
Performance Considerations
Potential Overhead
- Additional file system checks
- Slight performance impact on file operations
- Increased CPU usage on high-traffic sites
Mitigation Strategies
- Implement caching mechanisms
- Use opcode caching (OPcache)
- Optimize file operations
- Security benefits outweigh minimal overhead
Testing the Configuration
Testing Steps:
- Log into your WordPress dashboard
- Navigate to Media → Add New
- Upload an image file
- Verify successful upload to Media Library
- Test WordPress core updates
4. SSL Certificate Configuration
SSL/TLS certificates encrypt data between the server and client, ensuring secure communication. Let's
Encrypt provides free SSL certificates.
Installing Certbot
Update package lists:
sudo apt update
Install Certbot with Cloudflare DNS plugin:
sudo apt install certbot python3-certbot-dns-cloudflare
Obtaining SSL Certificates
Using webroot authentication:
sudo certbot certonly --webroot -w /var/www/example.com/public_html/ -d example.com -d
www.example.com
Generating Diffie-Hellman Parameters
Create SSL directory:
cd /etc/nginx/
sudo mkdir ssl/
Navigate to SSL directory:
cd ssl/
Generate DH parameters (this may take several minutes):
sudo openssl dhparam -out dhparam.pem 2048
Verify creation:
ls
Creating SSL Configuration Files
Site-Specific SSL Certificate Configuration
Create certificate 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_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
angelscript
Global SSL Configuration
Create global SSL settings file:
sudo nano /etc/nginx/ssl/ssl_all_sites.conf
# CONFIGURATION RESULTS IN A+ RATING AT SSLLABS.COM
LAST UPDATED: MAY 2025
SSL CACHING AND PROTOCOLS
ssl_session_cache shared:SSL:20m;
ssl_session_timeout 180m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
Cipher suite - must be on a single line
ssl_ciphers
ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
SSL STAPLING DISABLED (Let's Encrypt removed support May 2025)
#ssl_stapling on;
#ssl_stapling_verify on;
DNS resolver set to Cloudflare
resolver 1.1.1.1 1.0.0.1;
resolver_timeout 15s;
ssl_session_tickets off;
HSTS HEADERS
add_header Strict-Transport-Security "max-age=31536000;" always;
After setting up ALL subdomains, use this instead:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains;" always;
Enable QUIC and HTTP/3
ssl_early_data on;
add_header Alt-Svc 'h3=":$server_port"; ma=86400';
add_header x-quic 'H3';
quic_retry on;
angelscript
Important: The ssl_ciphers directive must be on a single line. Do not
split it across multiple lines.
5. HTTP/3 and QUIC Setup
What is HTTP/3? HTTP/3 is the latest version of the HTTP protocol, built on top of QUIC
(Quick UDP Internet Connections). It provides faster, more reliable connections, especially on unstable
networks.
Configuring NGINX Server Blocks
HTTP to HTTPS Redirect
server {
listen 80;
server_name example.com www.example.com;
# Permanent redirect to HTTPS
return 301 https://example.com$request_uri;
}
pgsql
Primary HTTPS Server Block (with reuseport)
server {
# Standard HTTPS with HTTP/2
listen 443 ssl;
http2 on;
# QUIC and HTTP/3 with reuseport (first server only)
listen 443 quic reuseport;
http3 on;
server_name example.com www.example.com;
# Include SSL certificate configuration
include /etc/nginx/ssl/ssl_certs_example.com.conf;
include /etc/nginx/ssl/ssl_all_sites.conf;
# Rest of your server configuration...
}
pgsql
Additional HTTPS Server Blocks (without reuseport)
server {
# Standard HTTPS with HTTP/2
listen 443 ssl;
http2 on;
# QUIC and HTTP/3 WITHOUT reuseport
listen 443 quic;
http3 on;
server_name site2.example.com;
# Include SSL configuration
include /etc/nginx/ssl/ssl_certs_site2.example.com.conf;
include /etc/nginx/ssl/ssl_all_sites.conf;
# Rest of your server configuration...
}
angelscript
Important: Use reuseport on only ONE server block. Additional server
blocks should NOT include the reuseport parameter.
Testing Configuration
Test NGINX configuration:
sudo nginx -t
Reload NGINX:
sudo systemctl reload nginx
Testing HTTP/3 Functionality
Disable Browser Caching for Testing
Add this directive temporarily to your location block:
# PREVENTS BROWSER CACHING - USED TO TEST HTTP3
REMOVE THIS DIRECTIVE AFTER CONFIRMING HTTP3 IS ENABLED
add_header Cache-Control 'no-cache,no-store';
angelscript
Command Line Testing
Test HTTP redirect:
curl -I http://example.com
Test HTTPS with www:
curl -I https://www.example.com
Test HTTPS without www:
curl -I https://example.com
Browser Testing
Steps to verify HTTP/3 in browser:
- Open Developer Console (F12)
- Go to Network tab
- Refresh the page
- Right-click on column headers
- Select "Protocol" to add the Protocol column
- Look for "h3" in the Protocol column
Additional Testing Tools
Online HTTP/3 testing tools:
Adding FastCGI Host Parameter
In your PHP processing location block, add:
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_param HTTP_HOST $host;
fastcgi_pass unix:/run/php/php8.3-fpm-example.sock;
include /etc/nginx/includes/fastcgi_optimize.conf;
}
angelscript
6. SSL Certificate Management
Certbot Commands
View All Certificates
sudo certbot certificates
Delete a Certificate
sudo certbot delete
Renew All Certificates
sudo certbot renew
Force Certificate Renewal
sudo certbot renew --force-renewal
Automated Certificate Renewal
Set up automatic renewal using cron:
sudo crontab -e
Add the following lines to renew certificates twice monthly:
# Renew SSL certificates on the 14th and 28th of each month at 1:00 AM
00 1 14,28 * * certbot renew --force-renewal
Reload NGINX on the 14th and 28th of each month at 2:00 AM
00 2 14,28 * * systemctl reload nginx
angelscript
Cron Format Explanation:
00 - Minute (0)
1 or 2 - Hour (1 AM or 2 AM)
14,28 - Day of month (14th and 28th)
* - Every month
* - Any day of week
8. File Ownership and Permissions
Permission Models Comparison
Standard Permissions
Best for: Development environments
- Directories: 770 (rwxrwx---)
- Files: 660 (rw-rw----)
- More flexible for updates
Hardened Permissions
Best for: Production environments
- Directories: 550 (r-xr-x---)
- Files: 440 (r--r-----)
- Maximum security
- wp-content remains writable
Standard Permissions (Development)
Navigate to site directory:
cd /var/www/example.com/
Set ownership:
sudo chown -R username:username public_html/
Set directory permissions (770):
sudo find /var/www/example.com/public_html/ -type d -exec chmod 770 {} \;
Set file permissions (660):
sudo find /var/www/example.com/public_html/ -type f -exec chmod 660 {} \;
Secure wp-config.php:
sudo chmod 400 public_html/wp-config.php
Hardened Permissions (Production)
Navigate to site directory:
cd /var/www/example.com/
Set ownership:
sudo chown -R username:username public_html/
Set read-only directory permissions (550):
sudo find /var/www/example.com/public_html/ -type d -exec chmod 550 {} \;
Set read-only file permissions (440):
sudo find /var/www/example.com/public_html/ -type f -exec chmod 440 {} \;
Make wp-content writable for uploads/updates:
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 Values Explained
| Value |
Binary |
Permissions |
Description |
| 7 |
111 |
rwx |
Read, Write, Execute |
| 6 |
110 |
rw- |
Read, Write |
| 5 |
101 |
r-x |
Read, Execute |
| 4 |
100 |
r-- |
Read only |
| 0 |
000 |
--- |
No permissions |
9. NGINX Security Directives (WordPress Firewall)
WordPress-Safe Firewall: This ruleset is based on the 8G Firewall with low
false-positive risks, updated for December 2026.
Creating Security Directives File
Navigate to includes directory:
cd /etc/nginx/includes/
Create security directives file:
sudo nano nginx_security_directives.conf
# WORDPRESS-SAFE NGINX 8G (based) FIREWALL Ruleset
Low false-positive risks
Updated December 2026
Disable favicon logging
location = /favicon.ico {
access_log off;
log_not_found off;
}
Deny access to sensitive core and config files
location = /wp-config.php { deny all; }
location = /wp-admin/install.php { deny all; }
location ~* ^/(readme|license|licence).(txt|html)$ { deny all; }
location ~* .ini$ { deny all; }
Harden WP core
location ~* ^/wp-includes/[^/]+.php$ { deny all; }
location ~* ^/wp-includes/js/tinymce/langs/.+.php$ { deny all; }
location ~* ^/wp-includes/theme-compat/ { deny all; }
Prevent PHP execution in uploads, themes, and plugins
location ~* ^/wp-content/uploads/..(php[1-8]?|pht|phtml?|phps)$ { deny all; }
location ~ ^/wp-content/plugins/..(php[1-8]?|pht|phtml?|phps)$ { deny all; }
location ~ ^/wp-content/themes/.*.(php[1-8]?|pht|phtml?|phps)$ { deny all; }
Protect upgrade and backup directories
location ~* ^/wp-content/(upgrade|backup-.)/..(php[1-8]?|pht|phtml?|phps)$ { deny all; }
Block development and dependency files/directories
location ~* (composer.(json|lock)|package.json|yarn.lock|/vendor/|/node_modules/) { deny all; }
Block dangerous or unused HTTP methods
if ($request_method ~* ^(TRACE|DELETE|TRACK)$) {
return 403;
}
Block known vulnerability scanners
if ($http_user_agent ~* (nikto|sqlmap|masscan|nmap|dirbuster|acunetix|openvas)) {
return 444;
}
angelscript
Including Security Directives
Edit your site configuration:
cd /etc/nginx/sites-available/
sudo nano example.com.conf
Add ABOVE your PHP processing location block:
include /etc/nginx/includes/nginx_security_directives.conf;
Test and reload:
sudo nginx -t
sudo systemctl reload nginx
Allowing PHP Execution for Specific Plugins
Special Case: Some plugins require PHP execution in the plugins directory. By default,
this is blocked for security.
Testing Blocked Execution
Create a test PHP file:
cd /var/www/example.com/
sudo nano public_html/wp-content/plugins/test556.php
<?php
phpinfo();
?>
vim
Access in browser: https://example.com/wp-content/plugins/test556.php
Result: 403 Forbidden (PHP execution blocked)
Allowing Specific Plugin PHP Execution
If a plugin needs to execute PHP, add a specific location block AFTER your main PHP processing block:
location = /wp-content/plugins/test556.php {
allow all;
include snippets/fastcgi-php.conf;
fastcgi_param HTTP_HOST $host;
fastcgi_pass unix:/run/php/php8.3-fpm-example.sock;
include /etc/nginx/includes/fastcgi_optimize.conf;
}
angelscript
Test and reload:
sudo nginx -t
sudo systemctl reload nginx && sudo systemctl restart php8.3-fpm
Clean up test file:
cd /var/www/example.com/public_html/wp-content/plugins/
sudo rm test556.php
Best Practice: Only whitelist specific PHP files that absolutely require execution.
This maintains maximum security while allowing necessary functionality.
10. Rate Limiting
Rate limiting protects your server from brute force attacks and excessive requests to sensitive
endpoints.
Rate Limiting Flow
Client Request
↓
NGINX Rate Limit Zone Check
↓
Within Limit → Allow
Exceeds Limit → Block (444)
Configuring Rate Limit Zone
Edit main NGINX configuration:
cd /etc/nginx/
sudo nano nginx.conf
Add in the http context (before server blocks):
##
Rate Limiting
Zone: wp | Size: 10MB | Rate: 30 requests per minute
limit_req_zone $binary_remote_addr zone=wp:10m rate=30r/m;
angelscript
Parameters Explained:
$binary_remote_addr: Client IP address (binary format for efficiency)
zone=wp:10m: Memory zone named "wp" with 10MB allocation
rate=30r/m: Maximum 30 requests per minute per IP
Creating Site-Specific Rate Limiting
Navigate to includes directory:
cd /etc/nginx/includes/
Create rate limiting configuration:
sudo nano rate_limiting_example.com.conf
# Rate limiting for wp-login.php (prevent brute force)
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.sock;
include /etc/nginx/includes/fastcgi_optimize.conf;
}
Rate limiting for xmlrpc.php (prevent DDoS)
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.sock;
include /etc/nginx/includes/fastcgi_optimize.conf;
}
angelscript
Important: Replace unix:/run/php/php8.3-fpm-example.sock with your actual
PHP-FPM socket path.
Rate Limiting Parameters Explained
| Parameter |
Description |
Value |
| limit_req zone |
References the rate limit zone |
wp |
| burst |
Allows temporary burst of requests |
20 requests |
| nodelay |
Process burst requests immediately |
- |
| limit_req_status |
HTTP status code for blocked requests |
444 (connection closed) |
Including Rate Limiting in Site Configuration
Edit your site configuration:
sudo nano /etc/nginx/sites-available/example.com.conf
Add inside your server block:
# Rate Limiting Include
include /etc/nginx/includes/rate_limiting_example.com.conf;
angelscript
Test and reload:
sudo nginx -t
sudo systemctl reload nginx
Why Rate Limit These Endpoints?
wp-login.php
- Primary target for brute force attacks
- Unlimited attempts can compromise site
- Slows down automated attack tools
xmlrpc.php
- Used for DDoS amplification attacks
- Allows multiple authentication attempts
- Can consume server resources
11. Additional WordPress Security
Disabling File Modifications from Dashboard
For maximum security, prevent file modifications through the WordPress dashboard:
Edit wp-config.php:
sudo nano /var/www/example.com/public_html/wp-config.php
Add this line before "That's all, stop editing!":
define('DISALLOW_FILE_MODS', true);
Reload PHP-FPM:
sudo systemctl reload php8.3-fpm
What this does: Disables theme/plugin installation, updates, and editing through the
WordPress admin panel. All changes must be done via command line or SFTP.
12. Database Security and Privileges
Proper database privilege management is crucial for security. Apply the principle of least privilege.
Recommended Database Privileges
Revoke All Existing Privileges
REVOKE ALL PRIVILEGES ON site_db.* FROM 'site_user'@'hostname';
Grant Minimum Required Privileges
GRANT SELECT, INSERT, UPDATE, DELETE ON site_db.* TO 'site_user'@'hostname';
Apply changes:
FLUSH PRIVILEGES;
Additional Privileges (When Needed)
For plugins that require table creation or modification:
GRANT CREATE, ALTER, INDEX ON database_name.* TO 'username'@'localhost';
Security Note: Only grant CREATE, ALTER, and INDEX privileges temporarily when
installing/updating plugins that require them. Revoke these privileges afterward.
Database Privilege Levels Explained
| Privilege |
Purpose |
Required? |
| SELECT |
Read data from tables |
✓ Always |
| INSERT |
Add new rows to tables |
✓ Always |
| UPDATE |
Modify existing data |
✓ Always |
| DELETE |
Remove rows from tables |
✓ Always |
| CREATE |
Create new tables |
Only during installation/updates |
| ALTER |
Modify table structure |
Only during installation/updates |
| INDEX |
Create/delete indexes |
Only during installation/updates |
| DROP |
Delete tables |
✗ Not recommended |
Best Practices
Production Sites
- Grant only SELECT, INSERT, UPDATE, DELETE
- Use separate admin user for maintenance
- Regularly audit database privileges
Development Sites
- Can include CREATE, ALTER, INDEX
- Still avoid DROP privilege
- Use different credentials than production
13. Quick Reference Commands
Service Management
sudo systemctl reload nginx
sudo systemctl restart nginx
sudo systemctl reload php8.3-fpm
sudo systemctl restart php8.3-fpm
sudo systemctl status nginx
sudo systemctl status php8.3-fpm
Testing and Validation
sudo nginx -t
curl -I https://example.com
sudo certbot certificates
Log Monitoring
sudo tail -f /var/log/nginx/error.log
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/fpm-php.example.com.log
Permissions Quick Commands
sudo chown -R username:username /var/www/example.com/public_html/
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 {} \;
14. Security Checklist
✓ Configuration Checklist
- [ ] PHP-FPM pools configured per site
- [ ] Open Base Dir configured with custom tmp directory
- [ ] SSL certificates installed and auto-renewal configured
- [ ] HTTP/3 and QUIC enabled
- [ ] Security headers configured
- [ ] File permissions set (hardened for production)
- [ ] WordPress security directives implemented
- [ ] Rate limiting configured for login and XMLRPC
- [ ] Database privileges minimized
- [ ] DISALLOW_FILE_MODS enabled in wp-config.php
- [ ] wp-config.php permissions set to 400
- [ ] Dangerous PHP functions disabled
- [ ] SSL Labs rating: A+
- [ ] HTTP/3 verified as working
15. Troubleshooting Common Issues
502 Bad Gateway
- Check PHP-FPM is running
- Verify socket path in NGINX config
- Check PHP-FPM error logs
- Ensure correct pool user/group
403 Forbidden
- Check file/directory permissions
- Verify ownership settings
- Review security directives
- Check Open Base Dir configuration
File Upload Failures
- Verify custom tmp directory exists
- Check tmp directory permissions (770)
- Confirm upload_tmp_dir in pool config
- Review wp-content permissions
Rate Limiting Issues
- Adjust burst value if needed
- Check rate limit zone in nginx.conf
- Review access logs for 444 status
- Consider whitelisting admin IPs
16. Performance Optimization Tips
NGINX Optimization
- Enable Gzip compression for text files
- Configure browser caching with appropriate expiry times
- Use FastCGI caching for dynamic content
- Enable HTTP/2 and HTTP/3 for faster connections
- Optimize worker processes and connections
PHP-FPM Optimization
- Enable OPcache for PHP script caching
- Configure appropriate pm.max_children based on RAM
- Use pm = dynamic for better resource utilization
- Set reasonable memory_limit values
- Monitor and adjust pool settings based on traffic
WordPress Optimization
- Use a caching plugin (WP Super Cache, W3 Total Cache)
- Optimize database tables regularly
- Minimize plugins to essential ones only
- Use a CDN for static assets
- Optimize images before uploading