标签: WordPress

  • WordPress AI Provider for DeepSeek 插件实现分析

    WordPress AI Provider for DeepSeek 插件实现分析

    前言

    WordPress 6.9 引入了 PHP AI Client SDK,为 WordPress 生态提供了统一的 AI 服务接入标准。本文分析我最近实现的 AI Provider for DeepSeek 插件,它是该 SDK 的 DeepSeek 适配实现,同时支持作为 Composer 包和 WordPress 插件两种使用方式。

    插件架构概览

    插件的目录结构遵循 PSR-4 规范:

    wp-ai-provider-for-deepseek/
    ├── plugin.php                          # WordPress 插件入口
    ├── readme.txt                        # WordPress 插件描述
    ├── assets/images/deepseek.svg        # 供应商 Logo
    └── src/
        ├── autoload.php                 # PSR-4 自动加载器
        ├── Provider/
        │   ├── DeepSeekProvider.php            # 主 Provider 类
        │   └── DeepSeekProviderAvailability.php # API Key 可用性检查器
        ├── Models/
        │   └── DeepSeekTextGenerationModel.php # 文本生成模型实现
        └── Metadata/
            └── DeepSeekModelMetadataDirectory.php # 模型元数据目录
    

    核心实现分析

    1. 插件入口与 Hook 注册(plugin.php)

    插件通过三个关键 Hook 完成初始化:

    // 注册 AI Provider(优先级 5,确保在 AI Client 之后加载)
    add_action('init', __NAMESPACE__ . '\\register_provider', 5);
    
    // 输出 Provider 数据到前端(确保 admin 页面可用)
    add_action('admin_enqueue_scripts', __NAMESPACE__ . '\\ensure_provider_data_output', 5);
    
    // 注册 Connector(WordPress 连接器系统)
    add_action('wp_connectors_init', __NAMESPACE__ . '\\register_connector', 10);
    
    // 自动审批 Connector 使用权限(优先级 20,确保 Approvals_Store 可用)
    add_action('init', __NAMESPACE__ . '\\auto_approve_connector', 20);
    

    设计亮点:

    • register_provider() 先检查 AiClient 类是否存在,避免依赖未安装时 fatal error
    • 使用 $registry->hasProvider() 防止重复注册
    • ensure_provider_data_output() 处理边缘情况:当 AI Client 的脚本未加载时,手动输出 window.aiProviderData

    2. Provider 主类(DeepSeekProvider.php)

    核心类继承自 AbstractApiProvider,实现四个工厂方法:

    class DeepSeekProvider extends AbstractApiProvider
    {
        // API 基础 URL
        protected static function baseUrl(): string
        {
            return 'https://api.deepseek.com';
        }
    
        // 创建模型实例(根据能力分发到具体模型类)
        protected static function createModel(...): ModelInterface
        {
            foreach ($capabilities as $capability) {
                if ($capability->isTextGeneration()) {
                    return new DeepSeekTextGenerationModel(...);
                }
            }
            throw new \RuntimeException('Unsupported model capabilities');
        }
    
        // 创建 Provider 元数据(支持版本化特性)
        protected static function createProviderMetadata(): ProviderMetadata
        {
            $args = [
                'deepseek',
                'DeepSeek',
                ProviderTypeEnum::cloud(),
                'https://platform.deepseek.com/api_keys',
                RequestAuthenticationMethod::apiKey()
            ];
    
            // 1.2.0+ 支持描述
            if (version_compare(AiClient::VERSION, '1.2.0', '>=')) {
                $args[] = __('Text generation with DeepSeek models.', 'ai-provider-for-deepseek');
            }
    
            // 1.3.0+ 支持 Logo
            if (version_compare(AiClient::VERSION, '1.3.0', '>=')) {
                $args[] = dirname(__DIR__, 2) . '/assets/images/deepseek.svg';
            }
    
            return new ProviderMetadata(...$args);
        }
    
        // 自定义可用性检查器(用真实 API 请求验证 Key)
        protected static function createProviderAvailability(): ProviderAvailabilityInterface
        {
            return new DeepSeekProviderAvailability(static::class);
        }
    
        // 模型元数据目录(动态从 API 发现可用模型)
        protected static function createModelMetadataDirectory(): ModelMetadataDirectoryInterface
        {
            return new DeepSeekModelMetadataDirectory();
        }
    }
    

    版本兼容处理是这段代码的精华:通过 version_compare 逐步添加新特性支持,确保插件在旧版 PHP AI Client 上也能正常运行。

    3. API Key 验证与缓存(DeepSeekProviderAvailability.php)

    DeepSeek 不支持标准的”列出模型”端点,因此插件采用真实请求验证法:发送一个 max_tokens=1 的最小化请求,根据 HTTP 状态码判断 Key 是否有效。

    public function isConfigured(): bool
    {
        // 1. 检查缓存(5 分钟过期)
        $cached = get_transient(self::CACHE_KEY);
        if ($cached !== false) {
            return (bool) $cached;
        }
    
        // 2. 获取 API Key(优先 WordPress 选项,fallback 到环境变量)
        $apiKey = $this->getApiKey();
        if (empty($apiKey)) {
            $this->cacheResult(false);
            return false;
        }
    
        // 3. 发送最小化请求
        $response = wp_remote_post($url, [
            'headers' => [
                'Content-Type'  => 'application/json',
                'Authorization' => 'Bearer ' . $apiKey,
            ],
            'body' => json_encode([
                'model'    => 'deepseek-v4-flash',
                'messages' => [['role' => 'user', 'content' => 'test']],
                'max_tokens' => 1,
            ]),
            'timeout' => 30,
        ]);
    
        // 4. 根据状态码判断(200/400 = Key 有效,401/403 = Key 无效)
        $responseCode = wp_remote_retrieve_response_code($response);
        $isConfigured = !in_array($responseCode, [401, 403], true);
    
        // 5. 缓存结果
        $this->cacheResult($isConfigured);
        return $isConfigured;
    }
    

    缓存策略有效减少了重复验证请求:使用 WordPress Transient API,5 分钟过期。注意错误时不缓存,允许下次重试。

    4. 模型元数据动态发现(DeepSeekModelMetadataDirectory.php)

    插件通过调用 DeepSeek 的 /models 端点动态获取可用模型列表,而非硬编码:

    protected function parseResponseToModelMetadataList(Response $response): array
    {
        $responseData = $response->getData();
        
        // 定义 DeepSeek 模型支持的能力和选项
        $textGenerationCapabilities = [
            CapabilityEnum::textGeneration(),
            CapabilityEnum::chatHistory(),
        ];
        
        $textGenerationOptions = [
            new SupportedOption(OptionEnum::systemInstruction()),
            new SupportedOption(OptionEnum::candidateCount()),
            new SupportedOption(OptionEnum::maxTokens()),
            new SupportedOption(OptionEnum::temperature()),
            new SupportedOption(OptionEnum::topP()),
            new SupportedOption(OptionEnum::customOptions()),
            new SupportedOption(OptionEnum::inputModalities()),  // 修复:添加必需的能力声明
            new SupportedOption(OptionEnum::outputModalities()),
        ];
    
        // 将 API 响应映射为 ModelMetadata 对象
        $models = array_map(function ($modelData) use ($textGenerationCapabilities, $textGenerationOptions) {
            return new ModelMetadata(
                $modelData['id'],    // model ID(如 deepseek-v4-flash)
                $modelData['id'],    // model name
                $textGenerationCapabilities,
                $textGenerationOptions
            );
        }, $responseData['data']);
    
        // 排序:deepseek-v4-flash 优先,其次是 deepseek-v4-pro,然后字母序
        usort($models, [$this, 'modelSortCallback']);
        
        return $models;
    }
    

    注意inputModalitiesoutputModalities 的添加——这是修复 ModelRequirements::fromPromptData()generate_text() 调用的关键,缺少这两个选项会导致运行时错误。

    5. 文本生成模型实现(DeepSeekTextGenerationModel.php)

    DeepSeek API 兼容 OpenAI 的 Chat Completions 格式,因此直接继承 AbstractOpenAiCompatibleTextGenerationModel

    class DeepSeekTextGenerationModel extends AbstractOpenAiCompatibleTextGenerationModel
    {
        protected function createRequest(
            HttpMethodEnum $method,
            string $path,
            array $headers = [],
            $data = null
        ): Request {
            // 覆盖父类:使用 DeepSeek Provider 的 URL 而非 OpenAI 的
            return new Request(
                $method,
                DeepSeekProvider::url($path),  // => https://api.deepseek.com/chat/completions
                $headers,
                $data,
                $this->getRequestOptions()
            );
        }
    }
    

    这个实现非常简洁,因为大部分工作(请求格式化、响应解析、流式输出等)都由父类 AbstractOpenAiCompatibleTextGenerationModel 完成。只需要覆盖 createRequest() 将请求路由到 DeepSeek 的 API 地址即可。

    Connector 审批系统

    WordPress 的 AI Client 引入了 Connector 审批机制:每个插件使用 Connector(如 DeepSeek)前必须获得审批。本插件在 init hook(优先级 20)自动审批自己:

    function auto_approve_connector(): void
    {
        if (!class_exists('\WordPress\AI\Connector_Approval\Approvals_Store')) {
            return;  // 旧版 AI Client 不支持审批系统
        }
    
        $store = new \WordPress\AI\Connector_Approval\Approvals_Store();
        $plugin_basename = plugin_basename(__FILE__);
        $connector_id = 'deepseek';
    
        if ($store->is_approved($plugin_basename, $connector_id)) {
            return;  // 已审批,跳过
        }
    
        $store->set_approval($plugin_basename, $connector_id, true);
    }
    

    这避免了用户手动在设置页面审批插件,提升了用户体验。

    使用方式

    作为 WordPress 插件

    // 1. 安装并激活插件(依赖 PHP AI Client 插件)
    // 2. 配置 API Key
    putenv('DEEPSEEK_API_KEY=your-api-key');
    
    // 3. 使用
    $result = AiClient::prompt('解释量子计算')
        ->usingProvider('deepseek')
        ->generateTextResult();
    
    echo $result->toText();
    

    作为 Composer 包

    composer require wordpress/ai-provider-for-deepseek
    
    <?php
    use WordPress\AiClient\AiClient;
    use WordPress\DeepSeekAiProvider\Provider\DeepSeekProvider;
    
    // 注册 Provider
    $registry = AiClient::defaultRegistry();
    $registry->registerProvider(DeepSeekProvider::class);
    
    // 设置 API Key
    putenv('DEEPSEEK_API_KEY=your-api-key');
    
    // 生成文本
    $result = AiClient::prompt('Explain quantum computing')
        ->usingProvider('deepseek')
        ->generateTextResult();
    
    echo $result->toText();
    

    技术亮点总结

    特性 实现方式 价值
    版本兼容 version_compare 渐进式添加特性 支持旧版 AI Client,无 breaking change
    API Key 验证 最小化真实请求 + Transient 缓存 准确验证 + 减少重复请求
    动态模型发现 调用 /models API + 元数据映射 自动支持新模型,无需更新代码
    Connector 审批 init hook 自动审批 零配置用户体验
    OpenAI 兼容 继承 AbstractOpenAiCompatible* 复用大量现有逻辑,代码量极小
    双模式运行 Composer 包 + WordPress 插件 最大灵活性,可在任何 PHP 项目中使用

    结论

    这个插件的实现展示了如何为一个新的 AI 服务商编写符合 WordPress PHP AI Client 标准的 Provider。核心要点:

    1. 继承正确的抽象类:文本生成继承 AbstractOpenAiCompatibleTextGenerationModel
    2. 实现四个工厂方法baseUrl()createModel()createProviderMetadata()createModelMetadataDirectory()
    3. 自定义可用性检查:当标准方法(列出模型)不可用时,用真实请求验证
    4. 版本兼容处理:使用 version_compare 渐进式添加新特性支持
    5. WordPress 集成:正确注册 Hook、Connector、审批

    完整代码已开源在 GitHub,欢迎试用和贡献。

  • WordPress 6.9.4 正式发布:更新内容速览

    WordPress 6.9.4 已于 2026年04月15日 正式发布。以下是本次更新的主要内容整理,供站长参考。

    ⚠️ 安全版本:本次更新包含安全修复,建议所有用户尽快升级。

    本次 6.9.4 是一个安全维护版本,修复了 10 个安全问题。WordPress 安全团队发现此前版本中部分安全修复未完全生效,因此紧急发布 6.9.4 补丁。

    🔒 安全修复

    • 路径穿越漏洞(PclZip 组件)
    • 授权绕过漏洞
    • XXE(XML 外部实体注入)漏洞

    如何升级

    登录 WordPress 后台 → 仪表盘 → 更新,点击「立即更新」即可完成升级。建议升级前先备份数据库和文件。

    参考链接

    本文由自动化脚本监测到 WordPress 新版本后自动生成,仅供参考。

  • Dnote网站深度诊断报告:发现的问题与优化建议

    Dnote网站深度诊断报告:发现的问题与优化建议

    前言

    最近对个人博客网站 dnote.cn 进行了一次全面的结构分析和诊断,发现了一些值得关注的问题。本文将详细记录发现的问题并提供相应的优化建议。

    一、网站基础数据

    • 文章总数:100篇
    • 分类数:3个(编程95篇、投资18篇、未分类3篇)
    • 标签数:22个
    • 页面数:3个(关于、小玩意儿、搜索)
    • 媒体文件:50个

    二、发现的问题

    1. 内容分类严重失衡

    在100篇文章中,编程类占据了95篇,比例高达95%,而投资类仅有18篇。这反映出内容定位过于单一,建议:

    • 将部分技术文章重新归类,细化分类标签
    • 考虑增加新的分类维度,如”产品”、”创业”、”生活”
    • 将”未分类”的3篇文章尽快归类

    2. SEO基础优化不足

    通过分析发现以下SEO问题:

    • 缺少结构化数据:未发现JSON-LD富媒体标记,错失搜索展示机会
    • 内链锚文本单一:多为”查看更多→”,应使用关键词锚文本
    • 文章摘要较短:部分文章描述信息不足,影响搜索结果吸引力

    3. 性能优化空间

    • 未使用CDN:静态资源未通过CDN分发
    • 缺少Gzip压缩:建议开启以减少传输体积
    • 缺少浏览器缓存配置:应设置合理的缓存策略

    4. 标签使用混乱

    22个标签中,部分标签存在重复或过细的问题,如同时存在”SQL”和”MySQL”、”Docker”和”Linux”等,建议进行标签整合。

    5. 文章日期异常

    所有文章显示发布日期为2026年3月13日(部分甚至显示2025年),这可能是:

    • 批量导入时的伪日期
    • 系统时间配置问题
    • WordPress时区设置问题

    建议核查并修正,避免搜索引擎误判内容为过期内容。

    三、优化建议汇总

    优先级 问题 建议方案
    分类失衡 重新划分分类,增加3-5个新分类
    日期异常 检查WordPress时区设置,修正文章日期
    SEO元标签 添加JSON-LD结构化数据
    内链优化 使用关键词作为锚文本
    性能优化 接入CDN,配置Gzip和缓存

    四、结语

    整体而言,网站技术架构合理,WordPress运行稳定,内容质量较高。主要问题集中在内容组织结构和SEO基础优化上。解决这些问题后,网站的搜索可见性和用户体验都会有明显提升。

    后续我会逐步进行优化并记录改进效果,敬请期待。

  • 优化WordPress后台加载速度

    优化WordPress后台加载速度

    1. PHP预加载脚本

    <?php
    
    /**
    *使用OPcache优化WordPress的预加载脚本。
    *将此文件放在WordPress安装的根目录中。
     */
    
    // Define the base path for WordPress
    define('WP_ROOT_DIR', __DIR__);
    
    // Define ABSPATH (required by WordPress core files)
    if ( ! defined( 'ABSPATH' ) ) {
        define( 'ABSPATH', WP_ROOT_DIR . '/' );
    }
    
    // Define WPINC (required by wp-includes/functions.php)
    if ( ! defined( 'WPINC' ) ) {
        define( 'WPINC', 'wp-includes' );
    }
    
    // Define WP_DEBUG (required by wp-includes/functions.php)
    if ( ! defined( 'WP_DEBUG' ) ) {
        define( 'WP_DEBUG', false );
    }
    
    // 仅预加载最基本的核心文件
    require WP_ROOT_DIR . '/wp-includes/default-constants.php';
    require WP_ROOT_DIR . '/wp-includes/rewrite.php';
    require WP_ROOT_DIR . '/wp-includes/theme.php';
    require WP_ROOT_DIR . '/wp-includes/post.php';
    require WP_ROOT_DIR . '/wp-includes/meta.php';
    require WP_ROOT_DIR . '/wp-includes/user.php';
    require WP_ROOT_DIR . '/wp-includes/cache.php';
    require WP_ROOT_DIR . '/wp-includes/capabilities.php';
    require WP_ROOT_DIR . '/wp-includes/shortcodes.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-query.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-widget.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-roles.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-user.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-post.php';
    
    // 后台管理模块的类
    require WP_ROOT_DIR . '/wp-admin/includes/class-wp-list-table.php';
    require WP_ROOT_DIR . '/wp-admin/includes/class-wp-media-list-table.php';
    require WP_ROOT_DIR . '/wp-admin/includes/class-wp-users-list-table.php';
    require WP_ROOT_DIR . '/wp-admin/includes/class-wp-themes-list-table.php';
    
    // Preload database-related files
    require WP_ROOT_DIR . '/wp-includes/wp-db.php';
    // require WP_ROOT_DIR . '/wp-includes/class-wpdb.php'; // 如果不需要,可以注释
    
  • WordPress添加关键词和描述标签

    WordPress添加关键词和描述标签

    关键词和描述标签作为SEO的基础配置,在wordpress中没有默认添加,这里记录一下在Wordpress中自动添加关键词和描述标签的方法。

    一、实现代码

    实现思路是使用标签来作为关键词,使用文章摘要作为页面描述,找到使用主题的functions.php文件,添加以下代码即可实现。

    /**
     * 添加SEO相关的Keywords和Description标签
     */
    function add_seo_meta_tags() {
        if (is_home()) {
            $tags = get_tags([
                'number' => 15,
                'orderby' => 'count',
                'order' => 'DESC'
            ]);
            $tags = array_map(function ($item) {
                return $item->name;
            }, $tags);
            if (count($tags) > 0) {
                ?>
                <meta name="keywords" content="<?php echo get_bloginfo('name'); ?>,<?php echo implode(',', $tags); ?>">
                <?php
            }
            ?>
                <meta name="description" content="<?php echo get_bloginfo('description'); ?>">
            <?php
        } else if (is_category() || is_tag()) {
            ?>
                <meta name="keywords" content="<?php echo single_cat_title(); ?>">
                <meta name="description" content="<?php echo strip_tags(category_description()); ?>">
            <?php
        } else if (is_singular()) {
            ?>
                <meta name="description" content="<?php echo strip_tags(get_the_excerpt()); ?>">
            <?php
    
            $tags = array_map(function ($item) {
                return $item->name;
            }, get_the_tags() ?: []);
            if (count($tags) > 0) {
                ?>
                    <meta name="keywords" content="<?php echo implode(',', $tags); ?>">
                <?php
            }
        }
    }
    add_action( 'wp_head', 'add_seo_meta_tags' );
    

    二、使用的前置要求

    请注意,需要保证主题的header.php调用过wp_head()函数,类似下面:

    <!DOCTYPE html>
    <html <?php language_attributes(); ?> class="no-js">
    <head>
    <meta charset="<?php bloginfo( 'charset' ); ?>">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    
    <?php wp_head(); ?>
    </head>
    

    OK,这样我们就给所有页面都添加上关键词和描述标签了~

    相关链接:

    1. wordpress关于meta标签的说明
  • 使用Docker Compose部署NextCloud和WordPress

    使用Docker Compose部署NextCloud和WordPress

    一、全局配置

    name: lnmp
    services:
      caddy:
        image: caddy:latest
        volumes:
          - ./www:/var/www/html
          - ./caddy/etc:/etc/caddy
          - ./caddy/data:/data
          - ./caddy/config:/config
        ports:
          - 80:80
          - 443:443/tcp
          - 443:443/udp
        logging:
          driver: "json-file"
          options:
            max-size: "10m"
            max-file: 3
        restart: always
    
      redis:
        image: redis:latest
        volumes:
          - ./redis/config:/etc/redis
          - ./redis/data:/data
        restart: always
        command: /etc/redis/redis.conf
    
      mysql:
        image: mysql:latest
        volumes:
          - ./mysql/config:/etc/mysql
          - ./mysql/data:/var/lib/mysql
          - ./mysql/mysql-files:/var/lib/mysql-files
        cap_add:
          - SYS_NICE
        security_opt:
          - seccomp:unconfined
        environment:
          MYSQL_ROOT_PASSWORD: password
        ports:
          - 3306:3306
        restart: always
    
      php:
        build: ./php
        volumes:
          - ./php/config:/usr/local/etc
          - ./php/logs:/var/log/php
          - ./www:/var/www/html
        depends_on:
          - caddy
          - mysql
          - redis
        cap_add:
          - SYS_PTRACE
        logging:
          driver: "json-file"
          options:
            max-size: "10m"
            max-file: 3
        restart: always
    
      imaginary:
        image: nextcloud/aio-imaginary:latest
        restart: always
        command: -concurrency 2 -enable-url-source
        environment:
          - PORT=9000
    

    1. PHP

    PHP官方的镜像启用和安装的扩展比较少,直接使用会导致WordPress和Nextcloud的健康检查一堆信息,所以使用Dockerfile来基于官方镜像构建一个专用的镜像,PHP需要的扩展包括:

    • gd(png/jpeg/gif/webp/avif)
    • imagick
    • opcache(考虑性能)
    • apcu(Nextcloud的本地缓存)
    • zip(影响WordPress插件安装)
    • redis(WordPress的对象缓存和Nextcloud的分布式缓存)
    • gmp
    • intl
    FROM registry.cn-beijing.aliyuncs.com/ianzhi/php:8.4-fpm-alpine
    
    # 配置国内镜像
    RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
    
    # 安装构建扩展相关依赖
    RUN apk add --no-cache --update --virtual .build-deps $PHPIZE_DEPS
    
    # MySQL
    RUN docker-php-ext-install pdo_mysql mysqli \
      && docker-php-ext-enable pdo_mysql mysqli
    
    # 常用扩展
    RUN docker-php-ext-install pcntl exif bcmath sysvsem \
        && docker-php-ext-enable opcache exif bcmath pcntl sysvsem
    
    # apcu
    RUN pecl install apcu && docker-php-ext-enable apcu
    
    # zip扩展
    RUN apk add --no-cache --update libzip=1.11.4-r0 libzip-dev=1.11.4-r0 unzip \
      && docker-php-ext-install zip \
      && docker-php-ext-enable zip
    
    # redis
    RUN pecl install https://pecl.php.net/get/redis-6.2.0.tgz \
      && docker-php-ext-enable redis
    
    # intl
    RUN apk add --no-cache --update icu icu-dev \
      && docker-php-ext-configure intl \
      && docker-php-ext-install intl \
      && docker-php-ext-enable intl
    
    # imagick
    RUN apk add --no-cache --update imagemagick-dev imagemagick-svg \
     && pecl install https://pecl.php.net/get/imagick-3.8.0.tgz \
     && docker-php-ext-enable imagick
    
    # gd
    RUN apk add --no-cache --update libpng libpng-dev libavif-dev libjpeg-turbo-dev freetype-dev freetype libjpeg-turbo libavif  \
      && docker-php-ext-configure gd --with-freetype --with-jpeg --with-avif \
      && docker-php-ext-install gd \
      && docker-php-ext-enable gd
    
    # ffmpeg nextcloud需要视频转码时启用
    # RUN apk add --no-cache ffmpeg
    
    # gmp nextcloud使用加密时使用
    # RUN apk add --no-cache --update gmp-dev \
    # && docker-php-ext-install gmp \
    # && docker-php-ext-enable gmp
    
    # pgsql
    # RUN apk add --no-cache --update libpq-dev postgresql-dev \
    #   && docker-php-ext-install pdo_pgsql \
    #   && docker-php-ext-enable pdo_pgsql
    
    # 删除构建依赖
    RUN apk del --no-network .build-deps
    
    # 配置文件
    RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini
    
  • WordPress常用插件

    WordPress常用插件

    WordPress提供了完善的插件机制,通过使用插件,我们可以优化Wordpress的性能、丰富Wordpress的功能,但是插件市场的插件实现也良莠不齐的,记录一些使用体验优秀的插件。

    一、性能优化插件

    1. Performance Lab

    WordPress性能团队提供的一些独立性能优化插件,是一个插件组合,包含了一系列可以优化WordPress站点性能的插件,包括但不限于:

    • 推测性加载
    • 现代图片格式支持
    • 翻译性能优化
    • web worker
    • 视图过渡动效

    Performance plugin from the WordPress Performance Team, which is a collection of standalone performance modules.

    2. Redis Object Cache

    WordPress的对象缓存插件。

    A persistent object cache backend powered by Redis. Supports Predis, PhpRedis, Relay, replication, sentinels, clustering and WP-CLI.

    3. Super Cache

    WordPress官方提供的快速缓存插件,支持CDN、预缓存以及cache-control相关功能等。

    4. Simple Cloudflare Turnstile

    这个是在登录页面增加一个Cloudflare验证码,避免一些尝试暴力破解登录密码的脚本。

    Easily add Cloudflare Turnstile to all your WordPress website forms to protect them from spam!

    A user-friendly, privacy-preserving reCAPTCHA alternative.

    二、REST API插件

    1. JWT Auth

    提供REST API的JWT认证,没有管理界面。

    三、功能插件

    1. woocommerce

    一站式帮助您在数天内推出一个在线商店并保持持续发展。 从您的第一笔交易到赚得数百万收入,Woo 都与您同在。

    2. Action Scheduler

    A robust scheduling library for use in WordPress plugins.

    3. Two-Factor

    通过“用户”→“个人资料”下的“双重身份验证选项”部分,为您的账户启用和配置一个或多个双重身份验证方式:

    • 邮件验证码
    • 基于时间的一次性密码(TOTP)
    • FIDO 通用第二因素(U2F)
    • 备用验证码
    • 虚拟方式(仅用于测试目的)

    4. 安全自定义字段

    安全自定义字段(SCF)扩展了 WordPress 的权限,将其转化为灵活的内容管理工具。有了 SCF,管理自定义数据变得简单而高效。

    按需轻松创建字段。 SCF 生成器可以轻松地将字段添加到 WordPress 编辑屏幕,无论您是为菜谱添加新的「成分」字段,还是为专业网站设计复杂的元数据。

    **灵活放置。**字段可应用于整个 WordPress–文章、页面、用户、分类法、媒体、评论,甚至自定义选项页面–按照您的意愿组织数据。

    **无缝显示。**使用 SCF 功能,您可以在模板中显示自定义字段数据,使所有级别的开发人员都能轻松实现内容集成。

    **全面的内容管理解决方案。**除自定义字段外,SCF 还允许您直接从 SCF 界面注册新的文章类型和分类法,从而提供更多控制,而无需额外的插件或自定义代码。

    **易于访问和用户友好的设计。**字段界面与 WordPress 的本地设计保持一致,为内容创建者创造了一种易于访问和使用的体验。

  • 如何避免WordPress/Woocommerce网站被黑

    如何避免WordPress/Woocommerce网站被黑

    WordPress本身的安全性相对来说还是值得信任的,但是依然还是有一些缺点,那么如何避免Wordpress站点被黑呢?我们来看看纯净Wordpress站点存在的一些安全隐患吧。

    一、后台登陆的暴力破解隐患

    默认的Wordpress站点是没有任何防止暴力破解措施的,几乎可以无限尝试密码,这增加了网站后台被暴力破解的风险。这个问题解决方案有很多种,我们可以在不安装插件的情况下,透过几种简单措施来增加被暴力破解的难度:

    1. 增加登录页面的Basic认证,避免登录页面被脚本扫描
    2. 不使用与显示名称类似的登录名,使用一个复杂的、包含多因素的登录账号
    3. 使用长度超过15位的、包含多因素的登陆密码

    当然,这些措施都无法彻底解决被暴力破解的问题,只是很大程度上增加了暴力破解的难度,阻挡了很多的“脚本小子”。相对来说,这些措施如果都实施的话,对于绝大多数网站来说就已经是相对安全的了,尽管依然有风险,但是按照现在的算力来看,破解这些密码相对来说也是非常困难的。不过为了保证安全性,我们还可以应用更多的安全措施,比如通过安装一些安全类的插件,来提供以下一些措施:

    1. 增加图形验证码,进一步增加破解难度
    2. 增加邮箱、短信验证码
    3. 使用MFA
    4. 限制账号登录失败的尝试次数
    5. 通过增加常用设备、常在地区、常用设备记录,在异地、陌生设备时要求多因素认证

    在应用了这些措施后,WEB本身的问题,很难说绝对安全,但是通过暴力破解来认证为管理员用户基本不可能的。

    二、数据丢失隐患

    网站被黑后,很多的WebShell存在锁文件、删除文件的破坏性行为,如果要实现在网站被黑后能够快速回复的目标,网站数据备份工作就是必不可少的。

    本地备份依然是不安全的,毕竟当服务器被黑时,服务器内所有文件都是不能保证安全的。这个问题,我们可以透过一些云服务商的存储服务来解决备份问题,最简单的就是使用云服务商提供的磁盘快照功能。当然我们也可以通过安装一些插件来实现将数据备份到网络存储的方式来实现备份功能,比如将数据存储在Google Drive、Microsoft One Drive、AWS的s3、国内阿里云的OSS、腾讯云的COS等。

    三、用户上传文件的安全

    如果我们的网站时开放注册的,我们还需要考虑用户上传文件的安全问题。这方面我们只能通过安装或者自行实现插件来接入一些WebShell查杀工具来保证网站的安全性,比如可以接入百度的WEBDIR来监测上传文件的安全性。

    四、内容审查

    如果网站开发注册,我们还需要关注的就是用户生成内容的安全,这个问题相对来说还是很重的,尤其是部署于国内的服务,UGC审查必不可少,防止用户发布一些违反法律的内容出去导致网站甚至本人出现违法、甚至犯罪的问题。这方面我们可以选择通过插件来接入云服务商的内容安全监测服务来实现,这方面的服务相对来说也比较多,可以根据实际需要来选择接入。

  • WordPress/woocommerce性能优化

    WordPress/woocommerce性能优化

    默认安装的 WordPress 性能表现很一般,尤其如果添加了 woocommerce 插件后,后台总有一种卡顿的感觉,为了提升 WordPress/woocommerce 的使用体验,需要进行一些性能优化操作。WordPress 本身是一个基于 PHP 实现的程序,因此要优化使用体验就要在 LNMP/LAMP 架构的组件配置上进行调整。这篇文章介绍一些 LNMP 架构中,对于 WordPress 性能有影响的配置项,理论上应该也适用于其他 PHP 项目。

    注意:本篇文章需要技术背景,标记★代表强烈建议启用。

    一、Nginx配置

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 60;
    

    1.1 GZIP压缩

    gzip on;
    gzip_vary on;
    gzip_disable "msie6";
    gzip_min_length 1k;
    gzip_comp_level 5;
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript application/x-httpd-php;
    

    1.2 静态缓存★

    location ~.*.(js|css|html|png|jpg|jpeg|gif|gz|svg|mp4|ogg|ogv|webm|htc|xml|woff|zip|webp)$
    {
        access_log off;
        expires  30d;
        try_files $uri =404;
    }
    

    二、PHP配置

    2.1 opcache配置★

    [opcache]
    ; 启用opcache
    opcache.enable = 1
    
    ; opcache使用的内存量
    opcache.memory_consumption = 256M
    
    ; 存储字符串的缓冲区大小,单位是MB
    opcache.interned_strings_buffer = 32
    
    ; 可存储的脚本文件数量上限
    opcache.max_accelerated_files = 1979
    
    ; 设置重新验证脚本缓存的频率,以秒为单位。如果设置为0,则每次请求都会重新验证缓存
    opcache.revalidate_freq = 60
    
    ; 启用或禁用时间戳验证。如果启用此功能,OPcache会在每次请求时检查脚本的时间戳以确定是否需要重新加载脚本
    opcache.validate_timestamps = 0
    
    ; 启用会将缓存持久化到文件
    opcache.file_cache = 0
    
    ; 是否将注释保存到缓存中,如果依赖注释中的注解需要启用,wordpress中没有使用
    opcache.save_comments = 0
    
    ; php8以上版本启用jit
    opcache.jit = 1235
    opcache.enable_cli = 1
    

    2.2 预加载

    预加载从PHP7.4开始支持,具体可以参考4.2

    三、WordPress调整

    可以通过直接修改WordPress配置文件(wp-config.php)或者安装插件进行的优化项目。

    3.1 关闭默认的CRON执行器

    <?php
    ...
    define( 'DISABLE_WP_CRON', true );
    

    可以通过系统的crontab来执行定时任务:

    # 修改crontab配置
    crontab -e
    
    # 添加下面行
    * * * * * php -f /path/to/wp-cron.php
    

    3.2 静态文件压缩

    <?php
    ...
    define( 'COMPRESS_CSS', true ); // 压缩CSS
    define( 'COMPRESS_SCRIPTS', true ); // 压缩JS
    define( 'ENFORCE_GZIP', true ); // 强制启用GZIP,如果WEB服务器启用了GZIP可以不开启
    define( 'CONCATENATE_SCRIPTS', true ); // 连接脚本
    

    3.3 插件优化

    插件也会对WordPress的性能有很大影响,有一些优秀的插件会极大优化WordPress性能表现,可以参考下面文章。

    WordPress常用插件

    WordPress常用插件

    四、MySQL/MariaDB数据库配置优化

    4.1 数据库配置优化

    适用于小型实例,中大型示例参考参数备注进行调整。数据库my.cnf配置文件中添加以下配置:

    # 表定义缓存数量,最低400
    table_definition_cache = 400
    # 打开表缓存
    table_open_cache = 500
    # 关闭performance_schema,大型实例不建议采用,大概能节省数据库默认占用一半的内存
    performance_schema = off
    
    # innodb的缓存
    # 数值越大,内存中占用越大,同时数据库性能也会提高。纯数据库服务器可以使用内存的 70%-80% 。
    # 单机部署时最好多测试一下
    innodb_buffer_pool_size = 256M
    
    # 尽可能增加innodb_buffer_pool_size
    innodb_buffer_pool_size = 512M
    
    # 决定了事务日志何时被写入并刷新到磁盘上
    # 0:每秒将日志写入并刷新到磁盘
    # 1:每次事务提交时,都将日志写入并刷新到磁盘
    # 2:每次事务提交时,都将日志写入日志缓冲区,但仅在每秒刷新到磁盘
    # 选择0/2,宕机时有可能丢失1秒内事务数据
    innodb_flush_log_at_trx_commit = 0
    

    4.2 持久化数据库连接

    查看WordPress性能团队的聊天记录突然发现WordPress默认情况下是没有启用持久连接的(数据库连接池),会导致每一次的请求都需要初始化数据库连接。解决办法比较简单,在 wp-content 目录下放一个 db.php 文件,写入以下内容,然后在配置文件中添加一个值为 trueDB_PERSIST 常量即可。

    这个小问题应该会在不久的未来经过测试后合并到WordPress核心中,此方法只适用于未合并前的这段时间,合并后就不需要了,目前6.5.2版本暂时还没有提供。

    关于PHP中的持久化连接,有兴趣可以参考官网中以下两篇文章:

    • PDO连接与连接管理
    • mysqli 扩展和持久化连接
    <?php
    
    if ( !class_exists('wpdb') ) {
        require_once ABSPATH . WPINC . '/class-wpdb.php';
    }
    
    class custom_wpdb extends wpdb {
    
        /**
         * Connects to and selects database.
         *
         * If `$allow_bail` is false, the lack of database connection will need to be handled manually.
         *
         * @since 3.0.0
         * @since 3.9.0 $allow_bail parameter added.
         *
         * @param bool $allow_bail Optional. Allows the function to bail. Default true.
         * @return bool True with a successful connection, false on failure.
         */
        public function db_connect( $allow_bail = true ) {
            $this->is_mysql = true;
    
            $client_flags = defined( 'MYSQL_CLIENT_FLAGS' ) ? MYSQL_CLIENT_FLAGS : 0;
    
            /*
             * Set the MySQLi error reporting off because WordPress handles its own.
             * This is due to the default value change from `MYSQLI_REPORT_OFF`
             * to `MYSQLI_REPORT_ERROR|MYSQLI_REPORT_STRICT` in PHP 8.1.
             */
            mysqli_report( MYSQLI_REPORT_OFF );
    
            $this->dbh = mysqli_init();
    
            $host    = $this->dbhost;
            $port    = null;
            $socket  = null;
            $is_ipv6 = false;
    
            $host_data = $this->parse_db_host( $this->dbhost );
            if ( $host_data ) {
                list( $host, $port, $socket, $is_ipv6 ) = $host_data;
            }
    
            /*
             * If using the `mysqlnd` library, the IPv6 address needs to be enclosed
             * in square brackets, whereas it doesn't while using the `libmysqlclient` library.
             * @see https://bugs.php.net/bug.php?id=67563
             */
            if ( $is_ipv6 && extension_loaded( 'mysqlnd' ) ) {
                $host = "[$host]";
            }
    
            if ( DB_PERSIST ) {
                $host = 'p:' . $host;
            }
    
            if ( WP_DEBUG ) {
                mysqli_real_connect( $this->dbh, $host, $this->dbuser, $this->dbpassword, null, $port, $socket, $client_flags );
            } else {
                // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
                @mysqli_real_connect( $this->dbh, $host, $this->dbuser, $this->dbpassword, null, $port, $socket, $client_flags );
            }
    
            if ( $this->dbh->connect_errno ) {
                $this->dbh = null;
            }
    
            if ( ! $this->dbh && $allow_bail ) {
                wp_load_translations_early();
    
                // Load custom DB error template, if present.
                if ( file_exists( WP_CONTENT_DIR . '/db-error.php' ) ) {
                    require_once WP_CONTENT_DIR . '/db-error.php';
                    die();
                }
    
                $message = '<h1>' . __( 'Error establishing a database connection' ) . "</h1>n";
    
                $message .= '<p>' . sprintf(
                    /* translators: 1: wp-config.php, 2: Database host. */
                    __( 'This either means that the username and password information in your %1$s file is incorrect or that contact with the database server at %2$s could not be established. This could mean your host&#8217;s database server is down.' ),
                    '<code>wp-config.php</code>',
                    '<code>' . htmlspecialchars( $this->dbhost, ENT_QUOTES ) . '</code>'
                ) . "</p>n";
    
                $message .= "<ul>n";
                $message .= '<li>' . __( 'Are you sure you have the correct username and password?' ) . "</li>n";
                $message .= '<li>' . __( 'Are you sure you have typed the correct hostname?' ) . "</li>n";
                $message .= '<li>' . __( 'Are you sure the database server is running?' ) . "</li>n";
                $message .= "</ul>n";
    
                $message .= '<p>' . sprintf(
                    /* translators: %s: Support forums URL. */
                    __( 'If you are unsure what these terms mean you should probably contact your host. If you still need help you can always visit the <a href="%s">WordPress support forums</a>.' ),
                    __( 'https://wordpress.org/support/forums/' )
                ) . "</p>n";
    
                $this->bail( $message, 'db_connect_fail' );
    
                return false;
            } elseif ( $this->dbh ) {
                if ( ! $this->has_connected ) {
                    $this->init_charset();
                }
    
                $this->has_connected = true;
    
                $this->set_charset( $this->dbh );
    
                $this->ready = true;
                $this->set_sql_mode();
                $this->select( $this->dbname, $this->dbh );
    
                return true;
            }
    
            return false;
        }
    }
    
    global $wpdb;
    
    $dbuser     = defined( 'DB_USER' ) ? DB_USER : '';
    $dbpassword = defined( 'DB_PASSWORD' ) ? DB_PASSWORD : '';
    $dbname     = defined( 'DB_NAME' ) ? DB_NAME : '';
    $dbhost     = defined( 'DB_HOST' ) ? DB_HOST : '';
    
    $wpdb = new custom_wpdb( $dbuser, $dbpassword, $dbname, $dbhost );
    

    配置文件中:

    define( 'DB_PERSIST', true );
    

    4.3 PHP预加载★

    这部分是确定不会带来什么问题的,其他的应该还有很多也可以预加载,但是没有测试过,后面更新再说。

    ; 这是php.ini配置文件,确保PHP版本>=7.4
    ; Specifies a PHP script that is going to be compiled and executed at server
    ; start-up.
    ; https://php.net/opcache.preload
    ;opcache.preload=
    opcache.preload=/var/www/html/preload.php
    
    <?php
    
    /**
     * 使用OPcache优化WordPress的预加载脚本。
     * 
     * 参考文档: https://www.php.net/manual/zh/opcache.preloading.php
     */
    // Define the base path for WordPress
    define('WP_ROOT_DIR', __DIR__ . '/wordpress');
    
    // wp-includes
    require WP_ROOT_DIR . '/wp-includes/class-wp.php';
    require WP_ROOT_DIR . '/wp-includes/class-wpdb.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-query.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-widget.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-roles.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-user.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-user-query.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-post.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-error.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-term.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-term-query.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-theme.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-theme-json.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-tax-query.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-site.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-dependencies.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-scripts.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-object-cache.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-network.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-admin-bar.php';
    require WP_ROOT_DIR . '/wp-includes/class-wp-application-passwords.php';
    
    // wp-admin/includes
    require WP_ROOT_DIR . '/wp-admin/includes/class-wp-list-table.php';
    require WP_ROOT_DIR . '/wp-admin/includes/class-wp-media-list-table.php';
    require WP_ROOT_DIR . '/wp-admin/includes/class-wp-plugins-list-table.php';
    require WP_ROOT_DIR . '/wp-admin/includes/class-wp-posts-list-table.php';
    require WP_ROOT_DIR . '/wp-admin/includes/class-wp-screen.php';
    

    以上就是LNMP架构中各组件的一些优化配置,文章持续更新中。

  • How to improve wordpress performance

    How to improve wordpress performance

    There are a number of things you can do to improve the performance of your WordPress site:

    • Choose a good hosting provider. A reliable hosting provider with fast servers is essential for a fast-loading WordPress site. Look for a provider that offers managed WordPress hosting, as this will take care of many of the technical aspects of running a WordPress site, freeing you up to focus on creating content.
    • Use the latest version of WordPress. WordPress is constantly being updated with new features and security patches. Make sure you’re running the latest version to take advantage of these improvements and to protect your site from security vulnerabilities.
    • Keep your plugins up to date. Plugins can add a lot of functionality to your WordPress site, but they can also slow it down if they’re not up to date. Make sure you regularly check for and install plugin updates to ensure your site is running as smoothly as possible.
    • Optimize your images. Large images can slow down your site, so it’s important to optimize them before you upload them. You can use a free tool like TinyPNG or Smush to compress your images without sacrificing quality.
    • Use a caching plugin. A caching plugin can store static copies of your pages and posts, which can be served to visitors instead of having to generate them each time. This can significantly improve the performance of your site.
    • Use a content delivery network (CDN). A CDN is a network of servers that can deliver your site’s static assets (such as images, CSS, and JavaScript) from servers that are closer to your visitors. This can improve the performance of your site for visitors all over the world.
    • Minify your CSS and JavaScript. Minifying your CSS and JavaScript files can reduce their size, which can improve the performance of your site. You can use a free tool like Minify Code to minify your files.
    • Avoid using plugins that you don’t need. Every plugin you add to your site adds a potential performance hit. If you’re not using a plugin, disable or delete it to improve your site’s performance.
    • Regularly backup your site. It’s important to regularly backup your site in case something goes wrong and you need to restore it. You can use a free tool like UpdraftPlus to backup your site to the cloud.

    By following these tips, you can improve the performance of your WordPress site and provide a better experience for your visitors.

  • WordPress站点网络更换主站域名

    WordPress站点网络更换主站域名

    一. 修改配置文件

    1. 登录到服务器,找到wp-config.php文件
    <?php
    define('WP_SITEURL', 'https://新域名.com');
    define('WP_HOME', 'https://新域名.com');
    

    二. 更新数据库

    站点网络需要更新的表比单站点多:

    # 更新wp_blogs表(站点网络的核心表):
    UPDATE wp_blogs SET domain = '新域名.com' WHERE blog_id = 1;
    
    # 更新wp_options表:
    UPDATE wp_options SET option_value = 'https://新域名.com' WHERE option_name = 'siteurl' OR option_name = 'home';
    
    # 更新所有站点的URL(确保网络中所有子站点也更新):
    UPDATE wp_options SET option_value = REPLACE(option_value, '旧域名.com', '新域名.com') WHERE option_name = 'siteurl' OR option_name = 'home';
    
    #更新wp_posts和wp_postmeta(确保文章内容中的链接也更新):
    UPDATE wp_posts SET post_content = REPLACE(post_content, '旧域名.com', '新域名.com');
    UPDATE wp_postmeta SET meta_value = REPLACE(meta_value, '旧域名.com', '新域名.com');
    
  • 使用WordPress作为小程序后端——APPID有效性前置检查

    使用WordPress作为小程序后端——APPID有效性前置检查

    上一篇实现了一个简单的前置检查,这一篇我们来聊一聊如何实现APPID的有效性检查。上一篇中,我们只是简单的将APPID获取到并传递到了请求处理函数中,这一篇,我们来实现一个APPID有效性的前置检查,或者叫中间件。

    APPID的检查相对比较简单,我们可以透过一种比较Wordpress的方式来实现:

    <?php
    
    add_filter('wechat_mp_permission_callback', function ($permission, WP_REST_Request $request) {
        if ($permission === false) {
            return false;
        }
    
        /**
         * @var $wechat_mp_apps
         * @example
         * [
         *     'APP_ID' => 'APP_SECRET'
         * ]
         */
        $wechat_mp_apps = apply_filters( 'wechat_mp_apps', [] );
        $attrs = $request->get_attributes();
        return array_key_exists($attrs['app_id'], $wechat_mp_apps);
    }, 10, 2);
    
  • 构建WordPress插件、主题开发镜像

    构建PHP镜像

    PHP内置服务器的路由脚本可以参考这个链接:使用PHP内置服务器运行WORDPRESS

    FROM php:8.1.27-alpine
    RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories 
    && mkdir /code 
    && docker-php-ext-install mysqli
    CMD [ "php", "-S", "0.0.0.0:8080", "-t", "/code", "/code/router.php" ]

    配置compose

    version: "3"
    services:
      wordpress:
        build: .
        volumes:
          - /Users/ianzhi/Code/php/wordpress:/code
        restart: always
        ports:
          - 8080:8080
      mysql:
        image: mysql:8.0.36
        environment:
          MYSQL_ROOT_PASSWORD: 'root'
        expose:
          - 3306
        ports:
          - 3306:3306
    
  • 使用Caddy反向代理WordPress

    具体配置

    xxx.xxxx.xxx {
        tls youremail@yourmailserver
    
        root * /path/to/wordpress
    
        # GZIP和FPM配置
        encode gzip
        file_server
        php_fastcgi php:9000
    
        # 静态文件配置
        @static_files {
            path_regexp .(?:css|js|woff2?|svg|gif|map|png|jpg|webp|gif|jpeg|mp4|mp3|wav|mov|heic)
        }
        header @static_files {
            Cache-Control "public, max-age=15778463"
            X-Robots-Tag "none"
            X-Permitted-Cross-Domain-Policies "none"
            X-Frame-Options "SAMEORIGIN"
            X-Download-Options "noopen"
            X-Content-Type-Options "nosniff"
            # Referrer-Policy "no-referrer"
        }
    
        # 禁止访问的目录/文件
        @disallowed {
            #path /wp-cron.php
            #path /xmlrpc.php
            path *.sql
            path /wp-content/uploads/*.php
            path /wp-content/uploads/*.html
            path /wp-content/debug.log
        }
        rewrite @disallowed =404
    }