搜索全站
安装部署 14 分钟阅读

限制 wp-admin 访问的 IP 白名单:服务器层与插件层方案

WordPress 后台被暴力破解是最高频的入侵方式。把 wp-admin 限制成只有你的 IP 能访问,能挡掉 99% 的扫描。本文给出 Nginx、Apache、插件三种实现方案和动态 IP 的应对办法。

U
UFO_PF 2025-11-05 · 阅读 14 分钟
限制 安装部署
阅读提示
本文约 5,339 字,预计阅读 14 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:后台每天被扫几千次

看一下 Nginx 访问日志就知道了:

grep -c "wp-login.php" /var/log/nginx/access.log
# 结果可能是几万

自动化扫描器 24 小时不停尝试 admin / administrator / 常见密码字典。即使你有强密码 + 双因素,这些请求也在消耗服务器资源,且有 0day 漏洞时被撞上的风险。

最彻底的方案:只放行你自己的 IP 访问后台。扫都扫不到,攻击面直接归零。

原理分析:要保护哪些路径

WordPress 后台相关的入口:

路径 作用 是否可限制
/wp-admin/ 后台全部页面 可限制(但见下文注意事项)
/wp-login.php 登录页 可限制
/wp-admin/admin-ajax.php 前后台 AJAX 接口 不能全限制,前台功能会挂
/wp-admin/admin-post.php 表单提交 不能全限制
/wp-json/wp/v2/* REST API 部分接口前台在用,谨慎
/xmlrpc.php 远程发布 建议直接封

关键陷阱:很多人一刀切把 /wp-admin/ 全封了,结果发现:

  • 前台的 AJAX 功能(如”加载更多”、联系表单、评论提交)全部失效;
  • 因为 admin-ajax.php 就在 /wp-admin/ 目录下。

正确做法:放行 admin-ajax.php 和 admin-post.php,只保护其他后台页面;或者更精细地判断 Referer。

保姆级实操

方案一:Nginx 层限制(推荐,性能最好)

server {
    # ... 其他配置

    # 1) 保护登录页
    location = /wp-login.php {
        allow 1.2.3.4;          # 你的固定 IP
        allow 203.0.113.0/24;   # 你的公司网段
        deny all;

        include fastcgi_params;
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

    # 2) 保护后台,但放行 AJAX 和必要的公开文件
    location ^~ /wp-admin/ {
        # 先放行前台功能依赖的接口
        location ~ ^/wp-admin/(admin-ajax\.php|admin-post\.php)$ {
            include fastcgi_params;
            fastcgi_pass unix:/run/php/php8.2-fpm.sock;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            # 这里不做 IP 限制
        }

        # 放行后台的静态资源(CSS/JS,登录页要用)
        location ~* ^/wp-admin/.*\.(css|js|png|jpg|svg|woff2?)$ {
            expires 7d;
        }

        allow 1.2.3.4;
        allow 203.0.113.0/24;
        deny all;

        location ~ \.php$ {
            include fastcgi_params;
            fastcgi_pass unix:/run/php/php8.2-fpm.sock;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        }
    }

    # 3) 直接封 xmlrpc.php
    location = /xmlrpc.php {
        deny all;
        access_log off;
        log_not_found off;
    }
}

注意 allow/deny 的顺序:Nginx 是从上往下匹配,命中即停止,所以 deny all 必须放最后。

方案二:Apache(.htaccess)

# 保护 wp-login.php
<Files wp-login.php>
    Require ip 1.2.3.4
    Require ip 203.0.113.0/24
    Require all denied
</Files>

# 保护 wp-admin
<Directory "/var/www/mimangfei/wp-admin">
    <FilesMatch "^(admin-ajax\.php|admin-post\.php)$">
        Require all granted
    </FilesMatch>
    Require ip 1.2.3.4
    Require all denied
</Directory>

Apache 2.4 的 Require 指令默认取第一个成功匹配,Require all granted 要写在前面。如果逻辑混乱,用 <RequireAny> 包起来:

<Directory "/var/www/mimangfei/wp-admin">
    <RequireAny>
        <FilesMatch "^(admin-ajax\.php|admin-post\.php)$">
            Require all granted
        </FilesMatch>
        Require ip 1.2.3.4
    </RequireAny>
</Directory>

方案三:加一层 HTTP Basic 认证(动态 IP 也适用)

如果 IP 不固定(家里宽带、出差),IP 白名单很痛苦。改用 Basic Auth——先过账户密码,再看到 WordPress 登录页,双保险。

# 生成密码文件
sudo apt install apache2-utils -y
sudo htpasswd -c /etc/nginx/.wpadmin_htpasswd mimangfei
location = /wp-login.php {
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/.wpadmin_htpasswd;

    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

location ^~ /wp-admin/ {
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/.wpadmin_htpasswd;
    # ... PHP 处理
}

注意:Basic Auth 和 fastcgi_cache 同时用时,被保护的路径要关缓存(前面已处理)。

方案四:插件层(没有服务器权限时)

插件 功能
Wordfence 有”只允许特定 IP 登录”、双因素、限流
iThemes Security(Solid Security) 后台隐藏 + IP 白名单
WPS Hide Login 把 /wp-login.php 改成自定义路径
Cerber Security 强大的访问控制和白名单

插件方案的优点是不用改服务器配置,缺点是请求已经进了 PHP,防护层级靠后,且依赖插件本身不出漏洞。

方案五:改后台路径(WPS Hide Login)

把登录地址从 /wp-login.php 改成 /my-secret-login,扫描器直接 404。这是成本最低、最实用的措施之一,但属于”安全通过模糊”(security through obscurity),不能替代其他措施。

动态 IP 的应对办法

  1. DDNS + 脚本更新白名单:家里宽带装 DDNS,服务器上跑定时任务更新 Nginx 配置里的 IP;

bash
#!/bin/bash
IP=$(dig +short myhome.ddns.example)
OLD=$(grep -oP 'allow \K[0-9.]+(?=;)' /etc/nginx/conf.d/wpadmin.conf | head -1)
if [ "$IP" != "$OLD" ]; then
sed -i "s/allow $OLD;/allow $IP;/" /etc/nginx/conf.d/wpadmin.conf
nginx -t && systemctl reload nginx
fi

  1. 用 VPN / 跳板机:固定一台云主机做跳板,白名单只放行跳板机 IP;
  2. Basic Auth(见方案三):最省事;
  3. Cloudflare Zero Trust / Access:免费版可配策略,按邮箱验证后才放行后台路径。这是体验最好的方案。

常见踩坑

  1. 把自己锁在门外:改配置前先确认自己的公网 IP(curl ifconfig.me),改完不要关当前浏览器标签,用另一个浏览器或无痕窗口测试。
  2. 前台 AJAX 挂了:admin-ajax.php 被封,表现为”加载更多”无反应、联系表单提交失败、评论提交转圈。必须放行。
  3. admin-post.php 被封:前台的非 AJAX 表单(如某些订阅、下载)会 403。
  4. 案例:某站封了整个 /wp-admin/,当天就收到用户反馈”商品加入购物车失效”。WooCommerce 的加购走 admin-ajax.php,被一起封了。改为只封 /wp-admin/ 下的其他 PHP 文件后恢复。
  5. CDN / 反向代理下拿到的是 CDN IP:allow 匹配的是 $remote_addr,如果前置了 Cloudflare,这里是 CDN 节点 IP。必须先用 反向代理下获取真实 IP 的方法还原,或者在 Cloudflare 层用 WAF 规则做限制。
  6. 忘了保护 /wp-login.php?action=register:只封了 /wp-admin/ 没封 /wp-login.php,登录页还是暴露的。两个都要封。
  7. Basic Auth 与 WordPress 登录冲突:两者是独立的,都要输。忘了 Basic Auth 密码会以为 WordPress 密码错了。
  8. 日志看不到真实攻击了:封 IP 后日志里全是 403,注意别因为日志”干净了”就以为没人攻击。定期看被拒次数:

bash
grep "wp-login.php" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head

总结

后台 IP 白名单是投入产出比最高的 WordPress 安全措施。落地要点:Nginx 层做(性能最好、拦得最早)、必须放行 admin-ajax.php 和 admin-post.php、优先改用 Basic Auth 或 Cloudflare Access 应对动态 IP、改前先确认自己的 IP 并保留一个测试窗口。配合强密码、双因素认证、双因素插件,后台基本可以高枕无忧。更全面的安全加固清单可以看WordPress 月度安全巡检清单。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。