کیت در فضای نمایشی آکادمی ویونیکس با نمایشگر نمونهٔ وردپرس و کش Nginx کار می‌کند.

WIUNIX ACADEMY

آموزش نصب وردپرس با Nginx و کش FastCGI در اوبونتو و دبیان

وردپرس را با PHP-FPM، MariaDB و HTTPS نصب کنید؛ سپس فقط صفحات عمومیِ مشخص را کش کنید و جداسازی نشست کاربران و محتوای رمزدار را بسنجید.

متوسط85 دقیقه مطالعهبه‌روزرسانی
محیطت را انتخاب کن

آموزش متناسب با محیط تو

کجا اجرا می‌کنی؟

سیستم‌عامل و نسخه را انتخاب کن تا مراحل، دستورها و راه‌حل‌ها برای همان محیط نمایش داده شوند.

Ubuntu 26.10بررسی‌شده با مستندات رسمی ·

از اینجا شروع کن

در پایان چه چیزی خواهی داشت؟

یک سایت وردپرس با HTTPS و هویت PHP اختصاصی داشته باشید و کش محدود عمومی را از راه بدنهٔ واقعی پاسخ و جداسازی کاربران بسنجید.

01مرورگر HTTPSTLS و محدودیت موقت IP نصب
02NginxGET عمومی مشخص به کش؛ نشست و مسیر خصوصی به bypass
03PHP-FPM + MariaDBسوکت اختصاصی، وردپرس و دیتابیس محلی
سیستم‌عامل‌ها و نسخه‌های پوشش‌داده‌شده

بازبینی ۲۰۲۶-۱۰-۲۰: نصب واقعی WordPress 7.1.3 با PHP-FPM 8.3.6، بستهٔ اوبونتو 8.3.6-0ubuntu0.24.04.12، MariaDB 10.11.14 و Nginx 1.24.0 در فضای شبکهٔ جدا روی Ubuntu 24.04.4 amd64 با مسیر آزمایشی و بسته‌های رسمی استخراج‌شده بررسی شد. جداسازی بدنه و نشست و رمز نوشته، هدرهای خصوصی، شکست، بی‌اعتبارسازی و بازگشت کش موفق بودند. این آزمون نصب کامل APT/systemd، ACME و فایروال عمومی، همهٔ سیستم‌عامل‌ها، بازیابی پشتیبان یا WooCommerce نیست. مسیرهای محیطی همچنان مبتنی بر مستندات‌اند و تصاویر DEMO نمایشی هستند.

قبل از شروع

  • سرور تازهٔ اوبونتو یا دبیان با systemd و sudo و SSH موجود؛ حداقل ۲ گیگابایت RAM و ۱۰ گیگابایت فضای آزاد برای آزمایش کوچک تک‌سایتی؛ ظرفیت تولید جدا محاسبه شود.
  • دامنهٔ تحت اختیار، A و AAAA درست، دسترسی ۸۰ و ۴۴۳، حساب و ایمیل ACME مجاز و IP عمومی سیستم مدیریت.
  • کلاینت Bash/curl و حساب‌های staging با فایل کوکی خصوصی؛ این مسیر سایت یا دیتابیس موجود، CDN، پراکسی یا شنوندهٔ عمومی FPM را فرض نمی‌کند.
  • PHP 8.5 از مخزن همین انتشار؛ سوکت اختصاصی در مرحلهٔ pool ساخته می‌شود.
01

قدم بعدی

بررسی سرور تازه و DNS

این مسیر برای نصب تازهٔ یک سایت است. دامنهٔ نمونه، ایمیل و IP مجاز مدیر را همه‌جا با مقادیر واقعی عوض کنید؛ اگر با IPv6 مدیریت می‌کنید، همان IP دقیق نیز مجاز شود. رکوردهای A و AAAA باید به این سرور برسند. در فایروال موجود دسترسی ۸۰ و ۴۴۳ را با حفظ SSH فراهم کنید. تنظیم سایت یا دیتابیس دیگری را بازنویسی و فایروال تازه را بدون بررسی فعال نکنید. حضور CDN یا پراکسی به طراحی جداگانهٔ اعتماد نیاز دارد. تا پایان نصب، محدودیت IP را نگه دارید.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

cat /etc/os-release
uname -m
sudo ss -lntup
sudo nginx -T 2>/dev/null | head -80
getent ahosts blog.example.com
از کجا بدانم درست انجام شد؟

سیستم‌عامل و DNS درست‌اند و مسیرها و پورت‌ها با نصب دیگری تداخل ندارند.

02

قدم بعدی

نصب بسته‌های رسمی توزیع

بسته‌ها از مخزن امضاشدهٔ همان انتشار نصب می‌شوند؛ مخزن نسخهٔ دیگر یا PPA بررسی‌نشده را جایگزین نکنید. PHP در اوبونتو ۲۶٫۱۰ و ۲۶٫۰۴ برابر ۸٫۵، در ۲۴٫۰۴ برابر ۸٫۳، در دبیان ۱۳ برابر ۸٫۴ و در دبیان ۱۲ برابر ۸٫۲ است. pool اختصاصی، سوکت ثابت این آموزش را می‌سازد و نام سوکت پیش‌فرض حدس زده نمی‌شود. نصب بسته ممکن است سرویس را روشن کند؛ هنوز پوشهٔ وردپرس وجود ندارد. پورت دیتابیس باید فقط محلی یا غیرفعال باشد.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

(
set -euo pipefail
sudo apt-get update
sudo apt-get install -y nginx mariadb-server php8.5-fpm php8.5-cli php8.5-mysql php8.5-curl php8.5-gd php8.5-mbstring php8.5-xml php8.5-zip php8.5-intl curl ca-certificates tar rsync openssl nano certbot
php8.5 -v
mariadb --version
nginx -v
sudo systemctl enable --now mariadb php8.5-fpm nginx
sudo ss -lntp 'sport = :3306'
)
از کجا بدانم درست انجام شد؟

APT بدون خطای امضا یا وابستگی تمام می‌شود؛ نسخه‌ها را ثبت کنید. شنوندهٔ عمومی ۳۳۰۶ وجود ندارد.

ورود به کنسول محلی دیتابیس

در نصب تازه، ورود مدیر محلی معمولاً با احراز هویت سوکت یونیکس انجام می‌شود. تاریخچهٔ SQL را غیرفعال کنید و رمز واقعی را در آرگومان پوسته ننویسید.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

sudo env MYSQL_HISTFILE=/dev/null mariadb
از کجا بدانم درست انجام شد؟

بررسی موفق است؛ در صورت خطا، پیش از ادامه علت را برطرف کنید.

03

قدم بعدی

ساخت کاربر محدود به دیتابیس سایت

در کنسول، رمز تصادفی اختصاصی ۶۴ نویسه‌ای hexadecimal از مدیر رمز جایگزین کنید؛ فقط 0–9 و a–f تا در رشتهٔ SQL و PHP نیاز به escape نداشته باشد. کاربر فقط مجوزهای لازم برای جدول‌های همین وردپرس دارد؛ مدیریت سراسری، ورود راه دور و GRANT OPTION ندارد. GRANT بلافاصله اعمال می‌شود و FLUSH PRIVILEGES لازم نیست. رمز را برای فایل تنظیمات نگه دارید. اگر نام دیتابیس یا کاربر از قبل وجود دارد، ادامه ندهید.

SQL

کنسول مدیر MariaDB که در پنل قبل باز شد؛ نه Bash.

CREATE DATABASE wordpress CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wp_site'@'localhost' IDENTIFIED BY 'REPLACE_WITH_A_LONG_RANDOM_PASSWORD';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX
  ON wordpress.* TO 'wp_site'@'localhost';
SHOW GRANTS FOR 'wp_site'@'localhost';
EXIT;
از کجا بدانم درست انجام شد؟

SHOW GRANTS دسترسی همین دیتابیس را نشان می‌دهد و مجوز مدیریت سراسری ندارد.

04

قدم بعدی

بررسی هویت و دسترسی شبکهٔ دیتابیس

به پوسته برگردید و رمز را فقط در prompt تعاملی وارد کنید. اگر ۳۳۰۶ عمومی است، ابتدا bind و سیاست شبکهٔ MariaDB را اصلاح کنید؛ سایت به دیتابیس عمومی احتیاج ندارد.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

mariadb -u wp_site -p wordpress -e 'SELECT DATABASE(), CURRENT_USER();'
sudo mariadb -e 'SELECT @@bind_address;'
sudo ss -lntp 'sport = :3306'
از کجا بدانم درست انجام شد؟

دیتابیس wordpress، هویت wp_site@localhost و شنونده محلی است.

05

قدم بعدی

دریافت و کنترل فایل‌های وردپرس

کد PHP آرشیو مستقل از معماری است. تا HTTPS و هر دو checksum تأیید نشده‌اند، استخراج و نصب انجام نمی‌شود. SHA1 از نشانی رسمی انتشار آمده و SHA256 از همان آرشیو تأییدشده در این بازبینی محاسبه شده؛ SHA256 را امضای ناشر معرفی نمی‌کنیم. این کنترل‌ها ناشر آلوده را پوشش نمی‌دهند. برای استفادهٔ بعدی، انتشار جاری را دوباره بررسی کنید. کد متعلق به root است و فقط uploads برای هویت PHP قابل‌نوشتن خواهد بود.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

(
set -euo pipefail
umask 077
work=$(mktemp -d)
cd "$work"
curl --proto '=https' --proto-redir '=https' -fL --connect-timeout 10 --max-time 180 -o wordpress-7.1.3.tar.gz https://wordpress.org/wordpress-7.1.3.tar.gz
curl --proto '=https' --proto-redir '=https' -fL --connect-timeout 10 --max-time 60 -o wordpress.sha1 https://wordpress.org/wordpress-7.1.3.tar.gz.sha1
[ "$(tr -d '\r\n ' < wordpress.sha1)" = 491a78c7676b15dffcc69130c6054b8f49228a91 ]
printf '%s  %s\n' 491a78c7676b15dffcc69130c6054b8f49228a91 wordpress-7.1.3.tar.gz | sha1sum --check --strict -
printf '%s  %s\n' d2a09acb6a15e3b9c471d72557753c266d6f41a79ed19bfe04cbfe49e283b2a5 wordpress-7.1.3.tar.gz | sha256sum --check --strict -
tar -xzf wordpress-7.1.3.tar.gz
sudo test ! -e /var/www/wordpress
sudo adduser --system --no-create-home --group wp-php
sudo install -d -o root -g root -m 0755 /var/www/wordpress
sudo rsync -a --chown=root:root wordpress/ /var/www/wordpress/
sudo find /var/www/wordpress -type d -exec chmod 0755 {} +
sudo find /var/www/wordpress -type f -exec chmod 0644 {} +
sudo install -d -o wp-php -g www-data -m 0755 /var/www/wordpress/wp-content/uploads
printf 'Retained verified download: %s\n' "$work"
)
از کجا بدانم درست انجام شد؟

هر دو کنترل OK می‌دهند، مقصد تازه ایجاد و آرشیو تأییدشده حفظ می‌شود.

بازکردن pool اختصاصی PHP

در این آزمایش، وجود pool پیش‌فرض تداخلی ندارد؛ سایت از سوکت آن استفاده نمی‌کند. pool سایت دیگری را غیرفعال نکنید. متن پنل بعدی را در nano با Ctrl+O، Enter و Ctrl+X ذخیره کنید.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

sudo nano /etc/php/8.5/fpm/pool.d/wordpress.conf
از کجا بدانم درست انجام شد؟

بررسی موفق است؛ در صورت خطا، پیش از ادامه علت را برطرف کنید.

06

قدم بعدی

اجرای PHP با هویت اختصاصی

مسیر فایل باید متعلق به نسخهٔ انتخاب‌شده باشد. نام سوکت ثابت است، اما مسیر و سرویس نسخه‌دار با سیستم‌عامل تغییر می‌کند. پنج worker و ۱۲۸ مگابایت نقطهٔ شروع آزمایش کوچک‌اند، نه تضمین ظرفیت تولید.

INI · /etc/php/8.5/fpm/pool.d/wordpress.conf

محتوای فایل؛ در فایل مشخص‌شده ذخیره شود، نه در پوسته.

[wordpress]
user = wp-php
group = wp-php
listen = /run/php/wordpress.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = ondemand
pm.max_children = 5
pm.process_idle_timeout = 10s
pm.max_requests = 500
chdir = /var/www/wordpress
security.limit_extensions = .php
php_admin_value[upload_max_filesize] = 16M
php_admin_value[post_max_size] = 16M
php_admin_value[memory_limit] = 128M
php_admin_flag[display_errors] = off
php_admin_flag[log_errors] = on
از کجا بدانم درست انجام شد؟

pool با wp-php اجرا می‌شود و سوکت ۰۶۶۰ در اختیار www-data است.

07

قدم بعدی

اعتبارسنجی PHP پیش از restart

restart برای مدتی کوتاه همهٔ poolهای این نسخه را قطع می‌کند. پیش از آن، آزمون تنظیمات باید موفق شود.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

(
set -euo pipefail
sudo php-fpm8.5 -t
sudo systemctl restart php8.5-fpm
systemctl is-active php8.5-fpm
sudo stat -c '%U:%G %a %n' /run/php/wordpress.sock
)
از کجا بدانم درست انجام شد؟

آزمون موفق است، سرویس فعال و سوکت www-data:www-data با مجوز ۶۶۰ است.

تولید مقادیر تصادفی احراز هویت

این فرمان خصوصی اجرا شود؛ خروجی در تصویر، گزارش یا Git قرار نگیرد. برای هر یک از هشت جایگاه بعدی، یک مقدار متفاوت بردارید.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

for n in $(seq 1 8); do openssl rand -hex 48; done
از کجا بدانم درست انجام شد؟

بررسی موفق است؛ در صورت خطا، پیش از ادامه علت را برطرف کنید.

ساخت فایل محافظت‌شدهٔ تنظیمات

این مرحله فقط برای نصب تازه است؛ guard فایل موجود را نمی‌پذیرد. روی سایت فعال تکرار نکنید. پنل بعدی محتوای فایل است و فرمان پوسته نیست.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

(
set -euo pipefail
sudo test ! -e /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
)
از کجا بدانم درست انجام شد؟

بررسی موفق است؛ در صورت خطا، پیش از ادامه علت را برطرف کنید.

08

قدم بعدی

تنظیم اتصال وردپرس به دیتابیس

همهٔ جایگاه‌ها و دو مقدار دامنه را عوض کنید. پیشوند جدول کنترل امنیتی نیست. TLS در همین Nginx پایان می‌یابد؛ به هدر دلخواه کاربر برای تشخیص HTTPS اعتماد نکنید. ویرایشگر کد پیشخوان خاموش است. کدهای متعلق به root به به‌روزرسانی منظم مدیریتی نیاز دارند؛ برای رفع پیام به‌روزرسانی، کل درخت را قابل‌نوشتن نکنید.

PHP · /var/www/wordpress/wp-config.php

محتوای فایل؛ در فایل مشخص‌شده ذخیره شود، نه در پوسته.

<?php
define( 'DB_NAME', 'wordpress' );
define( 'DB_USER', 'wp_site' );
define( 'DB_PASSWORD', 'REPLACE_WITH_THE_DATABASE_PASSWORD' );
define( 'DB_HOST', 'localhost' );
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';
از کجا بدانم درست انجام شد؟

بررسی موفق است؛ در صورت خطا، پیش از ادامه علت را برطرف کنید.

09

قدم بعدی

کنترل نحو و مجوزها

این کنترل‌ها محتوای خصوصی تنظیمات را چاپ نمی‌کنند.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

(
set -euo pipefail
sudo php8.5 -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 تنظیمات را می‌خواند اما نمی‌نویسد؛ uploads قابل‌نوشتن است و جایگاه خالی باقی نمانده.

آماده‌سازی پوشه و سیاست کش

پوشه در اختیار worker است. تنظیم پیش‌فرض توزیع، conf.d را داخل http وارد می‌کند؛ آزمون کامل این فرض را بررسی می‌کند. map را داخل server نگذارید.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

sudo install -d -o www-data -g www-data -m 0700 /var/cache/nginx/wordpress
sudo nano /etc/nginx/conf.d/wordpress-cache.conf
از کجا بدانم درست انجام شد؟

بررسی موفق است؛ در صورت خطا، پیش از ادامه علت را برطرف کنید.

10

قدم بعدی

کش فقط برای صفحات عمومیِ مشخص

تنها صفحهٔ نخست و sample-page مجازند. تمام مسیرهای دیگر، از جمله ورود و مدیریت، پیش‌نمایش، API، cron، خرید و حساب، خوراک، جست‌وجو و برگهٔ رمزدار خارج از فهرست‌اند. هر کوکی، Authorization، پارامتر، روش غیر از GET یا مسیر percent-encoded هم خواندن و هم نوشتن کش را متوقف می‌کند. کوکی ورود، رمز نوشته، دیدگاه و نشست ناشناختهٔ افزونه نیز پوشش داده می‌شود. پیش از خصوصی یا رمزدارکردن صفحه، آن را از فهرست بردارید و نسل کلید را عوض کنید. محتوای وابسته به مکان، دستگاه یا کاربر را بدون بررسی جداگانهٔ کلید و هدرها اضافه نکنید. حتی UTM عمداً bypass می‌شود؛ نادیده‌گرفتن آن نیازمند بررسی برنامه است.

Nginx · /etc/nginx/conf.d/wordpress-cache.conf

محتوای فایل؛ در فایل مشخص‌شده ذخیره شود، نه در پوسته.

# http context: included by the stock Debian/Ubuntu 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;
}
از کجا بدانم درست انجام شد؟

سقف کش ۵۱۲ و ناحیهٔ کلید ۲۰ مگابایت تعریف شده؛ کش سایت هنوز خاموش است.

آماده‌سازی HTTP فقط برای چالش

پورت ۸۰ باید برای مرجع صدور گواهی باز باشد. اگر گواهی از قبل مدیریت می‌شود، از همان روند مجاز و مسیر درست استفاده کنید و صدور موازی نسازید.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

sudo install -d -o root -g root -m 0755 /var/lib/letsencrypt/.well-known/acme-challenge
sudo nano /etc/nginx/sites-available/wordpress
از کجا بدانم درست انجام شد؟

بررسی موفق است؛ در صورت خطا، پیش از ادامه علت را برطرف کنید.

11

قدم بعدی

نوشتن سایت موقت HTTP

دامنه را عوض کنید. در HTTP فقط چالش ACME ارائه می‌شود و وردپرس در دسترس عمومی نیست.

Nginx · /etc/nginx/sites-available/wordpress

محتوای فایل؛ در فایل مشخص‌شده ذخیره شود، نه در پوسته.

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; }
}
از کجا بدانم درست انجام شد؟

بررسی موفق است؛ در صورت خطا، پیش از ادامه علت را برطرف کنید.

12

قدم بعدی

فعال‌سازی و آزمون چالش

اگر لینک سایت از قبل وجود دارد، علت را بررسی کنید. reload فقط پس از اعتبارسنجی انجام می‌شود. پاسخ چالش را از بیرون سرور هم ببینید؛ دسترسی محلی برای اثبات دسترسی CA کافی نیست.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

(
set -euo pipefail
sudo test ! -e /etc/nginx/sites-enabled/wordpress
sudo ln -s /etc/nginx/sites-available/wordpress /etc/nginx/sites-enabled/wordpress
sudo nginx -t
sudo systemctl reload nginx
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
)
از کجا بدانم درست انجام شد؟

اعتبارسنجی موفق است و بدنه دقیقاً acme-path-check است.

13

قدم بعدی

دریافت TLS پیش از نصب وردپرس

ایمیل و دامنه را عوض کنید و پیش از agree-tos شرایط CA را بخوانید و بپذیرید. فرمان حساب و گواهی واقعی ایجاد می‌کند. چالش از HTTP موجود می‌گذرد. شکست صدور پیش از ویرایشگر متوقف می‌شود؛ پس از موفقیت پنل بعدی را جایگزین کنید. نصب را با استثنای خطای گواهی مرورگر یا curl -k ادامه ندهید.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

(
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/sites-available/wordpress
)
از کجا بدانم درست انجام شد؟

بررسی موفق است؛ در صورت خطا، پیش از ادامه علت را برطرف کنید.

14

قدم بعدی

حفاظت از نصب با TLS و محدودیت IP

دامنه، مسیر گواهی و IP مدیر را عوض کنید و کش را خاموش نگه دارید. محدودیت سطح server همهٔ فایل‌های ایستا و PHP را می‌پوشاند. اجرای PHP در uploads و دریافت تنظیمات و فایل‌های مخفی ممنوع است. HTTP پس از چالش به دامنهٔ ثابت HTTPS می‌رود.

Nginx · /etc/nginx/sites-available/wordpress

محتوای فایل؛ در فایل مشخص‌شده ذخیره شود، نه در پوسته.

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/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;
    }
}
از کجا بدانم درست انجام شد؟

بررسی موفق است؛ در صورت خطا، پیش از ادامه علت را برطرف کنید.

15

قدم بعدی

آزمون TLS و دسترسی نصب‌کننده

اعتبارسنجی و reload روی سرور است. اگر خروجی سرور مجاز نیست، curl HTTPS را از سیستم مجاز اجرا کنید. شما باید نصب‌کننده را با ۲۰۰ و مبدأ مستقلِ غیرمجاز باید ۴۰۳ ببیند. برای عبور آزمون مجوز عمومی ندهید.

Bash

ترمینال سرور، مدیر دارای sudo.

(
set -euo pipefail
sudo nginx -t
sudo systemctl reload nginx
sudo ss -lntp 'sport = :443'
)
از کجا بدانم درست انجام شد؟

گواهی معتبر با دامنه تطبیق دارد و نصب‌کننده فقط از IP تعیین‌شده باز است.

کنترل پاسخ ۲۰۰ نصب‌کننده از کلاینت مجاز

از سیستم مجاز اجرا کنید؛ کنترل گواهی روشن بماند. از شبکهٔ مستقل غیرمجاز همین بررسی را با انتظار ۴۰۳ تکرار کنید؛ آن پاسخ منفی مجوز ادامهٔ نصب از مبدأ غیرمجاز نیست.

Bash

سیستم مجاز، Bash دارای curl.

(
set -euo pipefail
work=$(mktemp -d)
code=$(curl -sS --connect-timeout 5 --max-time 20 -D "$work/installer.h" -o "$work/installer.html" -w '%{http_code}' https://blog.example.com/wp-admin/install.php)
[ "$code" = 200 ]
grep -F WordPress "$work/installer.html"
)
از کجا بدانم درست انجام شد؟

HTTPS معتبر، پاسخ دقیق ۲۰۰ و بدنهٔ واقعی نصب‌کننده.

16

قدم بعدی

تکمیل نصب پشت محدودیت IP

از سیستم مجاز، نشانی نصب را باز کنید. عنوان سایت، نام مدیر غیرپیش‌فرض، رمز قوی و ایمیل واقعی را بدهید؛ نصب را تمام کنید و پیشخوان را ببینید. اطلاعات ورود در شواهد ثبت نشود. در STAGING برگهٔ نمونهٔ موجود را ویرایش کنید، نه اینکه نامک تکراری بسازید؛ نامک sample-page و متن PUBLIC_CACHE_MARKER باشد. برگهٔ جداگانهٔ Home با متن PUBLIC_HOME_MARKER را در تنظیمات ← خواندن، صفحهٔ نخست ایستا انتخاب کنید. پیوند یکتا را «نام نوشته» بگذارید و ذخیره کنید. برگهٔ sample-page عمومی بماند؛ برگهٔ رمزدار جدا با نامک locked-test و متن LOCKED_CONTENT_MARKER بسازید. نام نمایشی دو حساب آزمایشی CACHE_USER_A و CACHE_USER_B باشد و نوار ابزار سایت در شناسنامهٔ هر دو روشن شود. هر حساب باید نام خودش و پنجرهٔ خصوصی هیچ‌کدام را نشان دهد. آزمون‌های بعدی بر همین تفاوت واقعی بدنه تکیه دارند.

URL

نوار نشانی مرورگر سیستم مجاز؛ نه پوستهٔ سرور.

https://blog.example.com/wp-admin/install.php
از کجا بدانم درست انجام شد؟

پیشخوان و جدول‌ها آماده‌اند و برگه‌های عمومی و رمزدارِ جدا، با کش خاموش درست نمایش داده می‌شوند.

17

قدم بعدی

بررسی دیتابیس و بدنهٔ عمومی

curl از IP مجاز اجرا شود. عنوان واقعی سایت را جایگزین کنید. پاسخ ۲۰۰ و بدنهٔ منطبق لازم است؛ هدایت به نصب موفقیت نیست.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

(
set -euo pipefail
code=$(curl -sS --connect-timeout 5 --max-time 20 -o /tmp/wp-installed-public.html -w '%{http_code}' https://blog.example.com/)
[ "$code" = 200 ]
grep -F 'YOUR_PUBLIC_SITE_TITLE' /tmp/wp-installed-public.html
sudo mariadb wordpress -e "SHOW TABLES LIKE 'wp_options';"
)
از کجا بدانم درست انجام شد؟

عنوان در صفحهٔ نخست است و wp_options وجود دارد.

نگه‌داری نقطهٔ بازگشت تنظیمات

نسخهٔ خصوصی root ساختار مسیرها را نگه می‌دارد و پیش از ویرایشگر هر دو فایل را مقایسه می‌کند. نشانی چاپ‌شده را ثبت کنید؛ این پشتیبان تنظیمات است، نه دیتابیس و بارگذاری‌ها.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

sudo sh -eu <<'SAVE'
umask 077
install -d -m 0700 /root/academy-backups
backup=$(mktemp -d /root/academy-backups/wp-nginx-XXXXXXXX)
cp -a --parents /etc/nginx/sites-available/wordpress /etc/nginx/conf.d/wordpress-cache.conf "$backup/"
cmp /etc/nginx/sites-available/wordpress "$backup/etc/nginx/sites-available/wordpress"
cmp /etc/nginx/conf.d/wordpress-cache.conf "$backup/etc/nginx/conf.d/wordpress-cache.conf"
printf 'Verified pre-cache configuration: %s\n' "$backup"
SAVE
sudo nano /etc/nginx/sites-available/wordpress
از کجا بدانم درست انجام شد؟

بررسی موفق است؛ در صورت خطا، پیش از ادامه علت را برطرف کنید.

18

قدم بعدی

فعال‌سازی کش عمومیِ بررسی‌شده

در location موجود PHP فقط خط خاموش‌بودن کش را عوض کنید. هر دو کنترل bypass و no-cache و محدودیت IP آزمایش باقی بمانند. رفتار پیش‌فرض Nginx برای Set-Cookie، کنترل‌های خصوصی Cache-Control، Vary و X-Accel-Expires حفظ شود. برای گرفتن HIT هدرها را نادیده نگیرید و کوکی را پنهان نکنید. پاسخ عمومی ۲۰۰ در نبود زمان اعتبار بالادست، TTL شصت‌ثانیه‌ای دارد؛ هدر بالادست می‌تواند اولویت داشته باشد. افزونهٔ شخصی‌سازِ فاقد کوکی یا هدر درست با یک هدر کش امن نمی‌شود؛ چنین URLی را مجاز نکنید.

Nginx · /etc/nginx/sites-available/wordpress (existing PHP location)

محتوای فایل؛ در فایل مشخص‌شده ذخیره شود، نه در پوسته.

# In the PHP location, replace ONLY the existing cache-off line:
fastcgi_cache WORDPRESS;
از کجا بدانم درست انجام شد؟

بررسی موفق است؛ در صورت خطا، پیش از ادامه علت را برطرف کنید.

19

قدم بعدی

اعتبارسنجی پیش از reload

شکست آزمون مانع reload می‌شود. reload موفق اعتبار تنظیمات را نشان می‌دهد؛ حریم خصوصی را با بدنه بسنجید.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

(
set -euo pipefail
sudo nginx -t
sudo systemctl reload nginx
systemctl is-active nginx
)
از کجا بدانم درست انجام شد؟

بررسی موفق است؛ در صورت خطا، پیش از ادامه علت را برطرف کنید.

20

قدم بعدی

آزمون مستقل MISS و HIT مهمان

در staging بلافاصله پس از نسل تازهٔ کلید و پیش از گرم‌شدن برگه اجرا کنید. انتظار MISS سپس HIT و بدنه‌های عمومی برابر است. اگر از قبل گرم شده، با پنل نگه‌داری نسل استفاده‌نشده بگذارید و یک بار تکرار کنید؛ فایل کش فعال را حذف نکنید. curl باید از مبدأ مجاز بیاید.

Bash

کلاینت مجاز staging، Bash/curl؛ فایل کوکی خصوصی و محلی همان سیستم است.

(
set -euo pipefail
umask 077
work=$(mktemp -d)
base=https://blog.example.com
for n in 1 2; do
  code=$(curl -sS --connect-timeout 5 --max-time 20 -H 'Cookie:' -H 'Authorization:' -D "$work/$n.h" -o "$work/$n.html" -w '%{http_code}' "$base/sample-page/")
  [ "$code" = 200 ]
  grep -i '^X-WP-Cache:' "$work/$n.h"
done
grep -Eiq '^X-WP-Cache: MISS[[:space:]]*$' "$work/1.h"
grep -Eiq '^X-WP-Cache: HIT[[:space:]]*$' "$work/2.h"
cmp "$work/1.html" "$work/2.html"
grep -F 'PUBLIC_CACHE_MARKER' "$work/2.html"
if grep -Fq 'CACHE_USER_A' "$work/2.html"; then
  echo 'Unexpected cached/private content or status' >&2; exit 1
else
  rc=$?; [ "$rc" = 1 ] || exit "$rc"
fi
printf 'Retained anonymous evidence: %s\n' "$work"
)
از کجا بدانم درست انجام شد؟

دو پاسخ ۲۰۰، ابتدا MISS سپس HIT و بدنهٔ عمومی برابر بدون نشانگر کاربر.

21

قدم بعدی

آزمون نشست واقعی و جداسازی کاربران

با ابزار نشست مورداعتماد خود، دو ورود واقعی staging را خصوصی در فایل کوکی با فرمت Netscape صادر کنید. آزمون در Bash/curl سیستم مجاز است و مسیر فایل‌ها محلیِ همان کلاینت است. کوکی ورود ساختگی اثبات احراز هویت نیست. نوار ابزار و نام نمایشی باید شناخت هر حساب توسط وردپرس را نشان دهد؛ BYPASS تنها کافی نیست. بدنه‌ها خصوصی بمانند. کوکی‌های ساختگی رمز نوشته، سبد و افزونه فقط سیاست Nginx را می‌سنجند و آزمون ورود یا خرید واقعی نیستند.

Bash

سیستم مجاز، Bash/curl و فایل کوکی خصوصی نشست‌های واقعی staging.

(
set -euo pipefail
umask 077
work=$(mktemp -d)
base=https://blog.example.com
# Use an EXISTING private Netscape-format jar exported from your staging login.
# It must be root/user readable only. Never paste cookies into source or logs.
jar=$HOME/wp-staging-user-a.cookies
[ -s "$jar" ]
chmod 0600 "$jar"
for n in 1 2; do
  code=$(curl -sS --connect-timeout 5 --max-time 20 -b "$jar" -D "$work/a$n.h" -o "$work/a$n.html" -w '%{http_code}' "$base/sample-page/")
  [ "$code" = 200 ]
  grep -Eiq '^X-WP-Cache: BYPASS[[:space:]]*$' "$work/a$n.h"
  grep -F 'id="wpadminbar"' "$work/a$n.html"
  grep -F 'CACHE_USER_A' "$work/a$n.html"
  if grep -Fq 'CACHE_USER_B' "$work/a$n.html"; then
    echo 'Unexpected cached/private content or status' >&2; exit 1
  else
    rc=$?; [ "$rc" = 1 ] || exit "$rc"
  fi
done
code=$(curl -sS --connect-timeout 5 --max-time 20 -H 'Cookie:' -D "$work/public.h" -o "$work/public.html" -w '%{http_code}' "$base/sample-page/")
[ "$code" = 200 ]
grep -F 'PUBLIC_CACHE_MARKER' "$work/public.html"
if grep -Fq 'CACHE_USER_A' "$work/public.html"; then
  echo 'Unexpected cached/private content or status' >&2; exit 1
else
  rc=$?; [ "$rc" = 1 ] || exit "$rc"
fi
for cookie in 'wp-postpass_demo=test' 'woocommerce_items_in_cart=1' 'plugin_session=test'; do
  code=$(curl -sS --connect-timeout 5 --max-time 20 -b "$cookie" -D "$work/bypass.h" -o "$work/bypass.html" -w '%{http_code}' "$base/sample-page/")
  [ "$code" = 200 ]
  grep -Eiq '^X-WP-Cache: BYPASS[[:space:]]*$' "$work/bypass.h"
done
jar_b=$HOME/wp-staging-user-b.cookies
[ -s "$jar_b" ]
chmod 0600 "$jar_b"
code=$(curl -sS --connect-timeout 5 --max-time 20 -b "$jar_b" -D "$work/b.h" -o "$work/b.html" -w '%{http_code}' "$base/sample-page/")
[ "$code" = 200 ]
grep -Eiq '^X-WP-Cache: BYPASS[[:space:]]*$' "$work/b.h"
grep -F 'CACHE_USER_B' "$work/b.html"
if grep -Fq 'CACHE_USER_A' "$work/b.html"; then
  echo 'Unexpected cached/private content or status' >&2; exit 1
else
  rc=$?; [ "$rc" = 1 ] || exit "$rc"
fi
code=$(curl -sS --connect-timeout 5 --max-time 20 -H 'Cookie:' -D "$work/public-after-b.h" -o "$work/public.html" -w '%{http_code}' "$base/sample-page/")
[ "$code" = 200 ]
grep -F PUBLIC_CACHE_MARKER "$work/public.html"
if grep -Fq CACHE_USER_A "$work/public.html"; then
  echo 'Unexpected cached/private content or status' >&2; exit 1
else
  rc=$?; [ "$rc" = 1 ] || exit "$rc"
fi
if grep -Fq 'CACHE_USER_B' "$work/public.html"; then
  echo 'Unexpected cached/private content or status' >&2; exit 1
else
  rc=$?; [ "$rc" = 1 ] || exit "$rc"
fi
printf 'Private evidence retained at %s; inspect locally, do not publish cookies/bodies.\n' "$work"
)
از کجا بدانم درست انجام شد؟

حساب‌های A و B با BYPASS فقط هویت خود را می‌بینند و پاسخ مهمان بعدی هیچ‌کدام را ندارد.

22

قدم بعدی

کنترل پارامترها و مسیرهای پویا

پیش‌نمایش ممکن است به ورود هدایت شود. statusهای محدود رفتار ورود و دسترسی را می‌پذیرند، اما BYPASS لازم است و 5xx یا شکست ارتباط رد می‌شود. بدنه نیز باید متعلق به همان مسیر باشد. هر query string از جمله UTM به‌جای حذف پنهانی، bypass می‌شود.

Bash

کلاینت مجاز staging، Bash/curl؛ فایل کوکی خصوصی و محلی همان سیستم است.

(
set -euo pipefail
work=$(mktemp -d)
n=0
for target in '/sample-page/?preview=true' '/sample-page/?utm_source=qa' '/wp-login.php' '/wp-json/'; do
  n=$((n + 1))
  code=$(curl -sS --connect-timeout 5 --max-time 20 -D "$work/$n.h" -o "$work/$n.body" -w '%{http_code}' "https://blog.example.com$target")
  case "$code" in 200|301|302|401|403|404) ;; *) echo "Unexpected status $code" >&2; exit 1;; esac
  grep -Eiq '^X-WP-Cache: BYPASS[[:space:]]*$' "$work/$n.h"
  printf '%s -> %s, BYPASS\n' "$target" "$code"
done
)
از کجا بدانم درست انجام شد؟

بررسی موفق است؛ در صورت خطا، پیش از ادامه علت را برطرف کنید.

23

قدم بعدی

تفکیک کلید صفحات بازنویسی‌شده و روش درخواست

کلید شامل scheme، روش، دامنهٔ کنترل‌شده، URI اصلی و نسل است. بازنویسی پیوند یکتا نباید کلید همهٔ صفحات را index.php کند. فقط GET مجاز است؛ HEAD نیز عمداً bypass می‌شود تا پاسخِ بدون بدنه کلید GET را پر نکند. POST خالیِ مهمان به برگهٔ عمومی آزمایش اقدام فرم ندارد و باید bypass شود. Nginx برای POST از موتور کش عبور نمی‌کند؛ بنابراین هدر ممکن است به‌جای BYPASS غایب باشد، اما پاسخ کش‌شدهٔ HIT یا STALE مجاز نیست.

Bash

کلاینت مجاز staging، Bash/curl؛ فایل کوکی خصوصی و محلی همان سیستم است.

(
set -euo pipefail
work=$(mktemp -d)
code=$(curl -sS --connect-timeout 5 --max-time 20 -D "$work/home.h" -o "$work/home.html" -w '%{http_code}' https://blog.example.com/)
[ "$code" = 200 ]
grep -F PUBLIC_HOME_MARKER "$work/home.html"
if grep -Fq PUBLIC_CACHE_MARKER "$work/home.html"; then
  echo 'Unexpected cached/private content or status' >&2; exit 1
else
  rc=$?; [ "$rc" = 1 ] || exit "$rc"
fi
code=$(curl -sS --connect-timeout 5 --max-time 20 -X POST -d '' -D "$work/post.h" -o "$work/post.html" -w '%{http_code}' https://blog.example.com/sample-page/)
[ "$code" = 200 ]
if grep -Eiq '^X-WP-Cache: (HIT|STALE|UPDATING|REVALIDATED)[[:space:]]*$' "$work/post.h"; then
  echo 'Unexpected cached/private content or status' >&2; exit 1
else
  rc=$?; [ "$rc" = 1 ] || exit "$rc"
fi
grep -F PUBLIC_CACHE_MARKER "$work/post.html"
curl -sS --head --connect-timeout 5 --max-time 20 https://blog.example.com/sample-page/
)
از کجا بدانم درست انجام شد؟

کلید دو صفحه متفاوت است؛ POST بدنهٔ عمومی درست دارد و از کش نیست؛ HEAD بدون بدنه bypass می‌شود.

24

قدم بعدی

اثبات جداسازی محتوای رمزدار

برگهٔ locked-test در فهرست مجاز نیست. رمز آزمایشی آن را در مرورگر امن وارد و کوکی واقعی post-password را خصوصی صادر کنید. مهمان باید فرم را بدون متن محافظت‌شده ببیند؛ نشست دارای رمز متن را ببیند؛ مهمان تازه باز هم آن را نبیند. پیش از توسعهٔ تنظیمات، گردش خرید و حساب و افزونه‌های واقعی را با کاربران مجزا بسنجید. این آموزش WooCommerce نصب نمی‌کند و رفتار افزونهٔ دیگر را اثبات نمی‌کند.

Bash

کلاینت مجاز staging، Bash/curl؛ فایل کوکی خصوصی و محلی همان سیستم است.

(
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
)
از کجا بدانم درست انجام شد؟

همه BYPASS هستند و فقط نشست دارای رمز، نشانگر متن محافظت‌شده را می‌بیند.

25

قدم بعدی

بازکردن سایت آماده برای بازدیدکنندگان

پس از موفقیت نصب و آزمون‌های staging، فقط خطوط allow/deny موقتِ سطح server در HTTPS را حذف کنید. محافظت location برای تنظیمات و فایل مخفی و تمام قواعد bypass باقی بمانند. پنل قبلی اعتبارسنجی و reload را اجرا و از اتصال مستقل، پاسخ عمومی ۲۰۰ و ورود را بررسی کنید. کش بیرونی CDN نباید نتیجهٔ آزمون مبدأ را بپوشاند.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

sudo nano /etc/nginx/sites-available/wordpress
از کجا بدانم درست انجام شد؟

سایت کامل HTTPS عمومی است؛ نشست و مسیر خصوصی همچنان کش نمی‌شوند.

آماده‌سازی hook تمدید

مسیر چالش HTTP حفظ شود. timer موجود بستهٔ Certbot مسئول تمدید است؛ زمان‌بند موازی نسازید. hook بعدی پیش از reload گواهی جدید، Nginx را اعتبارسنجی می‌کند.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

sudo install -d -m 0755 /etc/letsencrypt/renewal-hooks/deploy
sudo nano /etc/letsencrypt/renewal-hooks/deploy/20-nginx-validated
از کجا بدانم درست انجام شد؟

بررسی موفق است؛ در صورت خطا، پیش از ادامه علت را برطرف کنید.

اعتبارسنجی استقرار گواهی تمدیدشده

این متن محتوای فایل است؛ آن را در نشست پوستهٔ نامرتبط اجرا نکنید.

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

محتوای فایل؛ در فایل مشخص‌شده ذخیره شود، نه در پوسته.

#!/bin/sh
set -eu
/usr/sbin/nginx -t
/bin/systemctl reload nginx
از کجا بدانم درست انجام شد؟

بررسی موفق است؛ در صورت خطا، پیش از ادامه علت را برطرف کنید.

26

قدم بعدی

آزمون تمدید و زمان‌بندی

اجرای مستقیم hook فقط پس از اعتبارسنجی reload می‌کند. dry-run با ACME آزمایشی ممکن است طول بکشد و معمولاً deploy hook را اجرا نمی‌کند؛ فراخوانی مستقیم، رفتار hook را جدا می‌سنجد.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

(
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
systemctl list-timers --all | grep certbot
)
از کجا بدانم درست انجام شد؟

hook و dry-run موفق‌اند و timer موجود زمان اجرا دارد.

بی‌اعتبارکردن کش بدون حذف فایل فعال

وجود API یا ماژول purge را فرض نمی‌کنیم. برای تغییر فوری، پیش از عوض‌شدن حریم صفحه، کش را خاموش یا URL را از فهرست حذف کنید؛ سپس نسل استفاده‌نشدهٔ پنل بعدی را بگذارید و اعتبارسنجی و reload کنید. کل سایت کوچک بی‌اعتبار می‌شود، نه یک URL. فایل قدیمی با کلید تازه خوانده نمی‌شود و با محدودیت حجم و زمان بی‌استفاده منقضی می‌شود. به نسل قدیمی برنگردید. انتشار عادی وردپرس این کش را پاک نمی‌کند؛ محتوا تا TTL واقعی می‌تواند بماند. در staging نشانگر را به PUBLIC_CACHE_MARKER_V2 عوض کنید، نسل تازه بدهید و آزمون مهمان را با نشانگر جدید تکرار کنید؛ متن قبلی نباید باشد.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

sudo nano /etc/nginx/sites-available/wordpress
از کجا بدانم درست انجام شد؟

بررسی موفق است؛ در صورت خطا، پیش از ادامه علت را برطرف کنید.

تغییر نسل کلید کش

خط کلید قبلی را جایگزین کنید؛ خط تکراری اضافه نکنید. نسل قبلاً استفاده‌نشده باشد و سپس اعتبارسنجی و reload کنید.

Nginx · /etc/nginx/sites-available/wordpress (existing PHP location)

محتوای فایل؛ در فایل مشخص‌شده ذخیره شود، نه در پوسته.

# Replace the previous key directive with this NEW generation:
fastcgi_cache_key "$scheme|$request_method|$host|$request_uri|wp-v2";
از کجا بدانم درست انجام شد؟

بررسی موفق است؛ در صورت خطا، پیش از ادامه علت را برطرف کنید.

خاموش‌کردن کش با حفظ سایت

اگر کاربری محتوای دیگری را دید، با خط بعدی کش را فوراً خاموش و اعتبارسنجی و reload کنید. نسخه‌های CDN یا توزیع بیرونی را هم از روند مسئول آن بی‌اعتبار و دامنهٔ افشا را بررسی کنید. فایل وردپرس، دیتابیس، uploads، حساب و گواهی را حفظ کنید. پوشهٔ کش فعال حذف نشود.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

sudo nano /etc/nginx/sites-available/wordpress
از کجا بدانم درست انجام شد؟

بررسی موفق است؛ در صورت خطا، پیش از ادامه علت را برطرف کنید.

27

قدم بعدی

بازگشت به PHP مستقیم بدون حذف داده

فقط خط انتخاب کش را جایگزین کنید؛ تا وجود ارجاع، zone بماند. پس از اعتبارسنجی و reload، بدنه و نشست باید درست باشند؛ این بار X-WP-Cache غایب است. پیش از فعال‌سازی دوباره سیاست را اصلاح و نسل تازه انتخاب کنید. فایل‌های قبلی می‌توانند عادی منقضی شوند.

Nginx · /etc/nginx/sites-available/wordpress (existing PHP location)

محتوای فایل؛ در فایل مشخص‌شده ذخیره شود، نه در پوسته.

# In the PHP location, replace WORDPRESS with off:
fastcgi_cache off;
از کجا بدانم درست انجام شد؟

وردپرس از PHP کار می‌کند؛ کش خاموش و همهٔ داده‌ها حفظ شده‌اند.

28

قدم بعدی

پشتیبان‌گیری پیش از نگه‌داری

مرحلهٔ اختیاری نگه‌داری: ابتدا از روند موجود برای توقف ویرایش محتوا، افزونه، تغییر ساختار و uploads استفاده کنید. dump برای InnoDB سازگاری تراکنشی دارد، اما فایل بارگذاری و جدول غیرتراکنشی افزونه را هم‌زمان نمی‌کند. تغییر درخت فایل، مقایسه را با خطا متوقف می‌کند. این پشتیبان اتمی آنلاین کل سایت نیست؛ پنجرهٔ نگه‌داری یا روند سازگار موجود لازم است. اصل فایل و داده باقی می‌ماند. checksum و خوانایی و مقایسهٔ محلی، قابلیت بازیابی SQL را اثبات نمی‌کند. پیش از بازنشستگی یا مهاجرت مخرب، در محیط بازیابی جدا restore و ورود و محتوا و uploads را بررسی و نسخهٔ رمزگذاری‌شدهٔ خارج از سرور نگه دارید. آزمون restore را روی دیتابیس زنده انجام ندهید. به‌روزرسانی مدیریتی هسته و افزونه از منابع رسمی منظم باشد و پس از آن آزمون حریم خصوصی تکرار شود.

Bash

ترمینال Bash سرور وردپرس، حساب مدیر دارای sudo.

sudo sh -eu <<'BACKUP'
umask 077
install -d -m 0700 /root/academy-backups
backup=$(mktemp -d /root/academy-backups/wordpress-XXXXXXXX)
mariadb-dump --single-transaction --quick --databases wordpress > "$backup/wordpress.sql"
test -s "$backup/wordpress.sql"
tar -C / -cpf "$backup/wordpress-files.tar" var/www/wordpress etc/nginx/sites-available/wordpress etc/nginx/conf.d/wordpress-cache.conf etc/php/8.5/fpm/pool.d/wordpress.conf
cd "$backup"
sha256sum wordpress.sql wordpress-files.tar > SHA256SUMS
sha256sum -c SHA256SUMS
tar -tf wordpress-files.tar >/dev/null
tar -C / -df wordpress-files.tar
printf 'Verified local files and dump: %s\n' "$backup"
BACKUP
از کجا بدانم درست انجام شد؟

آرشیو و dump خصوصی از کنترل محلی عبور می‌کنند و اصل داده حفظ می‌شود؛ آزمون restore جدا لازم است.

عیب‌یابی در مرز خطا

گزارش خصوصی و کوکی محلی بماند. هر بخش، پنل تشخیصی اجرایی جدا دارد.

از کجا بدانم درست انجام شد؟

بررسی موفق است؛ در صورت خطا، پیش از ادامه علت را برطرف کنید.

PHP پاسخ ۵۰۲ می‌دهد

سوکت واقعی، سرویس نسخهٔ انتخابی و مجوز را مقایسه کنید؛ restart مسیر اشتباه را درست نمی‌کند.

Bash
sudo nginx -t
sudo systemctl status php8.5-fpm --no-pager
sudo journalctl -u php8.5-fpm -n 50 --no-pager
sudo stat /run/php/wordpress.sock
sudo -u www-data test -w /run/php/wordpress.sock
اتصال دیتابیس ناموفق است

ورود محلی، مجوز دیتابیس و خوانایی فایل را بدون چاپ رمز بررسی کنید.

Bash
sudo systemctl status mariadb --no-pager
sudo journalctl -u mariadb -n 50 --no-pager
mariadb -u wp_site -p wordpress -e 'SELECT 1;'
sudo -u wp-php test -r /var/www/wordpress/wp-config.php
نبود HIT یا بدنهٔ اشتباه

برای بدنهٔ غلط ابتدا کش را خاموش کنید. کوکی، پارامتر یا هدر خصوصی می‌تواند به‌درستی مانع ذخیره شود. حجم و inode را ببینید؛ برای HIT هدر را حذف نکنید. درخواست ممکن است URL عمومی را گرم کند.

Bash
sudo nginx -t
sudo df -h /var/cache/nginx/wordpress
sudo df -i /var/cache/nginx/wordpress
sudo du -sh /var/cache/nginx/wordpress
curl -sS --connect-timeout 5 --max-time 20 -D - -o /dev/null https://blog.example.com/sample-page/
خطای TLS یا تمدید

DNS، دسترسی چالش و تطبیق گواهی را بررسی کنید؛ علت را اصلاح کنید و کنترل گواهی را دور نزنید.

Bash
getent ahosts blog.example.com
sudo nginx -t
sudo certbot certificates
sudo journalctl -u nginx -n 50 --no-pager

حفظ محدودهٔ روشن بهره‌برداری

ظرفیت PHP، فضای کش و inode و بازیابی تأییدشده را پایش کنید و فهرست مجاز کوچک بماند. object cache، CDN، چندسایتی، اشتراک و WooCommerce طراحی و آزمون گردش واقعی جدا می‌خواهند. HIT عمومی، صحت خرید یا امنیت ورود را ثابت نمی‌کند.

از کجا بدانم درست انجام شد؟

بررسی موفق است؛ در صورت خطا، پیش از ادامه علت را برطرف کنید.

پشتوانهٔ این آموزش

منابع رسمی

وضعیت بررسی هر محیط بالای صفحه مشخص است؛ بررسی مستندات به معنی اجرای عملی همهٔ نسخه‌ها نیست.

ادامهٔ مسیر

قدم بعدی تو

همهٔ آموزش‌های آکادمی ↗