WIUNIX ACADEMY
Install WordPress on Nginx with a privacy-safe FastCGI cache
Page 3 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
Generate independent authentication salts
Run privately; keep output out of screenshots/logs/Git. Use one different value for each of the eight key/salt placeholders below.
WordPress server, Bash, existing sudo administrator.
for n in $(seq 1 8); do openssl rand -hex 48; done
Check succeeds; stop on an error before continuing.
Create the protected configuration file
Fresh installation only: the guard refuses an existing configuration. Do not rerun this on a live site. Save the following file panel, not shell commands.
WordPress server, Bash, existing sudo administrator.
(
set -euo pipefail
sudo test ! -e /var/www/wordpress/wp-config.php
sudo test ! -L /var/www/wordpress/wp-config.php
sudo install -o root -g wp-php -m 0640 /dev/null /var/www/wordpress/wp-config.php
sudo nano /var/www/wordpress/wp-config.php
)
Check succeeds; stop on an error before continuing.
LET’S BUILD
Connect WordPress to MariaDB
Replace every placeholder and both hostname values. The table prefix is not a security control. Direct TLS termination is assumed; never trust arbitrary forwarded-HTTPS headers. The dashboard code editor is disabled. Root-owned core/themes/plugins need administrator-controlled timely updates; do not make the whole tree writable to solve an update prompt.
File contents; save in the named file, not the shell.
<?php
define( 'DB_NAME', 'wordpress' );
define( 'DB_USER', 'wp_site' );
define( 'DB_PASSWORD', 'REPLACE_WITH_THE_DATABASE_PASSWORD' );
define( 'DB_HOST', 'localhost:/var/lib/mysql/mysql.sock' );
define( 'DB_CHARSET', 'utf8mb4' );
define( 'DB_COLLATE', '' );
// Replace every placeholder with a DIFFERENT local random value.
define( 'AUTH_KEY', 'REPLACE_1' );
define( 'SECURE_AUTH_KEY', 'REPLACE_2' );
define( 'LOGGED_IN_KEY', 'REPLACE_3' );
define( 'NONCE_KEY', 'REPLACE_4' );
define( 'AUTH_SALT', 'REPLACE_5' );
define( 'SECURE_AUTH_SALT', 'REPLACE_6' );
define( 'LOGGED_IN_SALT', 'REPLACE_7' );
define( 'NONCE_SALT', 'REPLACE_8' );
$table_prefix = 'wp_';
define( 'WP_HOME', 'https://blog.example.com' );
define( 'WP_SITEURL', 'https://blog.example.com' );
define( 'FORCE_SSL_ADMIN', true );
define( 'DISALLOW_FILE_EDIT', true );
define( 'WP_DEBUG', false );
if ( ! defined( 'ABSPATH' ) ) { define( 'ABSPATH', __DIR__ . '/' ); }
require_once ABSPATH . 'wp-settings.php';
Check succeeds; stop on an error before continuing.
LET’S BUILD
Check syntax and ownership
These checks do not print private configuration values.
WordPress server, Bash, existing sudo administrator.
(
set -euo pipefail
sudo php -l /var/www/wordpress/wp-config.php
if sudo grep -q REPLACE_ /var/www/wordpress/wp-config.php; then
echo 'Replace all configuration placeholders first' >&2; exit 1
fi
sudo -u wp-php test -r /var/www/wordpress/wp-config.php
sudo -u wp-php test ! -w /var/www/wordpress/wp-config.php
sudo -u wp-php test -w /var/www/wordpress/wp-content/uploads
)
PHP reads but cannot write config; uploads is writable; no placeholders remain.
Prepare the cache directory and policy
The worker owns this new cache directory. The complete dedicated nginx.conf panel explicitly includes conf.d in http context, so the map/cache declarations have a defined location on every family.
WordPress server, Bash, existing sudo administrator.
(
set -euo pipefail
sudo install -d -o nginx -g nginx -m 0700 /var/cache/nginx/wordpress
sudo nano /etc/nginx/conf.d/wordpress-cache.conf
)
Check succeeds; stop on an error before continuing.
LET’S BUILD
Opt in only known public pages
Only homepage and sample-page are eligible. All other paths, including admin/login, preview, API/XML-RPC, cron, cart/checkout/account, feeds, searches and password-protected pages, stay outside the allowlist. Any cookie, Authorization, query string, non-GET method or percent-encoded path bypasses both lookup and storage. This covers WordPress login/post-password/comment cookies and unknown plugin sessions without maintaining an incomplete cookie-name list. Remove an allowed page and rotate the key BEFORE protecting or personalizing it. Never add geo/device/private/plugin-personalized content without separately assessing keys and response controls. UTM parameters also bypass deliberately; ignoring them requires application review.
File contents; save in the named file, not the shell.
# http context: included by the dedicated nginx.conf
fastcgi_cache_path /var/cache/nginx/wordpress levels=1:2 keys_zone=WORDPRESS:20m
max_size=512m inactive=10m use_temp_path=off;
map $request_method $wp_skip_method { default 1; GET 0; }
# Every cookie is private until separately assessed, including WordPress,
# wp-postpass, comment_author, WooCommerce and plugin session cookies.
map $http_cookie $wp_skip_cookie { default 1; "" 0; }
map $http_authorization $wp_skip_auth { default 1; "" 0; }
map $args $wp_skip_args { default 1; "" 0; }
# Opt in ONLY known public HTML URLs. Everything else stays dynamic.
map $request_uri $wp_skip_route {
default 1;
"/" 0;
"/sample-page/" 0;
}
# Defense in depth for the standard private/dynamic endpoints.
map $request_uri $wp_private_path {
default 0;
~*^/(wp-admin|wp-login\.php|wp-cron\.php|wp-json|xmlrpc\.php|cart|checkout|my-account|account)(/|\?|$) 1;
~*% 1;
}
512 MB bounded cache and 20 MB keys zone declared; virtual host caching is still off.