linux version:Rocky linux 9.7
nginx version: nginx/1.20.1
mysql Ver 8.0.46 for Linux on x86_64 (MySQL Community Server – GPL)
php version:PHP 8.0.30
一、环境准备与连接
1.1 使用 Xshell 连接ecs服务器
- 打开 Xshell,新建会话:
- 主机:输入服务器公网 IP
- 端口:22
- 协议:SSH
- 点击“连接”,输入用户名(root)和密码(或选择密钥登录)。
- 成功登录后,你会看到命令行提示符
1.2 更新系统
bash
dnf update -y
一般建议在新部署服务器时先执行一次全面更新,之后根据安全公告和实际需要定期更新。dnf update -y 中的 -y 自动确认,适合在非交互脚本中使用,但生产环境更新前最好先备份或建立快照。
二、安装 Nginx
bash
dnf install -y nginx systemctl enable nginx systemctl start nginx
验证:浏览器访问 http://服务器IP,能看到 Nginx 欢迎页即成功。
三、安装 MySQL 8.0(官方仓库)
3.1 添加 MySQL Yum 仓库
bash
dnf install -y https://dev.mysql.com/get/mysql80-community-release-el9-1.noarch.rpm
3.2 安装 MySQL 服务器
bash
dnf install -y mysql-community-server
Error:gpg密钥验证失败问题,如过期:
检查并更新 GPG 密钥(如遇验证失败)
bash
# 查看当前仓库配置的 gpgkey cat /etc/yum.repos.d/mysql-community.repo | grep gpgkey 如果密钥过期,需要下载新密钥(以 2025 年为例) cd /tmp wget https://repo.mysql.com/RPM-GPG-KEY-mysql-2025 rpm --import RPM-GPG-KEY-mysql-2025 编辑仓库文件,将 gpgkey 指向新密钥 vim /etc/yum.repos.d/mysql-community.repo 将所有 gpgkey= 行改为: gpgkey=https://repo.mysql.com/RPM-GPG-KEY-mysql-2025
3.3 启动并设置开机自启
bash
systemctl enable mysqld systemctl start mysqld
3.4 获取临时密码
bash
grep 'temporary password' /var/log/mysqld.log
复制输出的临时密码。
3.5 修改 root 密码
bash
mysql -u root -p
输入临时密码登录,然后执行:
sql
ALTER USER 'root'@'localhost' IDENTIFIED BY '你的强密码'; FLUSH PRIVILEGES; EXIT;
3.6 创建 WordPress 数据库和用户
bash
mysql -u root -p
输入新密码,执行:
sql
CREATE DATABASE wordpress CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'wpuser'@'localhost' IDENTIFIED BY '你的数据库用户密码'; GRANT ALL PRIVILEGES ON wordpress.* TO 'wpuser'@'localhost'; FLUSH PRIVILEGES; EXIT;
四、安装 PHP 8 及扩展
bash
dnf install -y php php-fpm php-mysqlnd php-gd php-xml php-mbstring php-json php-curl php-zip php-intl php-opcache systemctl enable php-fpm systemctl start php-fpm
使用 Xshell 修改 PHP-FPM 运行用户
vi /etc/php-fpm.d/www.conf- 找到: text user = apache group = apache 修改为: text user = nginx group = nginx
- 保存文件 esc+:wq
- 回到终端执行: bash systemctl restart php-fpm
五、配置 Nginx 虚拟主机
5.1 创建站点目录
bash
mkdir -p /var/www/wordpress
5.2 使用 Xshell 创建并编辑配置文件
- cd
/etc/nginx/conf.d/ - vi
wordpress.conf - 编辑,粘贴以下内容(将
server_name改为你的域名或 IP,若没有域名写_): nginx server { listen 80; server_name your_domain_or_ip; root /var/www/wordpress; index index.php index.html index.htm; location / { try_files $uri $uri/ /index.php?$args; } location ~ .php$ { include fastcgi_params; fastcgi_pass unix:/run/php-fpm/www.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } location ~* .(js|css|png|jpg|jpeg|gif|ico|svg)$ { expires max; log_not_found off; } } - 保存文件
5.3 测试并重载 Nginx
bash
nginx -t systemctl reload nginx
六、下载并配置 WordPress
6.1 下载 WordPress
bash
cd /var/www wget https://wordpress.org/latest.tar.gz tar -xzvf latest.tar.gz rm -f latest.tar.gz
6.2 设置权限
bash
chown -R nginx:nginx /var/www/wordpress
find /var/www/wordpress -type d -exec chmod 755 {} \;
find /var/www/wordpress -type f -exec chmod 644 {} \;
6.3 使用 Xshell 配置 wp-config.php
6.3.1 进入 WordPress 目录
在终端中执行:
bash
cd /var/www/wordpress
6.3.2 复制配置文件
bash
cp wp-config-sample.php wp-config.php
6.3.3 使用 vi 编辑器修改配置文件
执行:
bash
vi wp-config.php
这时会进入 vi 编辑器,全程键盘操作。
vi 基本操作:
- 按
/键进入搜索模式,输入DB_NAME然后回车,光标会跳到第一个匹配处。 - 按
n键跳转到下一个匹配项,直到定位到define( 'DB_NAME', 'database_name_here' );这一行。 - 按
i键进入插入模式(屏幕底部会出现-- INSERT --)。 - 使用方向键移动光标,将
'database_name_here'修改为'wordpress'。 - 按
Esc键退出插入模式。 - 重复上述步骤修改
DB_USER和DB_PASSWORD:- 搜索
DB_USER,将'username_here'改为'wpuser'。 - 搜索
DB_PASSWORD,将'password_here'改为您的数据库用户密码(例如'你的强密码')。
- 搜索
- 同样修改
DB_HOST为'localhost'(通常默认已是,但确认一下)。 - 按
Esc确保退出插入模式。 - 输入
:wq然后按回车,保存并退出。
6.4 替换安全密钥(可选但强烈建议)
ps:
为什么要替换?
具体作用:
- 加密 Cookie:当用户登录 WordPress 后,服务器会生成一个包含用户身份信息的 Cookie。这些密钥用于对该 Cookie 进行哈希和加密,确保 Cookie 无法被篡改或伪造。
- 防止会话劫持:如果没有这些随机密钥,攻击者可能更容易猜测或构造有效的登录 Cookie,从而冒充已登录用户。
- 增强密码存储安全性:虽然 WordPress 密码本身使用哈希存储,但密钥用于生成更复杂的认证令牌,间接提升了整体安全性。
为什么必须更换默认的占位符?
- WordPress 默认配置文件中的密钥是空占位符(如
'put your unique phrase here'),如果保持原样,所有使用该默认配置的站点将共享相同的“密钥”,这使得攻击者可以轻易伪造 Cookie 并入侵任何未修改密钥的站点。 - 更换为随机、唯一的密钥后,你的站点就拥有了独立的加密指纹,大幅提高安全性。
其他好处:
- 强制用户重新登录:如果网站曾经被入侵或怀疑 Cookie 泄露,更换密钥会使所有现有登录 Cookie 失效,用户必须重新登录,从而切断攻击者的会话。
总结:更换安全密钥是 WordPress 安全配置的基本步骤,确保你的站点使用独一无二的加密密钥,防止 Cookie 伪造和会话劫持。强烈建议在安装时或首次配置时完成这一步。
bash
# 备份原文件 cp wp-config.php wp-config.php.bak # 下载密钥并存储到临时文件 curl -s https://api.wordpress.org/secret-key/1.1/salt/ > /tmp/salt.txt # 使用 sed 删除原有的 8 行密钥定义,并在相同位置插入新密钥 sed -i '/AUTH_KEY/d; /SECURE_AUTH_KEY/d; /LOGGED_IN_KEY/d; /NONCE_KEY/d; /AUTH_SALT/d; /SECURE_AUTH_SALT/d; /LOGGED_IN_SALT/d; /NONCE_SALT/d' wp-config.php sed -i "/define( 'DB_COLLATE', '' );/r /tmp/salt.txt" wp-config.php
以上命令会删除所有旧密钥行,并在 DB_COLLATE 行之后插入新的密钥行。完成后可查看确认:
bash
grep -E 'AUTH_KEY|SECURE_AUTH_KEY|LOGGED_IN_KEY|NONCE_KEY|AUTH_SALT|SECURE_AUTH_SALT|LOGGED_IN_SALT|NONCE_SALT' wp-config.php
6.5 验证文件权限
确保配置文件的属主和权限正确:
bash
chown nginx:nginx wp-config.php chmod 640 wp-config.php
七、配置 SELinux 和防火墙
bash
# SELinux 设置 setsebool -P httpd_can_network_connect 1 setsebool -P httpd_can_network_connect_db 1 restorecon -Rv /var/www/wordpress 防火墙开放端口 ps: 这里可能没安装防火墙:
解决方案:
7.1 检查 firewalld 是否安装
bash
rpm -q firewalld
如果未安装,先安装:
bash
dnf install -y firewalld
7.2 启动 firewalld 并设置开机自启
bash
systemctl start firewalld systemctl enable firewalld
7.3 重新添加端口规则
bash
firewall-cmd --permanent --add-service=http firewall-cmd --permanent --add-service=https firewall-cmd --reload
7.4 验证规则
bash
firewall-cmd --list-services
应该能看到 http 和 https。
补充说明:
- 为什么 firewalld 没有运行? 可能在系统安装时没有启用,或者被其他防火墙工具(如 iptables)替代。Rocky Linux 9 默认使用 firewalld,但可能被禁用。
- 如果不想使用 firewalld,可以跳过这一步,因为阿里云安全组已经提供了网络层过滤,只要安全组放行了 80/443 端口,外部就能访问。但系统防火墙(firewalld)建议保持开启,以增加一层防护。
- 如果已经开启了 firewalld 但仍然无法访问,请检查阿里云安全组规则是否放行 80/443 端口。
>> firewall-cmd --permanent --add-service=http firewall-cmd --permanent --add-service=https firewall-cmd --reload
别忘了在阿里云控制台的安全组中放行 80 和 443 端口。
八、完成 WordPress 安装
浏览器访问 http://你的服务器IP 或域名,按照向导填写:
- 站点标题
- 管理员用户名
- 密码
- 邮箱
点击“安装 WordPress”即可。
九、启用 HTTPS:使用 Certbot 申请免费 SSL 证书。
9.0 前置条件(全部必须满足才能开始)
| 条件 | 验证命令 | 期望结果 |
|---|---|---|
| 域名已注册且实名认证通过 | 阿里云控制台查看 | 域名状态正常 |
| DNS A 记录指向真实 ECS 公网 IP | dig antpolsha.xyz +short | 返回真实公网 IP(如 47.119.131.43) |
| HTTP 80 可访问 | curl -I http://antpolsha.xyz | HTTP/1.1 200 OK |
| ECS 安全组放行 80 | 阿里云控制台 | 有 TCP 80/80 授权 0.0.0.0/0 |
| firewalld 放行 http | firewall-cmd --list-services | 包含 http |
| Nginx server_name 包含域名 | grep server_name /etc/nginx/conf.d/wordpress.conf | 有 antpolsha.xyz www.antpolsha.xyz |
| ICP 备案已通过(国内服务器必须) | 阿里云控制台 → ICP 备案 | 已备案 |
⚠️ 关键教训:DNS A 记录的记录值必须是服务器真实公网 IP,不能用文档里的示例 IP。错误示范:填了 47.98.123.45(示例 IP),导致 Let’s Encrypt 去访问别人的服务器,全部 Connection refused。
⚠️ 安全组必须有 80/443 对 0.0.0.0/0 放行。如果只放行校园网 IP,本机 curl 能通,但 Let’s Encrypt 从美国发起验证会被拦截。
9.1 前置检查(必须)
bash
# 检查真实公网 IP curl ifconfig.me
作用:确认服务器的真实公网 IP,与 DNS 记录值对比。
必要性:【必须】 ——DNS 记录值必须与它一致。
bash
# 检查 DNS 解析 dig antpolsha.xyz +short dig www.antpolsha.xyz +short
作用:确认域名解析指向真实 IP。
期望:返回与 curl ifconfig.me 一致的 IP。
bash
# 检查 HTTP 可访问 curl -I http://antpolsha.xyz
作用:验证 Let’s Encrypt 能否通过 HTTP-01 验证访问到服务器。
期望:返回 HTTP/1.1 200 OK。
必要性:【必须】 ——失败时申请证书一定会失败。
9.2 安装 Certbot(必须)
bash
# 安装 EPEL 仓库 dnf install -y epel-release
作用:启用 EPEL 额外软件仓库,Certbot 由其提供。
bash
# 安装 Certbot 和 Nginx 插件 dnf install -y certbot python3-certbot-nginx
作用:
certbot:Let’s Encrypt 官方证书管理客户端。python3-certbot-nginx:Nginx 集成插件,可自动修改配置。
踩坑记录:如果 dnf 安装 EPEL 包时卡住,换阿里云镜像:
bash
dnf clean all dnf remove -y epel-release dnf install -y https://mirrors.aliyun.com/epel/epel-release-latest-9.noarch.rpm dnf install -y certbot python3-certbot-nginx
bash
# 验证 certbot --version
期望:certbot 3.x.x(本次部署为 3.1.0)。
9.3 申请 Let’s Encrypt 证书(必须)
bash
certbot --nginx -d antpolsha.xyz -d www.antpolsha.xyz
参数说明:
--nginx:使用 Nginx 插件,自动修改配置。-d antpolsha.xyz:主域名。-d www.antpolsha.xyz:www 子域名(如不需要可去掉)。
交互提示及回答:
| 提示 | 回答 |
|---|---|
| Enter email address | 2297969511@qq.com(用于证书到期提醒) |
| Do you agree? | Y |
| Share with EFF? | N(可选) |
| HTTP redirect? | 2(自动重定向到 HTTPS) |
成功输出:
text
Successfully received certificate. Certificate is saved at: /etc/letsencrypt/live/antpolsha.xyz/fullchain.pem Key is saved at: /etc/letsencrypt/live/antpolsha.xyz/privkey.pem This certificate expires on 2026-12-20.
⚠️ 已知问题:Certbot 3.x 在处理多域名时,为第一个域名(antpolsha.xyz)自动部署证书后,会去匹配第二个域名(www.antpolsha.xyz)的 server 块,但此时配置已被临时修改,会报错:
text
Could not automatically find a matching server block for www.antpolsha.xyz.
这不影响证书签发,只是自动部署 Nginx 配置失败。解决方法是手动配置,见 9.4。
9.4 手动配置 Nginx 使用证书(必须,当 9.3 自动部署失败时)
前提:/etc/letsencrypt/live/antpolsha.xyz/ 目录下已有证书文件。
bash
# 确认证书文件存在 ls -la /etc/letsencrypt/live/antpolsha.xyz/
期望:看到 fullchain.pem 和 privkey.pem。
bash
# 编辑 Nginx 配置 vim /etc/nginx/conf.d/wordpress.conf
替换为以下结构(保留原有 location 配置,只增加 SSL 部分):
nginx
# HTTP 301 跳转到 HTTPS
server {
listen 80;
server_name antpolsha.xyz www.antpolsha.xyz;
return 301 https://$host$request_uri;
}
# HTTPS 主配置
server {
listen 443 ssl http2;
server_name antpolsha.xyz www.antpolsha.xyz;
root /var/www/wordpress;
index index.php index.html index.htm;
# SSL 证书路径(Certbot 生成)
ssl_certificate /etc/letsencrypt/live/antpolsha.xyz/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/antpolsha.xyz/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# SSL 安全加固
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# 访问日志
access_log /var/log/nginx/wordpress_access.log;
error_log /var/log/nginx/wordpress_error.log;
# WordPress 伪静态
location / {
try_files $uri $uri/ /index.php?$args;
}
# PHP 处理
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_read_timeout 300;
}
# 静态文件缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 30d;
log_not_found off;
access_log off;
}
# 禁止访问敏感文件
location ~* (wp-config\.php|\.htaccess|\.git) {
deny all;
}
}
bash
# 测试语法 nginx -t
期望:syntax is ok + test is successful。
bash
# 重载 Nginx systemctl reload nginx
作用:平滑加载新配置,使 443 端口开始监听。
⚠️ 关键教训:修改配置后必须 reload,否则 443 端口不会监听,curl https://... 会返回 Connection refused。
bash
# 确认 443 已监听 ss -tlnp | grep :443
期望:输出包含 0.0.0.0:443。
9.5 验证 HTTPS(必须)
bash
# 本地测试(绕过安全组) curl -I https://127.0.0.1 -k
期望:HTTP/2 200。
bash
# 域名测试 curl -I https://antpolsha.xyz curl -I https://www.antpolsha.xyz
期望:都返回 HTTP/2 200。
bash
# HTTP 跳转测试 curl -I http://antpolsha.xyz
期望:HTTP/1.1 301 Moved Permanently + Location: https://antpolsha.xyz/。
浏览器验证:访问 https://antpolsha.xyz,确认:
- 地址栏有锁图标 🔒
- 点击锁 → 证书有效,颁发者 Let’s Encrypt
- HTTP 自动跳转到 HTTPS
9.6 更新 WordPress 站点地址(必须)
背景:WordPress 数据库里存的还是旧 IP(如 http://47.119.131.43),会导致页面链接指向 IP、后台登录异常、混合内容警告。
bash
mysql -u root -p wordpress -e "UPDATE wp_options SET option_value='https://antpolsha.xyz' WHERE option_name IN ('siteurl','home');"
作用:把 WordPress 的站点地址和主页地址都改为 HTTPS 域名。
bash
# 验证
mysql -u root -p wordpress -e "SELECT option_name, option_value FROM wp_options WHERE option_name IN ('siteurl','home');"
期望:
text
siteurl https://antpolsha.xyz home https://antpolsha.xyz
bash
# 确认 Link 头也更新了 curl -I https://antpolsha.xyz
期望:link: <https://antpolsha.xyz/index.php?rest_route=/>。
⚠️ 执行时机:必须在 HTTPS 生效后才执行,否则 WordPress 后台可能登录不了。
9.7 配置自动续期(必须)
背景:Let’s Encrypt 证书有效期 90 天,Certbot 会自动创建 systemd timer 每天检查续期。
bash
# 检查 timer 是否存在 systemctl list-units --all | grep -i certbot
踩坑记录:Certbot 3.x 的 timer 名字是 certbot-renew.timer,不是旧版的 certbot.timer。首次部署时可能是 disabled 状态(显示为 -),需要手动启用。
bash
# 启用并启动 timer systemctl enable --now certbot-renew.timer
作用:
enable:设置开机自启。--now:立即启动。- 启用后,每天会运行两次
certbot renew检查续期。
bash
# 验证 timer 状态 systemctl status certbot-renew.timer
期望:
text
Loaded: loaded (/usr/lib/systemd/system/certbot-renew.timer; enabled; ...) Active: active (waiting) since ... Trigger: Tue 2026-09-22 08:08:39 CST; 13h left
bash
# 查看下次执行时间 systemctl list-timers | grep certbot
期望:显示 NEXT 时间。
9.8 创建 deploy hook(必须)
问题:Certbot 续期成功后,新证书文件已替换,但 Nginx 还在用旧证书。需要重载 Nginx。
bash
# 创建 hook 脚本 vim /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
内容:
bash
#!/bin/bash systemctl reload nginx
bash
# 赋予执行权限 chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
作用:Certbot 每次续期成功后,会自动执行该目录下所有脚本,从而重载 Nginx。
bash
# 验证 ls -la /etc/letsencrypt/renewal-hooks/deploy/
期望:reload-nginx.sh 有 x 权限。
说明:使用 renewal-hooks/deploy/ 目录是 Certbot 官方推荐方式,无需修改 systemd service 文件,也适用于手动 certbot renew。
9.9 验证自动续期机制(必须)
bash
# 手动触发一次检查(不会真正续期,因为还没到期) systemctl start certbot-renew.service systemctl status certbot-renew.service
期望:status=0/SUCCESS,日志显示 Certificate not yet due for renewal。
bash
# 查看日志 tail -30 /var/log/letsencrypt/letsencrypt.log
期望:看到 Processing /etc/letsencrypt/renewal/antpolsha.xyz.conf、Certificate not yet due for renewal。
bash
# 模拟续期测试 certbot renew --dry-run
期望:Congratulations, all simulated renewals succeeded。
必要性:【必须】 ——确保 90 天后证书能自动续期,否则网站会因证书过期而无法访问。
9.10 确认防火墙和安全组放行 443(必须)
bash
# firewalld firewall-cmd --list-services | grep https
期望:包含 https。
如果没有:
bash
firewall-cmd --permanent --add-service=https firewall-cmd --reload
阿里云安全组:
text
阿里云控制台 → ECS → 安全组 → sg-xxx → 入方向
确认存在:
| 协议 | 端口 | 授权对象 |
|---|---|---|
| TCP | 80/80 | 0.0.0.0/0 |
| TCP | 443/443 | 0.0.0.0/0 |
⚠️ 关键:80/443 必须对 0.0.0.0/0 开放,不能只放行校园网 IP,否则:
- 别人访问不了你的网站
- Let’s Encrypt 从美国验证会失败
- 搜索引擎爬虫无法抓取
9.11 验证完整清单
| 检查项 | 命令 | 期望结果 |
|---|---|---|
| 证书文件存在 | ls /etc/letsencrypt/live/antpolsha.xyz/ | 有 fullchain.pem 和 privkey.pem |
| 443 端口监听 | ss -tlnp | grep :443 | 0.0.0.0:443 |
| HTTPS 本地访问 | curl -I https://127.0.0.1 -k | HTTP/2 200 |
| HTTPS 域名访问 | curl -I https://antpolsha.xyz | HTTP/2 200 |
| HTTP 自动跳转 | curl -I http://antpolsha.xyz | 301 + Location: https://... |
| HSTS 已启用 | curl -I https://antpolsha.xyz | grep -i strict | 返回 strict-transport-security |
| TLS 版本正确 | grep ssl_protocols /etc/nginx/conf.d/wordpress.conf | TLSv1.2 TLSv1.3 |
| WP 站点地址 | mysql -u root -p wordpress -e "SELECT ..." | https://antpolsha.xyz |
| Timer 已启用 | systemctl is-enabled certbot-renew.timer | enabled |
| Timer 运行中 | systemctl is-active certbot-renew.timer | active |
| Deploy hook 存在 | ls /etc/letsencrypt/renewal-hooks/deploy/ | 有 reload-nginx.sh |
| 续期模拟通过 | certbot renew --dry-run | Congratulations, all simulated renewals succeeded |
9.12 本次部署踩过的坑(经验总结)
| 问题 | 原因 | 解决 |
|---|---|---|
dig 返回 NXDOMAIN | 域名未注册或 NS 未设置 | 注册域名 + 设置 NS 为阿里云 |
dig 返回错误 IP | DNS A 记录填了示例 IP 47.98.123.45 | 改为真实 IP 47.119.131.43 |
| Certbot 验证 Connection refused | 安全组只放行校园网 IP,Let’s Encrypt 从美国访问被拦截 | 80/443 对 0.0.0.0/0 放行 |
| Certbot 自动部署失败 | 多域名场景 Certbot 3.x 的已知边界问题 | 手动配置 Nginx 443 server 块 |
curl https://... Connection refused | 修改 Nginx 配置后没 reload | systemctl reload nginx |
certbot.timer 找不到 | Certbot 3.x 的 timer 名字是 certbot-renew.timer | 启用 certbot-renew.timer |
| 续期后 Nginx 未加载新证书 | Certbot 默认 service 没有 reload hook | 创建 renewal-hooks/deploy/reload-nginx.sh |
| WordPress 链接还指向 IP | 数据库 siteurl/home 还是旧值 | SQL 更新为 https://antpolsha.xyz |
9.13 后续可选优化(非必须)
| 优化项 | 命令/操作 | 作用 |
|---|---|---|
| SSL 在线评分 | 访问 ssllabs.com/ssltest/ | 检查是否 A 或 A+ |
| 检查混合内容 | 浏览器 F12 → Console | 查找并替换 http:// 资源 |
| 批量替换数据库中的 http | 安装 Better Search Replace 插件 | 修复残留的 http 链接 |
| OCSP Stapling | Nginx 配置 ssl_stapling on | 加速证书验证 |
| 证书到期监控 | 定时检查 certbot certificates | 提前发现续期失败 |
9.14 状态
✅ HTTPS 配置已彻底完成
- 证书有效期:2026-09-21 ~ 2026-12-20
- 自动续期:
certbot-renew.timer已启用,每天检查两次 - 续期后自动重载:
renewal-hooks/deploy/reload-nginx.sh已配置 - 下次自动检查时间:2026-09-22 08:08:39
十、定期备份
10.1 创建备份目录
bash
mkdir -p /backup/database mkdir -p /backup/website
10.2 备份 MySQL 数据库
使用 mysqldump 命令导出数据库,注意替换您的数据库用户名和密码。
bash
mysqldump -u root -p'你的数据库root密码' wordpress > /backup/database/wordpress_$(date +%F).sql
$(date +%F)会生成日期,如2026-08-28,避免覆盖。- 如果密码包含特殊字符,建议将密码放在
~/.my.cnf文件中,避免命令行泄露(见下文)。
验证备份文件:
bash
ls -lh /backup/database/
10.3 备份网站文件
网站目录为 /var/www/wordpress,打包压缩:
bash
tar -czf /backup/website/wordpress_files_$(date +%F).tar.gz /var/www/wordpress
验证:
bash
ls -lh /backup/website/
10.4 自动化备份(使用 cron)
编辑当前用户的 crontab:
bash
crontab -e
如果是第一次使用,会提示选择编辑器,输入 1 或直接回车(通常选择 vi)。添加以下两行(每天凌晨 2 点执行备份):
text
0 2 * * * mysqldump -u root -p'你的数据库root密码' wordpress > /backup/database/wordpress_$(date +\%F).sql 0 3 * * * tar -czf /backup/website/wordpress_files_$(date +\%F).tar.gz /var/www/wordpress
注意:cron 中
%需要转义为\%,否则会被当作换行符。
保存退出后,cron 会自动生效。可通过 crontab -l 查看已添加的任务。
10.5 更安全的备份方式(可选)
在命令行直接写密码有风险,建议创建一个 MySQL 配置文件存储凭证:
bash
vi ~/.my.cnf
内容:
ini
[mysqldump] user=root password=你的数据库root密码
保存后设置权限:
bash
chmod 600 ~/.my.cnf
然后备份命令可简化为:
bash
mysqldump wordpress > /backup/database/wordpress_$(date +%F).sql
cron 中同样使用该简化命令,无需再写密码。
10.6 自动化清理:定期删除旧备份
您可以在 cron 定时任务中添加清理命令,使其在备份完成后自动删除超过指定天数的旧备份文件。
编辑 crontab:
bash
crontab -e
添加清理任务(例如每天凌晨 4 点清理 7 天前的备份):
text
0 4 * * * find /backup/database -name "wordpress_*.sql" -mtime +7 -delete 0 4 * * * find /backup/website -name "wordpress_files_*.tar.gz" -mtime +7 -delete
说明:
find查找文件;-name "wordpress_*.sql"匹配文件名模式;-mtime +7表示修改时间在 7 天前;-delete删除匹配的文件。
验证命令是否生效:
可以手动执行一次,确认不会误删:
bash
find /backup/database -name "wordpress_*.sql" -mtime +7 -print
先使用 -print 查看将要删除的文件列表,确认无误后再使用 -delete。
10.7 异地备份:同步到阿里云 OSS
异地备份可以防止服务器本身故障(如磁盘损坏、被入侵)导致本地备份也丢失。这里以阿里云 OSS 为例。
10.7.1 安装 ossutil 工具
1). 获取最新版本的 ossutil 下载地址
方法一:查看阿里云官方文档
打开阿里云 OSS 工具页面(可在浏览器访问):
下载并安装命令行工具ossutil-对象存储(OSS)-阿里云帮助中心
在页面中查找 “下载 ossutil” 部分,获取当前最新的下载链接和版本号。例如,文档中可能会提供类似:
bash
wget https://gosspublic.alicdn.com/ossutil/1.7.18/ossutil64
或者更新版本。
方法二:尝试已知的可用版本(稍旧但通常有效)
如果无法访问文档,可以尝试其他版本,例如 1.7.18 或 1.7.16:
bash
wget https://gosspublic.alicdn.com/ossutil/1.7.18/ossutil64
如果还是 404,可以尝试不带版本号,使用官方安装脚本:
bash
curl -o /usr/local/bin/ossutil https://gosspublic.alicdn.com/ossutil/install.sh bash /usr/local/bin/ossutil
但这种方法不一定成功,因为安装脚本可能也有变化。
方法三:使用 curl 跟随重定向
有时候链接会重定向到最新版本,使用 -L 参数:
bash
curl -L -o ossutil64 https://gosspublic.alicdn.com/ossutil/ossutil64
如果成功下载,再继续后续操作。
2). 手动下载并上传(如果命令行下载总失败)
如果因为网络或链接问题始终无法下载,可以在您本地电脑浏览器中打开阿里云官方文档,找到 ossutil 的下载链接,用浏览器下载 ossutil64 文件,然后通过 FinalShell 的文件管理器上传到服务器(例如上传到 /root 目录)。上传后执行:
bash
chmod +x /root/ossutil64 mv /root/ossutil64 /usr/local/bin/ossutil
3). 验证安装
无论哪种方式,安装完成后运行:
bash
ossutil --version
如果能显示版本号,说明安装成功。
4). 继续配置 OSS
10.7.2 配置 OSS 访问凭证
运行配置向导:
bash
ossutil config
按提示输入:
- Endpoint:例如
oss-cn-hangzhou.aliyuncs.com(根据您的 OSS 地域填写) - AccessKey ID:在阿里云 AccessKey 管理页面创建
- AccessKey Secret:对应的密钥
- STSToken:留空直接回车
配置信息会保存在 ~/.ossutilconfig,权限自动设置。
10.7.3 创建 OSS Bucket(如果还没有)
可以在阿里云控制台创建,或使用命令:
bash
ossutil mb oss://your-bucket-name
(假设您已经创建好,此步骤可跳过)
10.7.4 同步备份目录到 OSS
使用 ossutil sync 命令,将本地备份目录同步到 OSS 的指定路径:
bash
ossutil sync /backup/database oss://your-bucket-name/backup/database --delete ossutil sync /backup/website oss://your-bucket-name/backup/website --delete
--delete表示删除 OSS 中本地不存在的文件(保持完全同步),可根据需求选择是否使用。
10.7.5 定时同步
在 crontab 中添加同步任务,例如每天凌晨 5 点(备份完成后)执行:
text
0 5 * * * /usr/local/bin/ossutil sync /backup/database oss://your-bucket-name/backup/database --delete 0 5 * * * /usr/local/bin/ossutil sync /backup/website oss://your-bucket-name/backup/website --delete
注意: 请确保服务器能访问 OSS(通常没问题),并妥善保管 AccessKey 权限,建议使用最小权限(仅允许访问该 Bucket)。
10.8. 定期验证:确保备份文件可用
备份文件如果损坏,等于没有备份。应定期验证备份的完整性,甚至进行恢复演练。
10.8.1 简单验证:检查备份文件大小和内容
- 数据库备份:可以用
mysql导入到一个临时数据库进行验证,或者使用gzip -t测试压缩包完整性。 - 网站文件备份:使用
tar -tzf列出压缩包内容,检查是否完整。
手动验证命令示例:
bash
# 检查 tar 包完整性 tar -tzf /backup/website/wordpress_files_2026-08-28.tar.gz > /dev/null && echo "tar OK" 检查 SQL 文件可读性(前几行) head -n 5 /backup/database/wordpress_2026-08-28.sql
10.8.2 自动验证脚本
可以编写一个简单的 Bash 脚本,定期检查最近备份文件是否存在且非空,并记录日志。
创建验证脚本:
bash
vi /usr/local/bin/check_backup.sh
内容如下:
bash
#!/bin/bash # 检查最近备份文件是否存在且大小大于0 DB_FILE=$(ls -t /backup/database/wordpress_*.sql | head -1) WEB_FILE=$(ls -t /backup/website/wordpress_files_*.tar.gz | head -1) if [[ -s "$DB_FILE" ]] && [[ -s "$WEB_FILE" ]]; then echo "$(date +%F_%T) Backup check passed" >> /var/log/backup_check.log else echo "$(date +%F_%T) Backup check FAILED" >> /var/log/backup_check.log # 可选:发送告警邮件 fi
赋予执行权限:
bash
chmod +x /usr/local/bin/check_backup.sh
在 crontab 中添加每天上午 9 点执行验证:
text
0 9 * * * /usr/local/bin/check_backup.sh
10.8.3 恢复演练(高级)
定期(如每月)将备份恢复到临时目录或临时数据库,确认备份可正常使用。例如:
- 将数据库备份导入到临时数据库
wordpress_test。 - 将网站文件解压到临时目录,检查关键文件是否存在。
数据库恢复演练:
bash
mysql -e "CREATE DATABASE IF NOT EXISTS wordpress_test" mysql wordpress_test < /backup/database/wordpress_2026-08-28.sql # 检查是否成功导入 mysql -e "SHOW TABLES FROM wordpress_test" # 清理 mysql -e "DROP DATABASE wordpress_test"
文件恢复演练:
bash
mkdir -p /tmp/restore_test tar -xzf /backup/website/wordpress_files_2026-08-28.tar.gz -C /tmp/restore_test ls /tmp/restore_test/var/www/wordpress rm -rf /tmp/restore_test
可将这些步骤写入脚本,定期手动执行。
总结
- 自动化清理:用
find命令配合 cron 删除过期备份,节省磁盘空间。 - 异地备份:使用
ossutil同步到阿里云 OSS,防止本地灾难。 - 定期验证:通过脚本检查备份文件完整性,并定期进行恢复测试,确保备份真的可用。
十一:Nginx 限流
11.1 限流原理
Nginx 限流通过漏桶算法实现:
text
请求进入 → 漏桶(按固定速率流出) → 后端处理
↓ 桶满了
返回 503(拒绝)
| 概念 | 作用 |
|---|---|
| rate | 允许的请求速率(如 30r/s) |
| burst | 允许突发的请求数(桶的容量) |
| nodelay | 突发请求立即处理,不排队延迟 |
| limit_conn | 单 IP 并发连接数限制 |
核心:超出速率的请求直接返回 503,不会打满后端 PHP-FPM。
11.2 两层限流设计
| 层级 | 对象 | 速率 | 作用 |
|---|---|---|---|
| general | 所有请求 | 30r/s,突发 50 | 防止 CC 攻击 |
| login | wp-login.php | 5r/m,突发 3 | 防止登录爆破 |
| limit_conn | 所有请求 | 单 IP 20 并发 | 防止连接耗尽 |
11.3 修改 nginx.conf(定义限流区)【必须】
bash
vim /etc/nginx/nginx.conf
在 http { ... } 块内(不要放在 server 块)添加:
nginx
# 普通请求:每个 IP 每秒 30 次 limit_req_zone $binary_remote_addr zone=general:10m rate=30r/s; # 登录请求:每个 IP 每分钟 5 次 limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m; # 并发连接:每个 IP 最多 20 个 limit_conn_zone $binary_remote_addr zone=addr:10m; # 限流时返回的状态码 limit_req_status 503; limit_conn_status 503;
参数说明:
| 参数 | 含义 |
|---|---|
$binary_remote_addr | 以客户端 IP 为限流 key |
zone=general:10m | 共享内存区名 general,10MB 约存 16 万 IP |
rate=30r/s | 每秒 30 个请求 |
rate=5r/m | 每分钟 5 个请求 |
limit_req_status 503 | 触发限流返回 503 |
⚠️ 关键教训:5r/s 和 5r/m 差 60 倍。写错会导致登录爆破无法被拦截。
必要性:【必须】
11.4 修改 wordpress.conf(应用限流)【必须】
bash
vim /etc/nginx/conf.d/wordpress.conf
在 443 server 块内添加限流规则。
完整配置参考
nginx
server {
listen 443 ssl http2;
server_name antpolsha.xyz www.antpolsha.xyz;
root /var/www/wordpress;
index index.php index.html index.htm;
# ========== 全局限流(server 块顶层)==========
limit_req zone=general burst=50 nodelay;
limit_conn addr 20;
# SSL 证书配置
ssl_certificate /etc/letsencrypt/live/antpolsha.xyz/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/antpolsha.xyz/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# ========== 登录接口:更严格的限流 ==========
location = /wp-login.php {
limit_req zone=login burst=3 nodelay;
auth_basic "Admin Area";
auth_basic_user_file /etc/nginx/.htpasswd;
include fastcgi_params;
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
# ========== wp-admin 目录 ==========
location ^~ /wp-admin/ {
auth_basic "Admin Area";
auth_basic_user_file /etc/nginx/.htpasswd;
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
try_files $uri $uri/ /index.php?$args;
}
# ========== 禁用 XML-RPC ==========
location = /xmlrpc.php {
deny all;
access_log off;
}
# ========== 默认 location ==========
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_read_timeout 300;
}
# ========== 静态文件(不限流)==========
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
limit_req off;
limit_conn off;
expires 30d;
log_not_found off;
access_log off;
}
# 禁止敏感文件
location ~* (wp-config\.php|\.htaccess|\.git) {
deny all;
}
}
应用位置说明
| 位置 | 限流规则 | 说明 |
|---|---|---|
| server 块顶层 | limit_req zone=general burst=50 nodelay; | 所有请求生效 |
| server 块顶层 | limit_conn addr 20; | 单 IP 最大 20 并发连接 |
location = /wp-login.php | limit_req zone=login burst=3 nodelay; | 登录接口单独严格限制 |
| 静态文件 location | limit_req off; limit_conn off; | 静态文件不限流 |
必要性:【必须】
11.5 测试并重载【必须】
bash
# 测试语法 nginx -t
期望:
text
nginx: configuration file /etc/nginx/nginx.conf test is successful
bash
# 平滑重载 systemctl reload nginx
⚠️ 注意:reload 不改变 Nginx 主进程启动时间,不能用 systemctl status 显示的 “Active since” 判断配置是否生效。判断标准是 nginx -t 通过 + 重载无报错。
必要性:【必须】
11.6 验证限流生效【必须】
测试 1:登录限流(推荐)
bash
for i in $(seq 1 10); do
code=$(curl -s -o /dev/null -w "%{http_code}" -u admin:你的basic密码 https://antpolsha.xyz/wp-login.php)
echo "第 $i 次: $code"
done
期望输出(5r/m + burst=3):
text
第 1 次: 200 第 2 次: 200 第 3 次: 200 第 4 次: 503 ← 触发限流 第 5 次: 503 第 6 次: 503 ...
看到 503 → 限流生效 ✅
测试 2:检查限流日志
bash
grep -i "limiting" /var/log/nginx/wordpress_error.log | tail -20
期望:
text
2026/09/29 ... [error] ... limiting requests, excess: 0.950 by zone "login", ...
测试 3:全局限流(可选)
bash
ab -n 200 -c 50 https://antpolsha.xyz/
关注输出:
text
Non-2xx responses: xx ← 出现 503 说明生效
注意:ab 对 HTTP/2 和 HTTPS 支持不佳,可能报 SSL 错误。优先用测试 1 验证。
必要性:【必须】
11.7 参数调优参考【建议】
个人博客(推荐起始值)
nginx
limit_req_zone $binary_remote_addr zone=general:10m rate=30r/s; limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m; limit_conn_zone $binary_remote_addr zone=addr:10m;
调整场景
| 现象 | 调整 |
|---|---|
| 正常用户被误伤 | rate 提到 50r/s,burst 提到 100 |
| 攻击仍然打满 | rate 降到 10r/s,burst 降到 20 |
| 登录太容易触发 | rate 调到 10r/m |
| 静态资源被限流 | 给静态文件 location 加 limit_req off; limit_conn off; |
不同场景参考值
| 场景 | general rate | burst | login rate |
|---|---|---|---|
| 个人博客 | 30r/s | 50 | 5r/m |
| 中小企业 | 50r/s | 100 | 10r/m |
| 高流量站点 | 100r/s | 200 | 20r/m |
11.8 常见问题
| 问题 | 原因 | 解决 |
|---|---|---|
| 限流不触发 | rate 写成 r/s 而非 r/m | 检查单位 |
| 限流不触发 | 配置未 reload | nginx -t && systemctl reload nginx |
| 限流不触发 | limit_req_zone 放在 server 块 | 必须放在 http 块 |
| 误伤正常用户 | rate 太小或 burst 太小 | 调大参数 |
| 静态资源被限流 | 没给静态文件加 off | 静态文件 location 加 limit_req off |
ab 测试报 SSL 错误 | ab 对 HTTP/2 支持差 | 改用 curl 循环测试 |
11.9 配置检查清单
| 检查项 | 命令 | 期望 |
|---|---|---|
| 限流区定义 | grep "limit_req_zone" /etc/nginx/nginx.conf | 3 行(general/login/conn) |
| general rate | grep "zone=general" /etc/nginx/nginx.conf | rate=30r/s |
| login rate | grep "zone=login" /etc/nginx/nginx.conf | rate=5r/m |
| server 块应用 | grep "limit_req zone" /etc/nginx/conf.d/wordpress.conf | 至少 2 行 |
| 语法正确 | nginx -t | successful |
| 已重载 | systemctl reload nginx | 无报错 |
| 限流触发 | 连续请求 wp-login.php 6 次 | 第 4 次开始 503 |
| 限流日志 | grep limiting /var/log/nginx/wordpress_error.log | 有记录 |
11.10 本次踩过的坑
| 问题 | 原因 | 解决 |
|---|---|---|
| 限流不触发 | rate=5r/s(应 5r/m) | 改正单位,差 60 倍 |
| 误以为配置未生效 | systemctl status 显示 “Active since 4 days ago” | reload 不改启动时间,配置已加载 |
ab 测试报 SSL 错误 | ab 不支持 HTTP/2 | 改用 curl 循环测试 |
for + wait 命令卡住 | wait 不接受管道 | 用 ab 或写文件统计 |
11.11 状态
✅ Nginx 限流已配置完成
- general 限流:30r/s,突发 50
- login 限流:5r/m,突发 3
- 并发连接:单 IP 20
- 验证结果:连续 6 次请求 wp-login.php,第 4 次开始返回 503