WIUNIX ACADEMY
Install WordPress on Nginx with a privacy-safe FastCGI cache
Page 4 of 8. Review the earlier steps and prerequisites before continuing.
YOUR SYSTEM. YOUR STEPS.
Where are you working?
Choose your OS and version. The prerequisites, commands and troubleshooting follow your environment.
USE YOUR OWN VALUES
Configure this tutorial
Set the example values for your environment; related explanations and copy panels update together. Never enter passwords, private keys or tokens. Values stay in this tab and are not sent to the server or share links. Check actual DNS, certificates and network policy before running commands.
Different IP addresses, domains or ports? Open the settings below, change the values and select “Apply to tutorial”, then copy the updated commands.
Set values for my environment4 editable values
Prepare challenge-only HTTP
Port 80 must be reachable by the certificate authority. If a certificate is already managed for this domain, reuse that owner’s approved automation and paths rather than create competing issuance.
WordPress server, Bash, existing sudo administrator.
(
set -euo pipefail
sudo install -d -o root -g root -m 0755 /var/lib/letsencrypt/.well-known/acme-challenge
sudo nano /etc/nginx/conf.d/wordpress-site.conf
)
Check succeeds; stop on an error before continuing.
LET’S BUILD
Write the temporary HTTP site
Replace the hostname. The only public HTTP content is ACME challenge material; WordPress is not exposed.
File contents; save in the named file, not the shell.
server {
listen 80;
listen [::]:80;
server_name blog.example.com;
location ^~ /.well-known/acme-challenge/ {
root /var/lib/letsencrypt;
default_type text/plain;
try_files $uri =404;
}
location / { return 404; }
}
Check succeeds; stop on an error before continuing.
LET’S BUILD
Validate and enable the challenge endpoint
The dedicated main configuration includes this challenge-only site directly. Full validation must succeed before the first Nginx start. Verify the challenge from an independent external connection as well; local DNS/reachability cannot prove CA access.
WordPress server, Bash, existing sudo administrator.
(
set -euo pipefail
sudo nginx -t
sudo systemctl unmask nginx.service
sudo systemctl enable --now nginx.service
printf 'acme-path-check\n' | sudo tee /var/lib/letsencrypt/.well-known/acme-challenge/academy-check >/dev/null
curl -fsS --connect-timeout 5 --max-time 15 http://blog.example.com/.well-known/acme-challenge/academy-check | grep -Fx acme-path-check
)
nginx -t passes and challenge body matches acme-path-check exactly.
LET’S BUILD
Issue TLS before WordPress setup
Replace email/domain. Read and accept the CA terms before agree-tos. This registers a real ACME account/issues a real certificate. Existing HTTP owns the challenge. Failure stops before the editor; after success replace the site with the next panel. Never use browser certificate exceptions or curl -k for setup.
WordPress server, Bash, existing sudo administrator.
(
set -euo pipefail
sudo certbot certonly --webroot -w /var/lib/letsencrypt -d blog.example.com --email YOUR_REAL_EMAIL --agree-tos
sudo test -s /etc/letsencrypt/live/blog.example.com/fullchain.pem
sudo test -s /etc/letsencrypt/live/blog.example.com/privkey.pem
sudo nano /etc/nginx/conf.d/wordpress-site.conf
)
Check succeeds; stop on an error before continuing.
LET’S BUILD
Protect setup with TLS and a temporary IP gate
Replace hostname, certificate paths and admin IP. Keep cache off. The server-level gate protects static and PHP paths. Upload PHP, configuration and hidden files are denied. HTTP redirects to one fixed canonical HTTPS hostname after challenges.
File contents; save in the named file, not the shell.
server {
listen 80;
listen [::]:80;
server_name blog.example.com;
location ^~ /.well-known/acme-challenge/ {
root /var/lib/letsencrypt;
default_type text/plain;
try_files $uri =404;
}
location / { return 301 https://blog.example.com$request_uri; }
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name blog.example.com;
if ($host != blog.example.com) { return 444; }
ssl_certificate /etc/letsencrypt/live/blog.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/blog.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
root /var/www/wordpress;
index index.php;
client_max_body_size 16m;
# Temporary installation gate. Replace with YOUR actual public admin IP.
allow 203.0.113.10;
deny all;
location / { try_files $uri $uri/ /index.php?$args; }
location ~* ^/wp-content/uploads/.*\.php(?:/|$) { return 403; }
location = /wp-config.php { deny all; }
location ~ /\. { deny all; }
location ~ \.php$ {
try_files $uri =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTPS on;
fastcgi_pass unix:/run/php-fpm/wordpress.sock;
fastcgi_cache off;
fastcgi_cache_key "$scheme|$request_method|$host|$request_uri|wp-v1";
fastcgi_cache_methods GET;
fastcgi_cache_valid 200 60s;
fastcgi_cache_lock on;
fastcgi_cache_bypass $wp_skip_method $wp_skip_cookie $wp_skip_auth $wp_skip_args $wp_skip_route $wp_private_path;
fastcgi_no_cache $wp_skip_method $wp_skip_cookie $wp_skip_auth $wp_skip_args $wp_skip_route $wp_private_path;
add_header X-WP-Cache $upstream_cache_status always;
}
}
Check succeeds; stop on an error before continuing.
LET’S BUILD
Check TLS and installer access
Validate/reload on server. Run the HTTPS curl on the allowed workstation if server egress is not permitted. Your address should get installer 200; an independent unpermitted client must get 403. Do not broaden the gate just to pass a test.
Server terminal, sudo administrator.
(
set -euo pipefail
sudo nginx -t
sudo systemctl reload nginx
sudo ss -lntp 'sport = :443'
)
Trusted hostname-matching TLS; installer restricted to the intended IP.