2026年11月12日

2026年11月12日

WordPress 设置 Cloudflare 的步骤|切换名称服务器、SSL、还原真实 IP、WAF

本文是日语原文的本地化版本。 阅读日语原文

为 WordPress 接入 Cloudflare,按下面的顺序进行:①把站点添加到 Cloudflare 并切换域名的名称服务器,②把 SSL/TLS 加密模式设为 Full (strict),③在服务器端还原访客的真实 IP,④用 WAF 自定义规则保护登录页。免费套餐也能使用 CDN、SSL、DDoS 防护和自定义规则(最多 5 条)。

本文说明各项设置,以及切换之后需要确认的地方。Cloudflare 界面中的名称和位置可能变动,因此这里依据 2026 年 11 月时的官方文档(developers.cloudflare.com)来写。

动手之前先确定的事项

  • 这意味着要把 DNS 的管理交给 Cloudflare。域名的注册商(Registrar)不会变,但 DNS 记录要改在 Cloudflare 的界面中编辑。
  • 邮件相关的 DNS 记录(MX 等)不要开启代理。经过 Cloudflare 的只有 Web 通信(HTTP/HTTPS)。
  • 确认源站(放置 WordPress 的服务器)上有有效的 SSL 证书。如果已经用 Let's Encrypt 等启用了 HTTPS,保持现状即可。
  • 如果在注册商处启用了 DNSSEC,请在切换名称服务器之前先将其关闭。残留旧的 DS 记录会导致域名解析失败。

步骤 1:添加站点并切换名称服务器

  1. 创建 Cloudflare 账号,在控制台(Dashboard)中添加域名。套餐可以选择 Free。
  2. Cloudflare 会读取当前的 DNS 记录并列出来,请确认有没有遗漏。有些记录无法被读取,请与现在的 DNS 管理界面对照,缺少的手动补上。
  3. 网站的 A(或 AAAA、CNAME)记录,把代理状态设为开启(橙色云朵)。MX 以及邮件服务器用的 A 记录设为关闭(仅 DNS,灰色云朵)。
  4. Cloudflare 会显示 2 个名称服务器的名称。名称形如 〇〇.ns.cloudflare.com,每个账号分配到的都不同。请使用你自己界面上显示的这 2 个,而不是其他文章里写的名称。
  5. 在域名注册商的管理界面中,把名称服务器改为这 2 个。

用下面的命令确认是否已经切换。生效可能需要几个小时。

# 名称服务器是否已变为 Cloudflare 的那 2 个
dig NS example.com +short

# 是否已经过 Cloudflare(会返回 server: cloudflare 和 cf-ray)
curl -sI https://example.com | grep -iE '^(server|cf-ray):'

在 Cloudflare 界面中,域名状态变为有效(Active)后,切换就完成了。

步骤 2:把 SSL/TLS 设为 Full (strict)

Cloudflare 的 SSL/TLS 有多种模式,决定访客与 Cloudflare 之间、Cloudflare 与源站之间如何加密。

模式 Cloudflare 与源站之间 在 WordPress 中的处理
Off 不加密 不使用
Flexible HTTP(不加密) 不使用。会导致重定向循环和混合内容
Full HTTPS(不验证证书) 在准备好证书之前的过渡方案
Full (strict) HTTPS(验证证书) 选这个

设置在 Cloudflare 控制台 SSL/TLS 的概述(Overview)页面中更改。默认的“Automatic SSL/TLS”会在源站支持的范围内自动选择安全的模式。自己选定 Full (strict),就不会以非预期的模式运行。源站的证书可以使用公共证书颁发机构(Let's Encrypt 等)的证书,或 Cloudflare 签发的 Origin CA 证书。

避免使用 Flexible 的原因如下。在 Flexible 下,请求以 HTTP 到达源站,于是 WordPress 或服务器回应“因为是 HTTP 来的,所以转到 HTTPS”,Cloudflare 又以 HTTP 去源站取内容,如此不断重复(浏览器中会显示 ERR_TOO_MANY_REDIRECTS)。使用 Full (strict) 时,请求也是以 HTTPS 到达源站,所以 WordPress 一侧不需要再添加根据 X-Forwarded-Proto 改写 $_SERVER['HTTPS'] 之类的额外代码。

同时请确认 WordPress 的“设置”(Settings)→“常规”(General)中的“WordPress 地址(URL)”和“站点地址(URL)”是 https://。如果图片等仍保留 http://,请参考混合内容的修复方法。

步骤 3:还原访客的真实 IP

经过 Cloudflare 后,到达服务器的连接来源(REMOTE_ADDR)会变成 Cloudflare 的 IP 地址。这样一来,访问日志、评论的 IP、限制登录尝试的插件等,都会把所有访客当作 Cloudflare 的 IP。Cloudflare 通过 CF-Connecting-IP 头传递访客的 IP,因此要设置成只有当连接来源是 Cloudflare 时才信任这个头。

如果不确认连接来源,就直接用头里的值改写 REMOTE_ADDR,那么不经过 Cloudflare、直接访问源站的人,就能通过这个头冒用任意 IP。旧文章里那种“只要有 HTTP_CF_CONNECTING_IP 就赋值给 REMOTE_ADDR”的代码,正是出于这个原因,请不要使用。

Cloudflare 的 IP 地址列表公开在 https://www.cloudflare.com/ips/。列表可能会变动,请定期检查。

Nginx 的情况

使用 ngx_http_realip_module。在 http 块或 server 块中,列出 Cloudflare 的全部网段。

# Cloudflare 的 IP 范围(截至 2026 年 11 月。最新内容请在 cloudflare.com/ips 确认)
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2a06:98c0::/29;
set_real_ip_from 2c0f:f248::/32;

real_ip_header CF-Connecting-IP;
sudo nginx -t && sudo systemctl reload nginx

Apache 的情况(Debian / Ubuntu)

使用 mod_remoteip。创建 /etc/apache2/conf-available/remoteip.conf,把 Cloudflare 的网段列在 RemoteIPTrustedProxy 中。

RemoteIPHeader CF-Connecting-IP
RemoteIPTrustedProxy 173.245.48.0/20 103.21.244.0/22 103.22.200.0/22 103.31.4.0/22
RemoteIPTrustedProxy 141.101.64.0/18 108.162.192.0/18 190.93.240.0/20 188.114.96.0/20
RemoteIPTrustedProxy 197.234.240.0/22 198.41.128.0/17 162.158.0.0/15 104.16.0.0/13
RemoteIPTrustedProxy 104.24.0.0/14 172.64.0.0/13 131.0.72.0/22
RemoteIPTrustedProxy 2400:cb00::/32 2606:4700::/32 2803:f800::/32 2405:b500::/32
RemoteIPTrustedProxy 2405:8100::/32 2a06:98c0::/29 2c0f:f248::/32
sudo a2enmod remoteip
sudo a2enconf remoteip
sudo apache2ctl configtest
sudo systemctl restart apache2

要在访问日志中输出真实 IP,请把 /etc/apache2/apache2.conf 中 LogFormat(combined)里的 %h 改为 %a。

无法修改服务器设置的情况(wp-config.php)

在租用的虚拟主机等无法改动 Web 服务器配置的环境中,可以在 wp-config.php 中做同样的判断。把它放在 require_once ABSPATH . 'wp-settings.php'; 这一行之前。只有当连接来源在 Cloudflare 的网段内时,才使用 CF-Connecting-IP 的值(必须是合法的 IP 地址)。

<?php
// 仅在经过 Cloudflare 时,才把 CF-Connecting-IP 当作访客的 IP
$cf_ranges = [
    '173.245.48.0/20', '103.21.244.0/22', '103.22.200.0/22', '103.31.4.0/22',
    '141.101.64.0/18', '108.162.192.0/18', '190.93.240.0/20', '188.114.96.0/20',
    '197.234.240.0/22', '198.41.128.0/17', '162.158.0.0/15', '104.16.0.0/13',
    '104.24.0.0/14', '172.64.0.0/13', '131.0.72.0/22',
    '2400:cb00::/32', '2606:4700::/32', '2803:f800::/32', '2405:b500::/32',
    '2405:8100::/32', '2a06:98c0::/29', '2c0f:f248::/32',
];

function cf_ip_in_range(string $ip, string $cidr): bool
{
    [$net, $bits] = explode('/', $cidr);
    $ip_bin  = inet_pton($ip);
    $net_bin = inet_pton($net);
    if ($ip_bin === false || $net_bin === false || strlen($ip_bin) !== strlen($net_bin)) {
        return false;
    }
    $bits  = (int) $bits;
    $bytes = intdiv($bits, 8);
    if ($bytes > 0 && substr($ip_bin, 0, $bytes) !== substr($net_bin, 0, $bytes)) {
        return false;
    }
    $rest = $bits % 8;
    if ($rest === 0) {
        return true;
    }
    $mask = (0xFF << (8 - $rest)) & 0xFF;
    return (ord($ip_bin[$bytes]) & $mask) === (ord($net_bin[$bytes]) & $mask);
}

if (!empty($_SERVER['HTTP_CF_CONNECTING_IP']) && !empty($_SERVER['REMOTE_ADDR'])) {
    foreach ($cf_ranges as $range) {
        if (cf_ip_in_range($_SERVER['REMOTE_ADDR'], $range)) {
            $visitor = filter_var($_SERVER['HTTP_CF_CONNECTING_IP'], FILTER_VALIDATE_IP);
            if ($visitor !== false) {
                $_SERVER['REMOTE_ADDR'] = $visitor;
            }
            break;
        }
    }
}

wp-config.php 一开始就有 <?php,所以不要抄写第 1 行的 <?php,从第 2 行开始粘贴。粘贴之前请先备份 wp-config.php。

步骤 4:用 WAF 自定义规则保护登录页

Cloudflare WAF 的自定义规则,在免费套餐中最多可以创建 5 条,并且可以选择 Log 以外的动作(Block、Managed Challenge 等)。创建方法:在控制台的 Security rules 页面点击“Create rule”,选择“Custom rules”,指定条件(Field、Operator、Value)和动作,然后“Deploy”。

示例 1:只对自己的 IP 开放登录页和后台

适用于有家庭或公司固定 IP 的情况。写成表达式如下(请把 203.0.113.10 和 198.51.100.0/24 换成你自己的 IP)。

(http.request.uri.path eq "/wp-login.php" or starts_with(http.request.uri.path, "/wp-admin/"))
and http.request.uri.path ne "/wp-admin/admin-ajax.php"
and not ip.src in {203.0.113.10 198.51.100.0/24}

动作选 Block。/wp-admin/admin-ajax.php 有时会被联系表单、无限滚动等公开页面的功能在未登录状态下调用,所以已从条件中排除。

示例 2:没有固定 IP 时,用国家和 Managed Challenge 来限制

(http.request.uri.path eq "/wp-login.php" or http.request.uri.path eq "/xmlrpc.php")
and not ip.src.country in {"JP"}

国家的判定使用 ip.src.country(ISO 3166-1 的两位字母代码)。旧文章里的 ip.geoip.country 已不推荐使用。动作建议一开始先用 Managed Challenge,在安全事件(Security events)中确认没有拦住正当访问之后,再按需改为 Block。Cloudflare 的文档也推荐这样的做法。xmlrpc.php 在通过 Jetpack 或手机 App 发布文章时会被用到,所以请先确认是否在使用,再把它加入条件。

按国家限制是可以被 VPN 等绕过的辅助措施。请与双重验证和登录次数限制组合使用。在 Nginx 一侧按国家判定的方法,参见 Nginx 用 GeoIP2 按国家限制访问。

步骤 5:缓存的默认行为与发布时的清除

Cloudflare 默认不缓存 HTML。它缓存的是图片、CSS、JavaScript、字体等按扩展名确定的静态文件。另外,包含 Set-Cookie 头的响应也不会被缓存。所以只要不做任何设置,后台和登录页就不会被 Cloudflare 缓存。

如果想连 HTML 也缓存,可以用 Cache Rules 扩大范围,但这时必须把 /wp-admin/、/wp-login.php、预览以及已登录用户看到的页面排除在外。排除方式出错的话,面向管理员的页面可能会显示给其他人,所以没有把握的话,保持默认最稳妥。

替换了 CSS 或图片但显示没有变化时,可以在控制台的缓存设置中清除(Purge)缓存。如果想在每次发布或更新文章时自动清除,可以调用 Cloudflare 的 API。首先在 Cloudflare 中创建一个只具有“Cache Purge”权限的 API 令牌,再与区域 ID(显示在域名的概述页面)一起,用常量写入 wp-config.php。

define( 'CF_ZONE_ID', '你的区域ID' );
define( 'CF_API_TOKEN', '你的API令牌' );

把下面的代码放进子主题的 functions.php,或放进自己写的小插件(用 Code Snippets 等也可以)。

<?php
// 文章发布或更新后,从 Cloudflare 清除该文章和首页的缓存
function cf_purge_urls(array $urls): bool
{
    if (!defined('CF_ZONE_ID') || !defined('CF_API_TOKEN')) {
        return false;
    }

    $response = wp_remote_post(
        'https://api.cloudflare.com/client/v4/zones/' . CF_ZONE_ID . '/purge_cache',
        [
            'headers' => [
                'Authorization' => 'Bearer ' . CF_API_TOKEN,
                'Content-Type'  => 'application/json',
            ],
            'body'    => wp_json_encode(['files' => array_values($urls)]),
            'timeout' => 15,
        ]
    );

    if (is_wp_error($response)) {
        error_log('Cloudflare purge failed: ' . $response->get_error_message());
        return false;
    }

    $result = json_decode(wp_remote_retrieve_body($response), true);
    return !empty($result['success']);
}

add_action('publish_post', function (int $post_id): void {
    if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) {
        return;
    }
    cf_purge_urls([get_permalink($post_id), home_url('/')]);
});

publish_post 会在文章发布时,以及已发布的文章被更新时触发。API 的 purge_cache 除了用于指定 URL 的 files 之外,还有清除全部内容的 purge_everything,但清除全部会让静态文件也全部重新获取,所以不要在每次发布文章时使用。

切换之后要确认的事项

  • curl -sI 的结果中有 server: cloudflare 和 cf-ray,并且浏览器中显示了锁形图标
  • 可以登录后台,能保存文章、上传媒体
  • 新的评论和访问日志中记录的是访客的 IP,而不是 Cloudflare 的 IP
  • 联系表单的邮件能够收到(MX 记录没有被代理)
  • 如果创建了 WAF 自定义规则,请用未被允许的线路(如手机的移动网络)打开 wp-login.php,确认出现拦截或质询

常见问题

出现 ERR_TOO_MANY_REDIRECTS

请确认 SSL/TLS 模式是不是 Flexible。在源站安装证书并改为 Full (strict) 即可解决。

进不去后台了

可能是 WAF 自定义规则的条件中没有包含你自己的 IP 或国家。请在 Cloudflare 控制台中暂时停用该规则,在安全事件中确认被哪条规则拦截之后,再修正表达式。

所有访问看起来都来自同一个 IP

说明步骤 3 的真实 IP 还原没有做好。Nginx 请确认 set_real_ip_from 的网段和 real_ip_header,Apache 请确认 remoteip 是否已启用。

总结

  • 名称服务器切换为分配给自己账号的 2 个,MX 等邮件用的记录不要开启代理
  • SSL/TLS 用 Full (strict)。Flexible 会导致重定向循环和混合内容
  • 真实 IP 只在连接来源位于 Cloudflare 网段时,才信任 CF-Connecting-IP 来还原
  • 登录页用 WAF 自定义规则按 IP 或国家限制,一开始先用 Managed Challenge 观察情况
  • HTML 默认不会被缓存。发布时的清除,用只有“Cache Purge”权限的 API 令牌来完成

相关文章

参考:Restoring original visitor IPs(Cloudflare)、Encryption modes(Cloudflare)、Custom rules(Cloudflare WAF)、Default cache behavior(Cloudflare)

关于 WordPress 有疑问?本站运营方位于日本,仅以日语回复。 联系我们(日语)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲