2026年11月12日

2026年11月12日

WordPress 設定 Cloudflare 教學|切換 DNS 名稱伺服器、SSL、還原真實 IP、WAF

本文是日文原文的在地化版本。 閱讀日文原文

在 WordPress 上設定 Cloudflare,依序進行:①在 Cloudflare 新增網站,並將網域的名稱伺服器切換過去;②將 SSL/TLS 加密模式設為 Full (strict);③在伺服器端還原訪客的真實 IP;④用 WAF 自訂規則保護登入頁。免費方案也能使用 CDN、SSL、DDoS 防護與自訂規則(最多 5 條)。

本文說明各項設定,以及切換之後要確認的地方。Cloudflare 的畫面名稱與配置可能會改變,因此這裡依據 2026 年 11 月時的官方文件(developers.cloudflare.com)撰寫。

作業前先決定的事

  • 這表示會把 DNS 的管理移到 Cloudflare。網域的註冊商不會改變,但 DNS 記錄的編輯會改在 Cloudflare 的畫面中進行。
  • 郵件相關的 DNS 記錄(MX 等)要設為不經過代理。經過 Cloudflare 的只有網頁的通訊(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)

如果是虛擬主機等無法更動網頁伺服器設定的環境,可以在 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 的 2 個字元代碼)。舊文章中的 ip.geoip.country 已不建議使用。動作一開始先用 Managed Challenge,在安全性事件中確認沒有誤擋正當的存取之後,必要時再改成 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 有疑問嗎?本站營運方位於日本,僅以日文回覆。 聯絡我們(日文)

お気軽にご相談ください

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