1. Introduction to SSL/TLS and HTTPS
Secure Sockets Layer (SSL) and its successor, Transport Layer Security (TLS), are cryptographic protocols
designed to provide secure communication over computer networks. In modern web infrastructure, serving
all sites over HTTPS is not just a best practice—it's a requirement for security, trust, and search
engine optimization.
Why SSL/TLS Certificates Are Essential
Client Browser
→
SSL/TLS Encryption
→
Web Server
Key Benefits of SSL/TLS Implementation:
- Data Encryption: SSL certificates encrypt all data transmitted between a user's
browser and your server. This encryption ensures that sensitive information such as login
credentials, personal details, credit card information, and financial transactions remain private
and cannot be intercepted or read by unauthorized individuals.
- Trust and Credibility: When a site uses SSL and displays the padlock icon or the
HTTPS protocol in the browser's address bar, it indicates to visitors that the site is secure. This
visual indication helps build trust and confidence with your site visitors, reducing bounce rates
and increasing conversions.
- SEO Advantages: Search engines, particularly Google, consider SSL encryption as a
ranking factor. Sites with SSL certificates tend to rank higher in search results compared to those
without. Implementing SSL can potentially improve a site's visibility and organic traffic.
- Compliance Requirements: SSL certificates are often required to meet certain
compliance standards, particularly when dealing with sensitive data such as credit card information
(PCI DSS), personal information (GDPR), or health information (HIPAA).
- Browser Compatibility: Modern browsers actively warn users when visiting non-HTTPS
sites, marking them as "Not Secure." This can significantly impact user trust and site
accessibility.
Let's Encrypt and Certbot
In this guide, we'll use Let's Encrypt, a free, automated, and open Certificate
Authority, along with Certbot, the recommended client for obtaining and managing Let's
Encrypt certificates.
Certbot Features:
- Automated Certificate Management: Certbot can set up scheduled tasks or cron jobs
to automatically renew SSL certificates before they expire (Let's Encrypt certificates are valid for
90 days).
- Web Server Integration: Certbot integrates seamlessly with popular web servers like
NGINX and Apache.
- Simple Command-Line Interface: With just a few commands, you can request, install,
and manage SSL certificates.
- Multiple Domain Support: Certbot can obtain certificates for multiple domains and
subdomains, including wildcard certificates.
Important Note: This guide focuses on manual configuration rather than automated
templates. This approach provides the best performance, security, and understanding of your SSL/TLS
implementation.
2. Prerequisites and DNS Setup
Critical Requirement: Before installing any SSL certificates, ensure your DNS records
are properly configured. Both the A record and the CNAME record must resolve to your server's IP
address.
DNS Requirements
For successful SSL certificate issuance and validation, you need the following DNS records configured:
| Record Type |
Hostname |
Value |
Purpose |
| A Record |
example.com |
Your Server IP |
Points domain to server |
| CNAME Record |
www.example.com |
example.com |
Points www subdomain to main domain |
Verification Commands
Before proceeding with certificate installation, verify your DNS records are properly propagated:
dig example.com
dig www.example.com
nslookup example.com
nslookup www.example.com
DNS Propagation: DNS changes can take up to 48 hours to fully propagate globally,
though they typically propagate within a few hours. Use online DNS checking tools to verify propagation
across different geographic regions.
3. Installing Certbot and Dependencies
We'll install Certbot and the Cloudflare DNS plugin, which enables advanced DNS-based authentication
methods for certificate issuance.
Step 1: Update Package Lists
Always update your package lists before installing new software:
Step 2: Upgrade Existing Packages (Optional but Recommended)
Step 3: Install Certbot and Cloudflare Plugin
Install both packages in a single command:
sudo apt install certbot python3-certbot-dns-cloudflare
Package Descriptions:
- certbot: The main Let's Encrypt client that automates the process of installing and
renewing SSL certificates.
- python3-certbot-dns-cloudflare: A Python module that facilitates integration
between Certbot and Cloudflare DNS servers, enabling DNS-based certificate validation.
Installation Note: During the installation of the python3-certbot-dns-cloudflare
package, you may see a syntax warning. This is harmless and does not affect the functionality of the
package. You can safely ignore this warning.
Verification
Verify the installation was successful:
4. Obtaining SSL Certificates
Now that Certbot is installed, we'll obtain SSL certificates for your domain. We'll use the manual method
for better control and understanding of the process.
Certificate Issuance Command
The following command requests certificates for both your main domain and the www subdomain:
sudo certbot certonly --webroot -w /var/www/example.com/public_html/ -d
example.com -d www.example.com
Command Breakdown:
sudo certbot - Runs Certbot with administrative privileges
certonly - Obtains the certificate but doesn't automatically install it (allows manual
configuration)
--webroot - Uses the webroot plugin for domain validation
-w /var/www/example.com/public_html/ - Specifies the webroot directory where validation
files will be placed
-d example.com - Specifies the first domain name
-d www.example.com - Specifies the second domain name (www subdomain)
Interactive Prompts
During the certificate request process, Certbot will ask several questions:
Prompt 1: Enter email address (used for urgent renewal and security notices)
Action: Provide a valid email address
Prompt 2: Do you agree with the Terms of Service?
Action: Type "Y" for Yes
Prompt 3: Would you be willing to share your email address with the Electronic Frontier
Foundation?
Action: Type "N" for No (optional, but recommended for privacy)
Successful Certificate Issuance
Upon successful completion, you'll see output similar to this:
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
Key is saved at: /etc/letsencrypt/live/example.com/privkey.pem
This certificate expires on: [date 90 days from now]
Important: Save the certificate and key file paths in a text document for reference.
You'll need these paths when configuring NGINX.
Certificate Files Explained
| File |
Purpose |
| fullchain.pem |
Complete certificate chain including your certificate and intermediate certificates |
| privkey.pem |
Private key for your certificate (keep this secure!) |
| chain.pem |
Intermediate certificates needed for SSL stapling |
| cert.pem |
Your domain certificate only |
5. Generating Diffie-Hellman Parameters
The Diffie-Hellman key exchange is a cryptographic method for securely exchanging encryption keys over a
public channel. The DH parameters file strengthens the security of SSL/TLS connections.
Understanding Diffie-Hellman
Diffie-Hellman is an algorithm used to establish a shared secret between two parties over an insecure
channel. It is primarily used as a method for exchanging keys for use in symmetric encryption
algorithms. The purpose of generating the dhparam.pem file is to define how OpenSSL performs the
Diffie-Hellman key exchange, ensuring forward secrecy.
Creating the SSL Directory
First, navigate to the NGINX configuration directory and create an SSL subdirectory:
cd /etc/nginx
sudo mkdir ssl
cd ssl
ls
Generating the DH Parameters File
Generate a 2048-bit Diffie-Hellman parameters file:
sudo openssl dhparam -out dhparam.pem 2048
⏱️ Time Required: Generating the DH parameters file can take several minutes (typically
2-5 minutes depending on your server's processing power). Do not interrupt the process. Be patient and
let the command complete.
Important Notes:
- The DH parameters file is created only once per server
- Do not recreate this file when adding additional sites to the server
- All sites on the server will use the same dhparam.pem file
- The file path will be:
/etc/nginx/ssl/dhparam.pem
Verification
Verify the file was created successfully:
You should see a file approximately 424 bytes in size.
6. Configuring NGINX for HTTPS
With certificates obtained and DH parameters generated, we now configure NGINX to serve your site
securely over HTTPS. We'll create two include files: one site-specific and one that applies to all
sites.
Step 1: Create Site-Specific SSL Configuration
This file contains the paths to your specific domain's certificate files:
cd /etc/nginx/ssl
sudo nano ssl_certs_example.com.conf
Add the following content (replace example.com with your actual domain):
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;
Directive Explanations:
ssl_certificate - Specifies the path to the full certificate chain (server certificate
+ intermediate certificates)
ssl_certificate_key - Specifies the path to the private key for cryptographic
operations
ssl_trusted_certificate - Specifies intermediate certificates for SSL stapling
(improves performance and security)
Step 2: Create Universal SSL Configuration
This file contains SSL directives that apply to all sites on your server. It's designed to achieve an A+
rating on SSL Labs:
sudo nano /etc/nginx/ssl/ssl_all_sites.conf
Add the following configuration:
# CONFIGURATION RESULTS IN A+ RATING AT SSLLABS.COM
# WILL UPDATE DIRECTIVES TO MAINTAIN A+ RATING - CHECK DATE
# DATE: 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;
# ssl_ciphers must be on a single line, do not split over multiple lines
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;
# LETS ENCRYPT REMOVED SUPPORT 7 MAY 2025 FOR SSL STAPLING
# REMOVE OR COMMENT BOTH ssl_stapling directives
#ssl_stapling on;
#ssl_stapling_verify on;
# resolver set to Cloudflare
# timeout can be set up to 30s
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 of your sub domains - comment the above and uncomment
the directive below
# 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;
Key Configuration Elements:
| Directive |
Purpose |
| ssl_session_cache |
Stores SSL session parameters to reduce computational overhead |
| ssl_session_timeout |
Sets session expiration time (180 minutes) |
| ssl_protocols |
Enables only secure TLS versions (1.2 and 1.3) |
| ssl_prefer_server_ciphers |
Prioritizes server's cipher suite preference |
| ssl_ciphers |
Defines allowed encryption algorithms (strong security) |
| ssl_dhparam |
Points to DH parameters file |
| Strict-Transport-Security |
Forces HTTPS for one year (prevents downgrade attacks) |
| ssl_early_data |
Enables TLS 1.3 early data (reduces latency) |
| HTTP/3 directives |
Enables next-generation HTTP protocol |
Important Note on HSTS: Initially, use the HSTS header without
includeSubDomains. Only after you've successfully configured SSL for all subdomains,
comment out the basic directive and uncomment the one that includes subdomains. This prevents
accessibility issues during subdomain setup.
Step 3: Update NGINX Server Block
Now we'll modify your site's NGINX configuration to use HTTPS:
cd /etc/nginx/sites-available
sudo nano example.com.conf
HTTP to HTTPS Redirect Block:
Add this server block to redirect all HTTP traffic to HTTPS:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}
HTTPS Server Block:
Modify your existing server block or create a new one:
server {
# Enable HTTPS with HTTP/2
listen 443 ssl;
http2 on;
# Enable HTTP/3 (use 'reuseport' only on first server block)
listen 443 quic reuseport;
http3 on;
server_name example.com www.example.com;
# Include SSL certificate files (site-specific)
include /etc/nginx/ssl/ssl_certs_example.com.conf;
# Include universal SSL configuration
include /etc/nginx/ssl/ssl_all_sites.conf;
root /var/www/example.com/public_html;
index index.php index.html;
# PHP processing location block
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_param HTTP_HOST $host;
fastcgi_pass unix:/run/php/php8.3-fpm-example.com.sock;
}
}
HTTP/3 Configuration Note: Use reuseport parameter only in the
first server block that uses QUIC/HTTP/3. For additional server blocks on the same
server, use listen 443 quic; without the reuseport parameter.
Step 4: Test and Reload Configuration
If the test is successful, reload NGINX:
sudo systemctl reload nginx
Congratulations! Your site is now configured to serve traffic exclusively over HTTPS
with modern security standards and HTTP/3 support.
7. PHP-FPM Pool Configuration
PHP-FPM (FastCGI Process Manager) pools allow you to run PHP processes under different user accounts,
providing isolation and security between sites hosted on the same server.
Creating a System User
First, create a dedicated user for your site:
User Group Configuration
Add users to appropriate groups for proper permissions:
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 Custom PHP-FPM Pool
Navigate to the PHP-FPM pool directory and create a 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 Template
Modify the configuration file with the following key settings:
; Pool name
[example]
; Unix user/group of the child processes
user = username
group = username
; Socket configuration
listen = /run/php/php8.3-fpm-example.com.sock
; Resource limits
rlimit_files = 15000
rlimit_core = 100
; PHP configuration
php_flag[display_errors] = off
php_admin_value[error_log] = /var/log/fpm-php.example.com.log
php_admin_flag[log_errors] = on
Creating the Log File
Create and configure the PHP-FPM log file:
sudo touch /var/log/fpm-php.example.com.log
sudo chown username:www-data /var/log/fpm-php.example.com.log
sudo chmod 660 /var/log/fpm-php.example.com.log
Configuring Temporary Directories
Create site-specific temporary directories for security:
cd /var/www/example.com/
sudo mkdir tmp
sudo chown username:username tmp
sudo chmod 770 tmp
Add these directives to your pool configuration:
php_admin_value[upload_tmp_dir] = /var/www/example.com/tmp/
php_admin_value[sys_temp_dir] = /var/www/example.com/tmp/
Configuring Open Basedir
Restrict PHP file access to specific directories:
php_admin_value[open_basedir] =
/var/www/example.com/public_html/:/var/www/example.com/tmp/
Disabling Dangerous PHP Functions
For enhanced security, disable potentially 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
Enabling Configuration Changes
After making changes, reload PHP-FPM:
sudo systemctl reload php8.3-fpm
Verifying Socket Configuration
sudo grep "listen = /" example.com.conf
Updating NGINX Configuration
Update your NGINX site configuration to use the new pool socket:
sudo nano /etc/nginx/sites-available/example.com.conf
Update the fastcgi_pass directive in your PHP location block:
fastcgi_pass unix:/run/php/php8.3-fpm-example.com.sock;
Test and reload NGINX:
sudo nginx -t
sudo systemctl reload nginx
8. Security Hardening
Implementing comprehensive security measures is critical for protecting your web applications from
attacks and vulnerabilities.
Creating NGINX Security Directives
Create a comprehensive security configuration file:
cd /etc/nginx/includes/
sudo nano nginx_security_directives.conf
WordPress Security Configuration
# 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 directories
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/dirs
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; }
Allowing Specific Plugin PHP Execution
If a plugin requires PHP execution, create a specific location block:
location = /wp-content/plugins/specific-plugin/file.php {
allow all;
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;
}
Including Security Configuration
Add the security include file to your site configuration:
sudo nano /etc/nginx/sites-available/example.com.conf
Add this line above your PHP processing location block:
include /etc/nginx/includes/nginx_security_directives.conf;
Test and reload:
sudo nginx -t
sudo systemctl reload nginx
9. File Permissions and Ownership
Proper file permissions are essential for security. We'll configure two permission sets: standard and
hardened.
Standard Permissions
Suitable for development environments or sites requiring frequent file modifications:
cd /var/www/example.com/
sudo chown -R username:username 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 {} \;
sudo chmod 400 public_html/wp-config.php
Hardened Permissions
Recommended for production environments:
cd /var/www/example.com/
sudo chown -R username:username public_html/
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
| Permission |
Meaning |
Use Case |
| 770 (directories) |
Owner and group: full access; Others: no access |
Standard - allows modifications |
| 660 (files) |
Owner and group: read/write; Others: no access |
Standard - allows modifications |
| 550 (directories) |
Owner and group: read/execute; Others: no access |
Hardened - read-only |
| 440 (files) |
Owner and group: read-only; Others: no access |
Hardened - read-only |
| 400 (wp-config.php) |
Owner: read-only; Group and others: no access |
Maximum security for sensitive files |
WordPress Note: The wp-content directory requires write permissions (770/660) even in
hardened configurations because WordPress needs to upload files, update plugins/themes, and cache
content.
11. Rate Limiting
Rate limiting protects your server from brute-force attacks and excessive requests to sensitive
endpoints.
Configuring Rate Limit Zone
Edit the main NGINX configuration:
cd /etc/nginx/
sudo nano nginx.conf
Add in the http context:
##
# Rate Limiting
limit_req_zone $binary_remote_addr zone=wp:10m rate=30r/m;
Creating Rate Limiting Rules
cd /etc/nginx/includes/
sudo nano rate_limiting_example.com.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;
}
Rate Limiting Parameters Explained
| Parameter |
Meaning |
| zone=wp:10m |
Creates a zone named "wp" with 10MB memory (tracks ~160,000 IPs) |
| rate=30r/m |
Allows 30 requests per minute per IP address |
| burst=20 |
Allows bursts of up to 20 requests |
| nodelay |
Process excess requests immediately (no queuing) |
| limit_req_status 444 |
Return 444 status (close connection) when limit exceeded |
Including Rate Limiting Configuration
cd /etc/nginx/sites-available/
sudo nano example.com.conf
Add this include directive:
include /etc/nginx/includes/rate_limiting_example.com.conf;
Test and reload:
sudo nginx -t
sudo systemctl reload nginx
12. Automatic Certificate Renewal
Let's Encrypt certificates are only valid for 90 days. Setting up automatic renewal ensures your
certificates never expire.
Certbot Management Commands
sudo certbot certificates
sudo certbot delete
sudo certbot renew
sudo certbot renew --force-renewal
Setting Up Automated Renewal
Configure a cron job to automatically renew certificates:
Add the following lines to renew certificates on the 14th and 28th of each month:
00 1 14,28 * * certbot renew --force-renewal
00 2 14,28 * * systemctl reload nginx
Cron Schedule Explanation
| Field |
Value |
Meaning |
| Minute |
00 |
At the start of the hour |
| Hour |
1 / 2 |
1:00 AM / 2:00 AM |
| Day |
14,28 |
On the 14th and 28th |
| Month |
* |
Every month |
| Day of Week |
* |
Any day |
Why Two Renewals Per Month? Since certificates are valid for 90 days, renewing twice
per month ensures you have at least 30 days to resolve any renewal issues before certificates expire.
13. Testing and Verification
Testing with cURL
Verify your site is accessible and properly redirecting:
curl -I http://example.com
curl -I http://www.example.com
curl -I https://www.example.com
curl -I https://example.com
SSL Labs Testing
Test your SSL configuration and get a security rating:
Visit https://www.ssllabs.com/ssltest/ and enter your domain. With the configuration in
this guide, you should receive an A+ rating.
HTTP/3 and QUIC Testing
Verify HTTP/3 is working:
Visit https://http3check.net/ and enter your domain to verify HTTP/3 support.
Browser Developer Tools Testing
To verify HTTP/3 in your browser:
- Open your browser's Developer Tools (F12)
- Navigate to the Network tab
- Right-click on the column headers
- Ensure "Protocol" is checked
- Refresh your page
- Look for "h3" in the Protocol column
Temporary No-Cache Directive for Testing
When testing HTTP/3, add this directive temporarily to prevent browser caching:
add_header Cache-Control 'no-cache,no-store';
Remember: Remove or comment out the no-cache directive after confirming HTTP/3 is
working properly. Leaving it active will negatively impact site performance.
Viewing NGINX Logs
Monitor your server logs for errors or issues:
cd /var/log/nginx
ls
sudo cat log_file_name.log
sudo less log_file_name.log
Database Security (WordPress)
Limit database privileges for enhanced security:
REVOKE ALL PRIVILEGES ON site_db.* FROM 'site_user'@'hostname';
GRANT SELECT, INSERT, UPDATE, DELETE ON site_db.* TO 'site_user'@'hostname';
FLUSH PRIVILEGES;
If needed, grant additional privileges for specific operations:
GRANT CREATE, ALTER, INDEX ON database_name.* TO 'username'@'localhost';
WordPress Security Hardening
Add this to wp-config.php to prevent file modifications from the admin panel:
define('DISALLOW_FILE_MODS', true);
🎉 Congratulations! You've successfully configured a production-ready NGINX server with
SSL/TLS, PHP-FPM pools, comprehensive security hardening, and modern protocols including HTTP/3. Your
server is now optimized for performance, security, and reliability.
Quick Reference Commands
NGINX Management
sudo nginx -t
sudo systemctl reload nginx
sudo systemctl restart nginx
sudo systemctl status nginx
PHP-FPM Management
sudo systemctl reload php8.3-fpm
sudo systemctl restart php8.3-fpm
sudo systemctl status php8.3-fpm
Certificate Management
sudo certbot certificates
sudo certbot renew
sudo certbot delete