普通视图

Received before yesterday

申请免费通配符证书

作者obaby
2025年11月21日 17:24

之前有个项目,用了个域名带下划线,结果申请证书的时候就悲剧了,嗯,你问为什么不买个通配符的?当然是为了省钱,不想花钱。

刚开始还以为是阿里的问题,后来去腾讯发现也不行,换了几家都不行。最后搜索发现这么个情况:

由于受CAB出台的新规(证书中所包含的域名不能有下划线)影响,从2018年12月7日起,所有新签发的证书的域名中不能包含下划线,在2018年12月7日之前如有签发过下划线的域名,则需要在2019年1月14日进行强制吊销。
如有证书用户受到影响,可以通过下述方法进行解决:
1.请用户将含有下划线的域名进行调整,然后CA对该老证书进行重签发,将新的标准FQDN添加到冲签发的证书中。
2.用Wildcard对原老证书进行替换(但如果老证是EV SSL证书,则Wildcard证书不适用于本解决方案,因为Wildcard是OV类型证书,不支持EV)。
域名命名规范 (RFC标准)
互联网的核心技术规范由IETF(互联网工程任务组)通过一系列名为RFC(意见征求稿)的文档来定义。关于域名如何命名的规则,主要在 RFC 1035 中明确规定。

合法字符集:RFC 1035规定,域名中的“标签”(例如 www、example、com 都是独立的标签)只能使用以下字符:

字母 a-z (不区分大小写)

数字 0-9

连字符 -

关键限制:连字符 - 不能出现在标签的开头或结尾。

根据这个标准,下划线 _ 根本不在允许的字符集之内。所以,从技术规范上讲,server_name.com 本身就是一个无效的域名。

所以,虽然之前能创建这种二级或者三级域名,但是,在申请对应的证书的时候就悲剧了。

所以为了不影响业务,尝试申请免费的通配符证书,还是通过 acmes.sh 搞吧。

1.安装

curl https://get.acme.sh | sh -s email=my@example.com

2.配置环境变量:

export Ali_Key="LTA**************6yn"
export Ali_Secret="q435*************EBSaDba5"

3.申请证书:

acme.sh --issue --dns dns_ali -d example.com -d *.example.com --debug

需要注意的是,通配符证书只能通过配置信息自动校验,不能通过添加解析的方式校验,所以要配置 key 和 secret。如果是不同的解析服务商,设置不同的环境变量即可。

zhongling@MacBookPro .acme.sh % export Ali_Key="LTA**************6yn"
export Ali_Secret="q435*************EBSaDba5"
zhongling@MacBookPro .acme.sh % ./acme.sh --issue -d lang.bi -d '*.lang.bi' -k ec-256 --dns dns_ali --dnssleep 60
[2025年11月21日 星期五 16时53分34秒 CST] Using CA: https://acme.zerossl.com/v2/DV90
[2025年11月21日 星期五 16时53分34秒 CST] Multi domain='DNS:lang.bi,DNS:*.lang.bi'
[2025年11月21日 星期五 16时53分41秒 CST] Getting webroot for domain='lang.bi'
[2025年11月21日 星期五 16时53分41秒 CST] Getting webroot for domain='*.lang.bi'
[2025年11月21日 星期五 16时53分41秒 CST] Adding TXT value: fcTxHx2osERz8mqJFaV2c0yKvo6vUSMA4SH1FR95PMQ for domain: _acme-challenge.lang.bi
[2025年11月21日 星期五 16时53分44秒 CST] The TXT record has been successfully added.
[2025年11月21日 星期五 16时53分44秒 CST] Sleeping for 60 seconds to wait for the the TXT records to take effect
[2025年11月21日 星期五 16时54分46秒 CST] lang.bi is already verified, skipping dns-01.
[2025年11月21日 星期五 16时54分46秒 CST] Verifying: *.lang.bi
[2025年11月21日 星期五 16时54分47秒 CST] Processing. The CA is processing your order, please wait. (1/30)
[2025年11月21日 星期五 16时54分52秒 CST] Success
[2025年11月21日 星期五 16时54分52秒 CST] Removing DNS records.
[2025年11月21日 星期五 16时54分52秒 CST] Removing txt: fcTxHx2osERz8mqJFaV2c0yKvo6vUSMA4SH1FR95PMQ for domain: _acme-challenge.lang.bi
[2025年11月21日 星期五 16时54分54秒 CST] Successfully removed
[2025年11月21日 星期五 16时54分54秒 CST] Verification finished, beginning signing.
[2025年11月21日 星期五 16时54分54秒 CST] Let's finalize the order.
[2025年11月21日 星期五 16时54分54秒 CST] Le_OrderFinalize='https://acme.zerossl.com/v2/DV90/order/7SLmDTCNs_Qw7zls2HFDpA/finalize'
[2025年11月21日 星期五 16时54分56秒 CST] Order status is 'processing', let's sleep and retry.
[2025年11月21日 星期五 16时54分56秒 CST] Sleeping for 15 seconds then retrying
[2025年11月21日 星期五 16时55分12秒 CST] Polling order status: https://acme.zerossl.com/v2/DV90/order/7SLmDTCNs_Qw7zls2HFDpA
[2025年11月21日 星期五 16时55分13秒 CST] Downloading cert.
[2025年11月21日 星期五 16时55分13秒 CST] Le_LinkCert='https://acme.zerossl.com/v2/DV90/cert/bvCTHYFrpbcye-ASpKoS5g'
[2025年11月21日 星期五 16时55分15秒 CST] Cert success.
-----BEGIN CERTIFICATE-----
MIID/zCCA4WgAwIBAgIQS5gLQdZXhrEHdsgVdwPdgzAKBggqhkjOPQQDAzBLMQsw
CQYDVQQGEwJBVDEQMA4GA1UEChMHWmVyb1NTTDEqMCgGA1UEAxMhWmVyb1NTTCBF
Q0MgRG9tYWluIFNlY3VyZSBTaXRlIENBMB4XDTI1MTEyMTAwMDAwMFoXDTI2MDIx
OTIzNTk1OVowFzEVMBMGA1UEAxMMaGFpa2VodWkubmV0MFkwEwYHKoZIzj0CAQYI
KoZIzj0DAQcDQgAEehCGvspbOuBBQjuauz9ghdv9bmvPGJmlz/LttbMjBlBi31Wh
****************************************************************
qaiMNTAnBgNVHREEIDAeggxoYWlrZWh1aS5uZXSCDiouaGFpa2VodWkubmV0MAoG
CCqGSM49BAMDA2gAMGUCMHlmfYvfKEWtJ/CM7UNx6sJPwzu5fU1c5j8v2Oj4REQh
/KE0yJHo3YZkXegvxlSAPAIxAOPw+ZwRsatCaRL8yEGp4mX0umkKx+XbtTlus5NK
aBIOcZiS307CH5mXKOb1jXMPpg==
-----END CERTIFICATE-----
[2025年11月21日 星期五 16时55分15秒 CST] Your cert is in: /Users/zhongling/.acme.sh/lang.bi_ecc/lang.bi.cer
[2025年11月21日 星期五 16时55分15秒 CST] Your cert key is in: /Users/zhongling/.acme.sh/lang.bi_ecc/lang.bi.key
[2025年11月21日 星期五 16时55分15秒 CST] The intermediate CA cert is in: /Users/zhongling/.acme.sh/lang.bi_ecc/ca.cer
[2025年11月21日 星期五 16时55分15秒 CST] And the full-chain cert is in: /Users/zhongling/.acme.sh/lang.bi_ecc/fullchain.cer
zhongling@MacBookPro .acme.sh % start
/Users/zhongling/.acme.sh/
zsh: command not found: start
zsh: permission denied: /Users/zhongling/.acme.sh/
zhongling@MacBookPro .acme.sh % start /Users/zhongling/.acme.sh/
zsh: command not found: start
zhongling@MacBookPro .acme.sh % open /Users/zhongling/.acme.sh/
zhongling@MacBookPro .acme.sh % open /Users/zhongling/.acme.sh/

最终,省去了更换域名的麻烦。先将就用着吧。

 

再谈WP更新逻辑

作者obaby
2025年11月4日 10:44

最近发现一个问题就是,wp 的后台打开速度越来越慢了,不过用屁股想想也能猜到肯定是 wp 后台的各种更新检查导致的。

网上通用的办法是直接禁用掉各种更新检查,但是鉴于安全问题其实我并不想直接这么干。现在各种漏洞扫描利用太频繁了,新版本能一定程度上降低被利用的风险。

wp 的更新的代码,本质上是有缓存机制的:

function _maybe_update_core() {
    $current = get_site_transient( 'update_core' );

    if ( isset( $current->last_checked, $current->version_checked )
        && 12 * HOUR_IN_SECONDS > ( time() - $current->last_checked )
        && wp_get_wp_version() === $current->version_checked
    ) {
        return;
    }

    wp_version_check();
}

检查更新的时候会判断是否在 12 小时内已经执行过,然而,这个破玩儿的问题在于,写入transient的时候不知道为什么失败了,于是每次进入后台都看到一堆 http 请求。

这 tm 就贼啦智障了,多的时候七八个请求,一个请求一秒钟,全部请求完都十几秒了。

所以,我直接基于 redis 做了个缓存机制,避免重复请求。在 24 小时内请求过就不会再次请求了。直接对请求进行拦截。

既避免了无法检查更新,也解决了每次都检查更新的问题。

代码如下:

<?php
/**
 * 使用 Redis 缓存控制所有 WordPress 更新检查
 * 
 * 控制以下更新检查的频率:
 * 1. wp_version_check() - WordPress 核心版本检查
 * 2. wp_update_themes() - 主题更新检查
 * 3. wp_update_plugins() - 插件更新检查
 * 4. wp_check_browser_version() - 浏览器版本检查
 * 5. wp_check_php_version() - PHP 版本检查
 * 
 * 不依赖 WordPress 的 transient,直接使用 Redis 缓存
 * 如果 24 小时内更新过,跳过更新检查
 * 
 * 使用方法:
 * 将此代码添加到主题的 functions.php 文件末尾
 * 或添加到插件的初始化函数中
 * https://h4ck.org.cn/wp-admin/index.php?debug_all_checks=1#qm-overview
 */

// ==========================================
// 配置项
// ==========================================

// 缓存键名
define('WP_CORE_UPDATE_CHECK_KEY', 'wp_core_update_check_time');
define('WP_THEMES_UPDATE_CHECK_KEY', 'wp_themes_update_check_time');
define('WP_PLUGINS_UPDATE_CHECK_KEY', 'wp_plugins_update_check_time');
define('WP_BROWSER_VERSION_CHECK_KEY', 'wp_browser_version_check_time');
define('WP_PHP_VERSION_CHECK_KEY', 'wp_php_version_check_time');

// 缓存有效期(24 小时)
define('WP_UPDATE_CHECK_EXPIRATION', DAY_IN_SECONDS);


// ==========================================
// 1. WordPress 核心版本检查 (wp_version_check)
// ==========================================

/**
 * 拦截 _maybe_update_core 函数
 * 在 admin_init 优先级 0 中移除 _maybe_update_core,使用 Redis 缓存控制
 */
add_action('admin_init', function() {
    // 移除 WordPress 原来的 _maybe_update_core 钩子
    remove_action('admin_init', '_maybe_update_core', 1);
    
    // 检查 Redis 缓存,看是否在 24 小时内更新过
    $last_check_time = wp_cache_get(WP_CORE_UPDATE_CHECK_KEY);
    
    if ($last_check_time !== false && is_numeric($last_check_time)) {
        $time_since_check = time() - $last_check_time;
        
        // 如果在 24 小时内,直接 return,跳过更新检查
        if ($time_since_check < WP_UPDATE_CHECK_EXPIRATION) {
            error_log('[Redis Core Check] 跳过核心版本检查 - 距离上次检查: ' . human_time_diff($last_check_time, time()));
            return; // 直接返回,不执行更新检查
        }
    }
    
    // 如果缓存过期或不存在,执行原来的更新检查逻辑
    _maybe_update_core();
}, 0); // 优先级 0,在 _maybe_update_core (优先级 1) 之前执行

/**
 * 监控 wp_version_check 的执行,设置 Redis 缓存
 * 注意:只有在缓存不存在时才设置,避免频繁刷新时重置缓存时间
 */
add_action('wp_version_check', function() {
    $existing_cache = wp_cache_get(WP_CORE_UPDATE_CHECK_KEY);
    if ($existing_cache === false) {
        wp_cache_set(
            WP_CORE_UPDATE_CHECK_KEY,
            time(),
            '',
            WP_UPDATE_CHECK_EXPIRATION
        );
        error_log('[Redis Core Check] wp_version_check 执行,已设置 Redis 缓存');
    }
}, 10);

/**
 * 通过过滤器拦截 update_core transient 设置,设置 Redis 缓存
 * 注意:只有在缓存不存在时才设置,避免频繁刷新时重置缓存时间
 */
add_filter('pre_set_site_transient_update_core', function($value, $transient_name) {
    $existing_cache = wp_cache_get(WP_CORE_UPDATE_CHECK_KEY);
    if ($existing_cache === false) {
        wp_cache_set(
            WP_CORE_UPDATE_CHECK_KEY,
            time(),
            '',
            WP_UPDATE_CHECK_EXPIRATION
        );
    }
    return $value;
}, 999, 2);


// ==========================================
// 2. 主题更新检查 (wp_update_themes)
// ==========================================

/**
 * 拦截 _maybe_update_themes 函数
 * 在 admin_init 优先级 0 中移除 _maybe_update_themes,使用 Redis 缓存控制
 */
add_action('admin_init', function() {
    // 移除 WordPress 原来的 _maybe_update_themes 钩子
    remove_action('admin_init', '_maybe_update_themes', 1);
    
    // 检查 Redis 缓存,看是否在 24 小时内更新过
    $last_check_time = wp_cache_get(WP_THEMES_UPDATE_CHECK_KEY);
    
    if ($last_check_time !== false && is_numeric($last_check_time)) {
        $time_since_check = time() - $last_check_time;
        
        // 如果在 24 小时内,直接 return,跳过更新检查
        if ($time_since_check < WP_UPDATE_CHECK_EXPIRATION) {
            error_log('[Redis Themes Check] 跳过主题更新检查 - 距离上次检查: ' . human_time_diff($last_check_time, time()));
            return; // 直接返回,不执行更新检查
        }
    }
    
    // 如果缓存过期或不存在,执行原来的更新检查逻辑
    _maybe_update_themes();
}, 0); // 优先级 0,在 _maybe_update_themes (优先级 1) 之前执行

/**
 * 监控 wp_update_themes 的执行,设置 Redis 缓存
 * 注意:只有在缓存不存在时才设置,避免频繁刷新时重置缓存时间
 */
add_action('wp_update_themes', function() {
    $existing_cache = wp_cache_get(WP_THEMES_UPDATE_CHECK_KEY);
    if ($existing_cache === false) {
        wp_cache_set(
            WP_THEMES_UPDATE_CHECK_KEY,
            time(),
            '',
            WP_UPDATE_CHECK_EXPIRATION
        );
        error_log('[Redis Themes Check] wp_update_themes 执行,已设置 Redis 缓存');
    }
}, 10);

/**
 * 通过过滤器拦截 update_themes transient 设置,设置 Redis 缓存
 * 注意:只有在缓存不存在时才设置,避免频繁刷新时重置缓存时间
 */
add_filter('pre_set_site_transient_update_themes', function($value, $transient_name) {
    $existing_cache = wp_cache_get(WP_THEMES_UPDATE_CHECK_KEY);
    if ($existing_cache === false) {
        wp_cache_set(
            WP_THEMES_UPDATE_CHECK_KEY,
            time(),
            '',
            WP_UPDATE_CHECK_EXPIRATION
        );
    }
    return $value;
}, 999, 2);


// ==========================================
// 3. 插件更新检查 (wp_update_plugins)
// ==========================================

/**
 * 方法1:在 HTTP 请求级别拦截插件更新 API
 * 直接拦截到 api.wordpress.org/plugins/update-check/ 的请求
 */
add_filter('pre_http_request', function($preempt, $args, $url) {
    // 检查是否是插件更新检查的 API 请求
    if (strpos($url, 'api.wordpress.org/plugins/update-check/') !== false) {
        // 检查 Redis 缓存,看是否在 24 小时内更新过
        $last_check_time = wp_cache_get(WP_PLUGINS_UPDATE_CHECK_KEY);
        
        if ($last_check_time !== false && is_numeric($last_check_time)) {
            $time_since_check = time() - $last_check_time;
            
            // 如果在 24 小时内,返回假数据,跳过 API 请求
            if ($time_since_check < WP_UPDATE_CHECK_EXPIRATION) {
                error_log('[Redis Plugins Check] 跳过插件更新检查 API 请求 - 距离上次检查: ' . human_time_diff($last_check_time, time()));
                
                // 返回一个符合 WordPress API 格式的响应
                // WordPress 期望的完整格式:
                // - plugins: 需要更新的插件数组
                // - no_update: 不需要更新的插件数组(必需!)
                // - translations: 翻译更新数组
                $fake_response_data = array(
                    'plugins' => array(), // 需要更新的插件(空数组表示没有更新)
                    'no_update' => array(), // 不需要更新的插件(必需字段)
                    'translations' => array() // 翻译更新(空数组表示没有更新)
                );
                
                // 编码为 JSON,确保是数组格式
                $fake_response = json_encode($fake_response_data, JSON_UNESCAPED_SLASHES);
                
                // 验证 JSON 编码是否成功
                if ($fake_response === false || json_last_error() !== JSON_ERROR_NONE) {
                    error_log('[Redis Plugins Check] JSON 编码失败: ' . json_last_error_msg());
                    // 如果编码失败,返回一个包含所有必需字段的简单有效 JSON
                    $fake_response = '{"plugins":[],"no_update":[],"translations":[]}';
                }
                
                return array(
                    'body' => $fake_response,
                    'response' => array('code' => 200, 'message' => 'OK'),
                    'headers' => array('Content-Type' => 'application/json; charset=UTF-8'),
                );
            }
        }
    }
    
    return $preempt;
}, 5, 3); // 提高优先级,确保在其他过滤器之前执行

/**
 * 方法2:拦截 _maybe_update_plugins 函数
 * 在 admin_init 优先级 0 中移除 _maybe_update_plugins,使用 Redis 缓存控制
 */
add_action('admin_init', function() {
    // 移除 WordPress 原来的 _maybe_update_plugins 钩子
    remove_action('admin_init', '_maybe_update_plugins', 1);
    
    // 检查 Redis 缓存,看是否在 24 小时内更新过
    $last_check_time = wp_cache_get(WP_PLUGINS_UPDATE_CHECK_KEY);
    
    if ($last_check_time !== false && is_numeric($last_check_time)) {
        $time_since_check = time() - $last_check_time;
        
        // 如果在 24 小时内,直接 return,跳过更新检查
        if ($time_since_check < WP_UPDATE_CHECK_EXPIRATION) {
            error_log('[Redis Plugins Check] 跳过插件更新检查 - 距离上次检查: ' . human_time_diff($last_check_time, time()));
            return; // 直接返回,不执行更新检查
        }
    }
    
    // 如果缓存过期或不存在,执行原来的更新检查逻辑
    _maybe_update_plugins();
}, 0); // 优先级 0,在 _maybe_update_plugins (优先级 1) 之前执行

/**
 * 在 HTTP 响应时设置 Redis 缓存(插件检查)
 * 只有在实际执行了 API 请求并成功获取响应后,才设置缓存
 * 同时确保响应格式正确
 */
add_filter('http_response', function($response, $args, $url) {
        // 只有在缓存不存在或已过期时,才设置缓存
        // 这样可以避免频繁刷新时不断重置缓存时间
        $existing_cache = wp_cache_get(WP_PLUGINS_UPDATE_CHECK_KEY);
        if ($existing_cache === false) {
            // 缓存不存在,说明这是真正的更新检查,设置缓存
            wp_cache_set(
                WP_PLUGINS_UPDATE_CHECK_KEY,
                time(),
                    '',
                WP_UPDATE_CHECK_EXPIRATION
            );
            error_log('[Redis Plugins Check] 插件更新检查 API 请求完成,已设置 Redis 缓存');
        } else {
            error_log('[Redis Plugins Check] 缓存已存在,不更新缓存时间');
        }
    // 检查是否是插件更新检查的 API 请求
    if (strpos($url, 'api.wordpress.org/plugins/update-check/') !== false) {
        if (!is_wp_error($response)) {

            
            // 确保响应体格式正确
            $body = wp_remote_retrieve_body($response);
            if (!empty($body)) {
                $decoded = json_decode($body, true);
                // 如果解析失败或格式不正确,修复它
                if (!is_array($decoded)) {
                    error_log('[Redis Plugins Check] JSON 解析失败,创建默认响应');
                    $decoded = array();
                }
                
                // 确保所有必需字段存在且是正确的类型
                if (!isset($decoded['plugins']) || !is_array($decoded['plugins'])) {
                    $decoded['plugins'] = array();
                }
                if (!isset($decoded['no_update']) || !is_array($decoded['no_update'])) {
                    $decoded['no_update'] = array();
                }
                if (!isset($decoded['translations']) || !is_array($decoded['translations'])) {
                    $decoded['translations'] = array();
                }
                
                // 重新编码响应体(只有在需要修复时才重新编码)
                $response['body'] = json_encode($decoded, JSON_UNESCAPED_SLASHES);
            }
        }
    }
    return $response;
}, 10, 3);

/**
 * 监控 wp_update_plugins 的执行,设置 Redis 缓存
 * 注意:只有在缓存不存在时才设置,避免频繁刷新时重置缓存时间
 */
add_action('wp_update_plugins', function() {
    // 只有在缓存不存在时,才设置缓存
    // 这样可以避免在缓存有效期内不断重置缓存时间
    $existing_cache = wp_cache_get(WP_PLUGINS_UPDATE_CHECK_KEY);
    if ($existing_cache === false) {
        wp_cache_set(
            WP_PLUGINS_UPDATE_CHECK_KEY,
            time(),
            '',
            WP_UPDATE_CHECK_EXPIRATION
        );
        error_log('[Redis Plugins Check] wp_update_plugins 执行,已设置 Redis 缓存');
    }
}, 10);

/**
 * 通过过滤器拦截 update_plugins transient 设置,设置 Redis 缓存
 * 同时确保 transient 数据格式正确
 * 注意:只有在缓存不存在时才设置,避免频繁刷新时重置缓存时间
 */
add_filter('pre_set_site_transient_update_plugins', function($value, $transient_name) {
    // 确保格式正确
    if ($value && is_object($value)) {
        // 确保 response 是数组
        if (isset($value->response) && !is_array($value->response)) {
            error_log('[Redis Plugins Check] 修复 response 格式,从 ' . gettype($value->response) . ' 转换为数组');
            $value->response = is_array($value->response) ? $value->response : array();
        }
        // 确保 translations 是数组
        if (isset($value->translations) && !is_array($value->translations)) {
            $value->translations = is_array($value->translations) ? $value->translations : array();
        }
        // 确保 checked 是数组
        if (isset($value->checked) && !is_array($value->checked)) {
            $value->checked = is_array($value->checked) ? $value->checked : array();
        }
    }
    
    // 只有在缓存不存在时,才设置 Redis 缓存
    // 这样可以避免在缓存有效期内不断重置缓存时间
    $existing_cache = wp_cache_get(WP_PLUGINS_UPDATE_CHECK_KEY);
    if ($existing_cache === false) {
        wp_cache_set(
            WP_PLUGINS_UPDATE_CHECK_KEY,
            time(),
            '',
            WP_UPDATE_CHECK_EXPIRATION
        );
        error_log('[Redis Plugins Check] transient 设置,已设置 Redis 缓存');
    }
    
    return $value;
}, 999, 2);


// ==========================================
// 4. 浏览器版本检查 (wp_check_browser_version)
// ==========================================

/**
 * 拦截 wp_check_browser_version 函数
 * 这个函数通过 HTTP 请求到 api.wordpress.org/core/browse-happy/
 * 我们使用过滤器拦截 HTTP 请求
 */
add_filter('pre_http_request', function($preempt, $args, $url) {
    // 检查是否是浏览器版本检查的 API 请求
    if (strpos($url, 'api.wordpress.org/core/browse-happy/') !== false) {
        // 检查 Redis 缓存,看是否在 24 小时内更新过
        $last_check_time = wp_cache_get(WP_BROWSER_VERSION_CHECK_KEY);
        
        if ($last_check_time !== false && is_numeric($last_check_time)) {
            $time_since_check = time() - $last_check_time;
            
            // 如果在 24 小时内,返回假数据,跳过 API 请求
            if ($time_since_check < WP_UPDATE_CHECK_EXPIRATION) {
                error_log('[Redis Browser Check] 跳过浏览器版本检查 - 距离上次检查: ' . human_time_diff($last_check_time, time()));
                // 返回一个成功的响应(避免重复请求)
                return array(
                    'body' => '{"success":true}',
                    'response' => array('code' => 200, 'message' => 'OK'),
                    'headers' => array(),
                );
            }
        }
    }
    
    return $preempt;
}, 10, 3);

/**
 * 在 HTTP 响应时设置 Redis 缓存(浏览器检查)
 * 注意:只有在缓存不存在时才设置,避免频繁刷新时重置缓存时间
 */
add_filter('http_response', function($response, $args, $url) {
    // 检查是否是浏览器版本检查的 API 请求
    if (strpos($url, 'api.wordpress.org/core/browse-happy/') !== false && !is_wp_error($response)) {
        $existing_cache = wp_cache_get(WP_BROWSER_VERSION_CHECK_KEY);
        if ($existing_cache === false) {
            wp_cache_set(
                WP_BROWSER_VERSION_CHECK_KEY,
                time(),
                '',
                WP_UPDATE_CHECK_EXPIRATION
            );
            error_log('[Redis Browser Check] wp_check_browser_version 执行,已设置 Redis 缓存');
        }
    }
    return $response;
}, 10, 3);

/**
 * 监控 wp_check_browser_version 的执行,设置 Redis 缓存
 * 作为备用方案,通过动作钩子捕获
 * 注意:只有在缓存不存在时才设置,避免频繁刷新时重置缓存时间
 */
add_action('wp_check_browser_version', function() {
    $existing_cache = wp_cache_get(WP_BROWSER_VERSION_CHECK_KEY);
    if ($existing_cache === false) {
        wp_cache_set(
            WP_BROWSER_VERSION_CHECK_KEY,
            time(),
            '',
            WP_UPDATE_CHECK_EXPIRATION
        );
        error_log('[Redis Browser Check] wp_check_browser_version 执行,已设置 Redis 缓存');
    }
}, 10);


// ==========================================
// 5. PHP 版本检查 (wp_check_php_version)
// ==========================================

/**
 * 拦截 wp_check_php_version 函数
 * 这个函数通过 HTTP 请求到 api.wordpress.org/core/serve-happy/
 * 我们使用过滤器拦截 HTTP 请求
 */
add_filter('pre_http_request', function($preempt, $args, $url) {
    // 检查是否是 PHP 版本检查的 API 请求
    if (strpos($url, 'api.wordpress.org/core/serve-happy/') !== false) {
        // 检查 Redis 缓存,看是否在 24 小时内更新过
        $last_check_time = wp_cache_get(WP_PHP_VERSION_CHECK_KEY);
        
        if ($last_check_time !== false && is_numeric($last_check_time)) {
            $time_since_check = time() - $last_check_time;
            
            // 如果在 24 小时内,返回假数据,跳过 API 请求
            if ($time_since_check < WP_UPDATE_CHECK_EXPIRATION) {
                error_log('[Redis PHP Check] 跳过 PHP 版本检查 - 距离上次检查: ' . human_time_diff($last_check_time, time()));
                // 返回一个成功的响应(避免重复请求)
                return array(
                    'body' => '{"success":true}',
                    'response' => array('code' => 200, 'message' => 'OK'),
                    'headers' => array(),
                );
            }
        }
    }
    
    return $preempt;
}, 10, 3);

/**
 * 在 HTTP 响应时设置 Redis 缓存(PHP 检查)
 * 注意:只有在缓存不存在时才设置,避免频繁刷新时重置缓存时间
 */
add_filter('http_response', function($response, $args, $url) {
    // 检查是否是 PHP 版本检查的 API 请求
    if (strpos($url, 'api.wordpress.org/core/serve-happy/') !== false && !is_wp_error($response)) {
        $existing_cache = wp_cache_get(WP_PHP_VERSION_CHECK_KEY);
        if ($existing_cache === false) {
            wp_cache_set(
                WP_PHP_VERSION_CHECK_KEY,
                time(),
                '',
                WP_UPDATE_CHECK_EXPIRATION
            );
            error_log('[Redis PHP Check] wp_check_php_version 执行,已设置 Redis 缓存');
        }
    }
    return $response;
}, 10, 3);

/**
 * 监控 wp_check_php_version 的执行,设置 Redis 缓存
 * 作为备用方案,通过动作钩子捕获
 * 注意:只有在缓存不存在时才设置,避免频繁刷新时重置缓存时间
 */
add_action('wp_check_php_version', function() {
    $existing_cache = wp_cache_get(WP_PHP_VERSION_CHECK_KEY);
    if ($existing_cache === false) {
        wp_cache_set(
            WP_PHP_VERSION_CHECK_KEY,
            time(),
            '',
            WP_UPDATE_CHECK_EXPIRATION
        );
        error_log('[Redis PHP Check] wp_check_php_version 执行,已设置 Redis 缓存');
    }
}, 10);


// ==========================================
// 兜底方案:在 shutdown 时检查并设置缓存
// ==========================================

/**
 * 在页面加载结束时检查
 * 如果更新检查执行了但没有设置缓存,在这里设置
 */
add_action('shutdown', function() {
    // 检查核心更新
    $core_transient = get_site_transient('update_core');
    if ($core_transient && is_object($core_transient)) {
        $last_check = wp_cache_get(WP_CORE_UPDATE_CHECK_KEY);
        if ($last_check === false && isset($core_transient->last_checked)) {
            wp_cache_set(
                WP_CORE_UPDATE_CHECK_KEY,
                $core_transient->last_checked,
                '',
                WP_UPDATE_CHECK_EXPIRATION
            );
        }
    }
    
    // 检查主题更新
    $themes_transient = get_site_transient('update_themes');
    if ($themes_transient && is_object($themes_transient)) {
        $last_check = wp_cache_get(WP_THEMES_UPDATE_CHECK_KEY);
        if ($last_check === false && isset($themes_transient->last_checked)) {
            wp_cache_set(
                WP_THEMES_UPDATE_CHECK_KEY,
                $themes_transient->last_checked,
                '',
                WP_UPDATE_CHECK_EXPIRATION
            );
        }
    }
    
    // 检查插件更新
    $plugins_transient = get_site_transient('update_plugins');
    if ($plugins_transient && is_object($plugins_transient)) {
        $last_check = wp_cache_get(WP_PLUGINS_UPDATE_CHECK_KEY);
        if ($last_check === false && isset($plugins_transient->last_checked)) {
            wp_cache_set(
                WP_PLUGINS_UPDATE_CHECK_KEY,
                $plugins_transient->last_checked,
                '',
                WP_UPDATE_CHECK_EXPIRATION
            );
        }
    }
    
    // 浏览器和 PHP 检查可能没有标准的 transient,使用当前时间
    // 但这里我们已经在动作钩子中处理了
}, 999);


// ==========================================
// 在后台显示状态(可选,用于调试)
// ==========================================

add_action('admin_notices', function() {
    // 只在管理员权限时显示
    if (!current_user_can('manage_options')) {
        return;
    }
    
    // 只在 URL 参数包含 debug_all_checks 时显示
    // 或者在后台任何页面都显示(方便调试)
    $show_debug = isset($_GET['debug_all_checks']) || 
                  (defined('WP_DEBUG') && WP_DEBUG && defined('WP_DEBUG_DISPLAY') && WP_DEBUG_DISPLAY);
    
    if (!$show_debug) {
        return;
    }
    
    // 确保常量已定义
    if (!defined('WP_CORE_UPDATE_CHECK_KEY')) {
        return;
    }
    
    $checks = array(
        'WordPress 核心版本检查' => WP_CORE_UPDATE_CHECK_KEY,
        '主题更新检查' => WP_THEMES_UPDATE_CHECK_KEY,
        '插件更新检查' => WP_PLUGINS_UPDATE_CHECK_KEY,
        '浏览器版本检查' => WP_BROWSER_VERSION_CHECK_KEY,
        'PHP 版本检查' => WP_PHP_VERSION_CHECK_KEY,
    );
    
    echo '<div class="notice notice-info is-dismissible">';
    echo '<p><strong>Redis 更新检查状态:</strong></p>';
    echo '<ul style="margin-left: 20px;">';
    
    foreach ($checks as $name => $key) {
        $last_check = wp_cache_get($key);
        
        if ($last_check !== false && is_numeric($last_check)) {
            $time_since = time() - $last_check;
            $hours_remaining = (WP_UPDATE_CHECK_EXPIRATION - $time_since) / 3600;
            
            echo '<li><strong>' . esc_html($name) . ':</strong>';
            echo '上次检查:' . date('Y-m-d H:i:s', $last_check) . ' | ';
            echo '距离现在:' . human_time_diff($last_check, time()) . ' | ';
            
            if ($hours_remaining > 0) {
                echo '剩余时间:' . number_format($hours_remaining, 1) . ' 小时 | ';
            } else {
                echo '已过期 | ';
            }
            
            echo '下次检查:' . date('Y-m-d H:i:s', $last_check + WP_UPDATE_CHECK_EXPIRATION);
            echo '</li>';
        } else {
            echo '<li><strong>' . esc_html($name) . ':</strong>未设置缓存,下次访问后台将执行检查。</li>';
        }
    }
    
    echo '</ul>';
    echo '<p><small>提示:访问 <code>?debug_all_checks=1</code> 可查看此状态,或启用 WP_DEBUG 模式</small></p>';
    echo '</div>';
}, 10); // 使用优先级 10,确保在 admin_init 之后执行


// ==========================================
// 工具函数:手动清除所有缓存(用于测试)
// ==========================================

/**
 * 手动清除所有 Redis 缓存,强制下次检查更新
 * 访问:/?clear_all_check_cache=1
 */
add_action('init', function() {
    if (isset($_GET['clear_all_check_cache']) && current_user_can('manage_options')) {
        wp_cache_delete(WP_CORE_UPDATE_CHECK_KEY);
        wp_cache_delete(WP_THEMES_UPDATE_CHECK_KEY);
        wp_cache_delete(WP_PLUGINS_UPDATE_CHECK_KEY);
        wp_cache_delete(WP_BROWSER_VERSION_CHECK_KEY);
        wp_cache_delete(WP_PHP_VERSION_CHECK_KEY);
        wp_die('所有 Redis 缓存已清除,下次访问后台将执行更新检查。', '缓存已清除', array('back_link' => true));
    }
}, 1);

也可以直接用插件,插件地址:

https://github.com/obaby/baby-wp-update-config-tool

⚠ 重要提示

在使用本插件之前,您需要首先配置 WordPress 的 Redis 缓存功能。

本插件依赖于 Redis 缓存系统来存储更新检查的时间戳。如果您的 WordPress 站点未配置 Redis 缓存,插件将无法正常工作。请确保:

  1. 已安装并配置 Redis 服务器
  2. 已安装 WordPress Redis 缓存插件(如 Redis Object Cache、WP Redis 等)
  3. Redis 缓存功能已正常启用并运行

如果 Redis 缓存未配置,插件在初始化时会记录警告信息,但不会阻止插件运行。

❌