← Start of tutorial

WIUNIX ACADEMY

Install WordPress on Nginx with a privacy-safe FastCGI cache

Page 7 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.

Almalinux 9.8Reviewed against official documentation ·

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
28

LET’S BUILD

Prove password-protected body isolation

Use locked-test, absent from the allowlist. In the protected browser enter its test password and privately export the real post-password jar. Anonymous must see the form without secret text, valid password session must see secret text, and new anonymous must still lack it. Test actual cart/checkout/account/plugin workflows with distinct real users before broadening this configuration. This guide does not install WooCommerce or prove third-party plugin behavior.

Bash

Allowed staging client, Bash/curl; all cookie files are private and local to this client.

(
set -euo pipefail
umask 077
work=$(mktemp -d)
base=https://blog.example.com
code=$(curl -sS --connect-timeout 5 --max-time 20 -D "$work/locked.h" -o "$work/locked.html" -w '%{http_code}' "$base/locked-test/")
[ "$code" = 200 ]
grep -Eiq '^X-WP-Cache: BYPASS[[:space:]]*$' "$work/locked.h"
grep -F 'post-password-form' "$work/locked.html"
if grep -Fq LOCKED_CONTENT_MARKER "$work/locked.html"; then
  echo 'Unexpected cached/private content or status' >&2; exit 1
else
  rc=$?; [ "$rc" = 1 ] || exit "$rc"
fi
# Existing private jar from entering this test post password in your browser.
jar=$HOME/wp-staging-postpass.cookies
[ -s "$jar" ]
chmod 0600 "$jar"
code=$(curl -sS --connect-timeout 5 --max-time 20 -b "$jar" -D "$work/unlocked.h" -o "$work/unlocked.html" -w '%{http_code}' "$base/locked-test/")
[ "$code" = 200 ]
grep -Eiq '^X-WP-Cache: BYPASS[[:space:]]*$' "$work/unlocked.h"
grep -F LOCKED_CONTENT_MARKER "$work/unlocked.html"
code=$(curl -sS --connect-timeout 5 --max-time 20 -D "$work/again.h" -o "$work/again.html" -w '%{http_code}' "$base/locked-test/")
[ "$code" = 200 ]
if grep -Fq LOCKED_CONTENT_MARKER "$work/again.html"; then
  echo 'Unexpected cached/private content or status' >&2; exit 1
else
  rc=$?; [ "$rc" = 1 ] || exit "$rc"
fi
)
How do I know it worked?

All requests BYPASS; only valid post-password session sees LOCKED_CONTENT_MARKER.

29

LET’S BUILD

Open the completed site to visitors

After setup and staging privacy tests pass, use this editor to remove ONLY the temporary allow/deny lines at HTTPS server scope. Keep location-specific configuration/hidden-file protection and all cache bypass rules. Run the earlier validation/reload panel, then require public 200 and working login from an independent connection. Keep outer CDN caching disabled during origin tests.

Bash

WordPress server, Bash, existing sudo administrator.

(
set -euo pipefail
sudo nano /etc/nginx/conf.d/wordpress-site.conf
)
How do I know it worked?

Finished HTTPS site is public; sessions and private URLs remain uncached.

Prepare the renewal deploy hook

Keep HTTP challenge routing. The package’s existing Certbot timer owns renewal; do not create a competing scheduler. The following hook validates Nginx before reloading a renewed certificate.

Bash

WordPress server, Bash, existing sudo administrator.

(
set -euo pipefail
sudo install -d -m 0755 /etc/letsencrypt/renewal-hooks/deploy
sudo nano /etc/letsencrypt/renewal-hooks/deploy/20-nginx-validated
)
How do I know it worked?

Check succeeds; stop on an error before continuing.

Validate renewed certificate deployment

Save as file contents; do not paste a script body into an unrelated shell session.

Shell · /etc/letsencrypt/renewal-hooks/deploy/20-nginx-validated

File contents; save in the named file, not the shell.

#!/bin/sh
set -eu
/usr/sbin/nginx -t
/bin/systemctl reload nginx
How do I know it worked?

Check succeeds; stop on an error before continuing.

30

LET’S BUILD

Check renewal and its scheduler

The direct hook call gracefully reloads only after validation. Dry-run uses ACME staging and may take time; deploy hooks are not normally run by dry-run, so the direct call separately tests the hook.

Bash

WordPress server, Bash, existing sudo administrator.

(
set -euo pipefail
sudo chmod 0750 /etc/letsencrypt/renewal-hooks/deploy/20-nginx-validated
sudo /etc/letsencrypt/renewal-hooks/deploy/20-nginx-validated
sudo certbot renew --dry-run
sudo systemctl enable --now certbot-renew.timer
systemctl list-timers --all certbot-renew.timer
)
How do I know it worked?

Hook and dry-run succeed; existing package timer is scheduled.

Invalidate without deleting active cache files

No purge API/module is assumed. For urgent changes disable cache or remove the public URL BEFORE changing its privacy, then select a never-used generation in the next panel and validate/reload. This invalidates the whole small site, not one URL. Old files are unreachable by new keys and expire by inactive/size controls. Never revert to an old generation. Normal WordPress publication does not trigger this cache invalidation; content can remain until its actual upstream/fallback TTL. In staging, change the marker to PUBLIC_CACHE_MARKER_V2, rotate generation, then repeat the anonymous test using the new marker and require the old content absent.

Bash

WordPress server, Bash, existing sudo administrator.

(
set -euo pipefail
sudo nano /etc/nginx/conf.d/wordpress-site.conf
)
How do I know it worked?

Check succeeds; stop on an error before continuing.

Explore all tutorials ↗