Prerequisites
Before beginning this configuration, ensure you have the following requirements met:
- Root or sudo access to your server
- NGINX installed and running
- A registered domain name pointing to your server
- Certbot installed for SSL certificate management
SSL Certificate Installation
Install Certbot
First, update your system and install Certbot with the Cloudflare DNS plugin:
Obtain SSL Certificate
Use Certbot to obtain an SSL certificate for your domain. Replace the paths and domain names with your actual values:
View Installed Certificates
To view all certificates installed on your server:
Certificate Management Commands
Renew certificates
Force renewal
Delete certificate
Diffie-Hellman Parameters
The Diffie-Hellman parameters file enhances SSL/TLS security by enabling perfect forward secrecy. This ensures that even if your private key is compromised in the future, past communications remain secure.
Create SSL Directory
Generate DH Parameters File
Verify Creation
You should see dhparam.pem in the directory listing.
SSL Configuration Files
Site-Specific SSL Configuration
Create a configuration file for your specific domain's SSL certificates. This file contains the paths to your Let's Encrypt certificates.
File contents:
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;
example.com with your actual domain name in both
the filename and the certificate paths.
Verify File Path
Copy this path to your text editor for later use.
Global SSL Configuration
Create a universal SSL configuration file that applies to all sites on your server. This file contains security settings that achieve an A+ rating on SSL Labs.
File contents:
# 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;
NGINX Server Block Configuration
Configuration Architecture Diagram
Open Your Site Configuration
Add Non-Secure Server Block (HTTP Redirect)
Add this server block at the top of your configuration file. This block will redirect all HTTP traffic to HTTPS:
server {
listen 80;
server_name example.com www.example.com;
# 301 PERMANENT REDIRECT TO HTTPS
return 301 https://example.com$request_uri;
}
https://example.com) must match your
WordPress installation URL exactly. Do not attempt to change from www to non-www or vice versa at
this stage, as it will cause a "too many redirects" error.
Modify Existing Server Block for HTTPS
Locate your existing server { block and modify the listen directives:
Original:
listen 80;
Change to (First Site):
listen 443 ssl;
http2 on;
listen 443 quic reuseport;
http3 on;
For Additional Sites (Second, Third, etc.):
listen 443 ssl;
http2 on;
listen 443 quic;
http3 on;
reuseport option. All subsequent sites should omit it. This is because socket options
can only be specified once for any given listening socket.
Add SSL Include Directives
Inside the secure server block (the one listening on port 443), add these include directives. Place
them after the try_files directive:
include /etc/nginx/ssl/ssl_certs_example.com.conf;
include /etc/nginx/ssl/ssl_all_sites.conf;
Replace ssl_certs_example.com.conf with your actual site-specific SSL configuration
filename.
Add HTTP/3 Testing Header
To test HTTP/3, temporarily add this directive inside the location / block:
location / {
try_files $uri $uri/ /index.php?$args;
# PREVENTS BROWSER CACHING SITE - USED TO TEST HTTP3
# COMMENT OR REMOVE DIRECTIVE AFTER CONFIRMING HTTP3 IS ENABLED
add_header Cache-Control 'no-cache,no-store';
}
Fix HTTP/3 PHP Warning
To prevent "undefined array key HTTP_HOST" warnings in PHP logs when using HTTP/3, add this directive to your PHP processing block:
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;
}
The key addition is:
Complete Example Configuration
Here is a complete example of a properly configured NGINX server block with SSL/TLS and HTTP/3:
# HTTP to HTTPS redirect
server {
listen 80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}
# Secure HTTPS server block
server {
listen 443 ssl;
http2 on;
listen 443 quic reuseport;
http3 on;
server_name example.com www.example.com;
root /var/www/example.com/public_html;
index index.php index.html index.htm;
# SSL Configuration
include /etc/nginx/ssl/ssl_certs_example.com.conf;
include /etc/nginx/ssl/ssl_all_sites.conf;
location / {
try_files $uri $uri/ /index.php?$args;
# Temporary for HTTP/3 testing - remove after verification
add_header Cache-Control 'no-cache,no-store';
}
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;
}
}
Test and Reload Configuration
Test NGINX syntax:
Expected output:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Reload NGINX:
HTTP/3 and QUIC Setup
Protocol Hierarchy Diagram
Key Differences
| Feature | HTTP/2 | HTTP/3 |
|---|---|---|
| Transport | TCP | UDP (QUIC) |
| Connection | Requires handshake | Faster 0-RTT |
| Head-of-line blocking | Possible | Eliminated |
| Encryption | Optional | Built-in |
| Performance | Good | Excellent |
- 0-RTT Connection: HTTP/3 can resume connections without a handshake, reducing latency
- No Head-of-line Blocking: Lost packets only affect the stream they belong to, not all streams
- Built-in Encryption: QUIC has encryption built into the protocol, improving security
- Better Mobile Performance: Handles network changes (WiFi to cellular) seamlessly
Testing and Verification
cURL Testing
Use cURL to test your redirects and SSL configuration:
Test HTTP redirect:
Test www redirect:
Test HTTPS with www:
Test HTTPS without www:
SSL Labs Testing
Visit SSL Labs and test your domain. With this configuration, you should receive an A+ rating.
HTTP/3 Verification
Online Tool:
Visit https://http3check.net/ and enter your domain.
Browser Console Method:
- Open your website in Chrome or Firefox
- Press F12 to open Developer Tools
- Go to Network tab
- Refresh the page (Ctrl+F5 or Cmd+Shift+R)
- Right-click on column headers
- Ensure Protocol column is enabled
- Look for h3 in the Protocol column
Automated Certificate Renewal
Setup Cron Job
Configure automatic certificate renewal to ensure your certificates never expire:
Add these lines:
# Renew 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
This ensures your certificates are automatically renewed twice per month and NGINX is reloaded to use the new certificates.
Troubleshooting
Common Issues
Issue: "Too Many Redirects" Error
Cause: Mismatch between WordPress URL and NGINX redirect URL, or conflicting redirect rules.
Solution:
- Ensure the redirect URL in your NGINX configuration matches your WordPress installation URL exactly
- Do not attempt to change from www to non-www (or vice versa) until WordPress is properly configured
- Check for conflicting redirects in WordPress plugins or .htaccess files
Issue: HTTP/3 Not Working
Cause: Browser caching or UDP port 443 blocked by firewall.
Solution:
- Clear browser cache completely (including SSL/TLS state)
- Ensure the
Cache-Controltesting header is present during testing - Verify UDP port 443 is open:
- Check your firewall logs for blocked UDP traffic
- Verify your hosting provider doesn't block UDP traffic
Issue: PHP Warnings in Logs
Warning: PHP warning undefined array key HTTP_HOST
Solution:
Add this line to your PHP processing block:
Issue: SSL Certificate Not Found
Error: nginx: [emerg] cannot load certificate
Solution:
- Verify certificate paths:
- Ensure certificate files exist:
fullchain.pem,privkey.pem,chain.pem - Check file permissions (certificates should be readable by the nginx user)
- Verify the domain name in your configuration matches the certificate domain
Issue: 502 Bad Gateway Error
Cause: PHP-FPM not running or socket path incorrect.
Solution:
- Check PHP-FPM status:
- Verify socket path exists:
- Check NGINX error logs for specific errors:
Security Best Practices
HSTS (HTTP Strict Transport Security)
The configuration includes HSTS headers to force browsers to use HTTPS:
add_header Strict-Transport-Security "max-age=31536000;" always;
After configuring all subdomains, enable HSTS for subdomains:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains;" always;
includeSubDomains after ensuring ALL subdomains
are properly configured with SSL certificates. This directive cannot be easily reversed.
Cipher Suite Security
The configuration uses only strong, modern cipher suites that provide:
- Perfect Forward Secrecy (PFS): Protects past sessions against future compromises of secret keys
- Protection against known attacks: Defends against BEAST, CRIME, and other SSL/TLS vulnerabilities
- Compatibility with modern browsers: Supports all current browsers while maintaining security
TLS Protocol Versions
Only TLS 1.2 and TLS 1.3 are enabled, providing:
- Strong encryption: Modern cryptographic algorithms
- Modern security standards: Compliance with current best practices
- Protection against protocol downgrade attacks: Prevents attackers from forcing older, vulnerable protocols
Additional Resources
Useful Commands
View NGINX error log:
View NGINX access log:
Check NGINX status:
Restart NGINX:
Check open ports:
Test SSL configuration:
Reload NGINX configuration:
View certificate details:
Summary Checklist
Use this checklist to ensure you've completed all necessary steps:
- Certbot installed and SSL certificates obtained
- Diffie-Hellman parameters file created
- Site-specific SSL configuration file created
- Global SSL configuration file created
- NGINX server block modified with HTTP redirect
- NGINX server block modified for HTTPS
- HTTP/2 and HTTP/3 enabled
fastcgi_param HTTP_HOSTadded to PHP block- Configuration tested with
sudo nginx -t - NGINX reloaded
- SSL Labs test shows A+ rating
- HTTP/3 verified as working
- Cache-Control testing header removed
- Automated certificate renewal configured