搜索全站
性能优化 15 分钟阅读

WordPress oEmbed 嵌入功能详解:用法、性能影响与关闭方法

粘贴一个 B 站链接就自动变成播放器,靠的就是 oEmbed。但它会引入额外的 JS、增加 DNS 查询、并让所有站点都能嵌入你的文章。本文讲清 oEmbed 的工作机制、支持的站点、四个隐藏代价,以及禁用与优化的完整代码。

U
UFO_PF 2019-12-28 · 阅读 15 分钟
Wo 性能优化
阅读提示
本文约 5,795 字,预计阅读 15 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:方便背后的三个隐形代价

WordPress 4.4 引入 oEmbed 之后,在编辑器里粘贴一个 YouTube、Twitter、B 站的链接,它会自动变成嵌入播放器。这个功能确实方便。

但它有三个不太被注意的代价:

  1. 每个页面都加载 wp-embed.min.js(哪怕文章里一个嵌入都没有);
  2. 首次嵌入要做一次外部 HTTP 请求去拉取 oEmbed 数据,慢且可能超时;
  3. 任何 WordPress 站点都能嵌入你的文章(wp-embed 端点默认开启),等于把内容开放给了外部调用。

对性能敏感的站点,这三点都值得处理。

原理分析:oEmbed 是怎么工作的

oEmbed 是一个开放协议:资源方(如 YouTube)提供一个 oEmbed 接口,消费方(你的 WordPress)请求它拿到嵌入 HTML。

流程:

粘贴 URL → WordPress 识别为 oEmbed 提供方 → 请求对方的 /oembed 端点
        → 拿到 HTML 片段 → 存进 postmeta 缓存 → 前台输出 iframe

WordPress 用 WP_Embed 类实现,通过 WP_oEmbed 做发现:

// 注册一个 oEmbed 提供方
wp_oembed_add_provider( 'https://bilibili.com/video/*', 'https://api.bilibili.com/x/web-interface/view' );

内置支持的常见站点(部分):

站点 说明
YouTube 需要能访问 youtube.com
Twitter / X 需要能访问
Vimeo、Flickr、SoundCloud、Spotify 需要能访问
Instagram、TikTok 需要能访问
国内的 B 站、优酷、腾讯视频 默认不支持,需要插件或手工注册

这是国内站点最容易踩的坑:WordPress 内置的提供方全是境外站点,国内直接粘贴 B 站链接不会自动嵌入,而且因为要请求境外接口,还会拖慢编辑器加载。

缓存机制:WordPress 把拉取到的嵌入 HTML 存在 wp_postmeta 的 _oembed_xxx 键里:

SELECT post_id, meta_key FROM wp_postmeta WHERE meta_key LIKE '_oembed_%';

所以第一次慢,之后走缓存。

保姆级实操

一、正常使用

在区块编辑器里:粘贴 URL 到空行 → 自动出现”嵌入”提示 → 点”嵌入”即可。

或者用专门的嵌入区块(YouTube / Vimeo / Twitter 等独立区块)。

二、给国内站点加 oEmbed 支持(以 B 站为例)

add_action( 'init', function() {
    // B 站没有标准 oEmbed 端点,用自定义解析
    wp_embed_register_handler(
        'bilibili',
        '#https?://(?:www\.)?bilibili\.com/video/(?:av(\d+)|BV([0-9A-Za-z]+))#i',
        'mmf_bilibili_embed_handler'
    );
});

function mmf_bilibili_embed_handler( $matches, $attr, $url, $rawattr ) {
    $bvid = isset( $matches[2] ) ? $matches[2] : '';
    if ( empty( $bvid ) ) {
        return '';
    }
    $embed = sprintf(
        '<div class="video-wrap"><iframe src="//player.bilibili.com/player.html?bvid=%s&high_quality=1" scrolling="no" frameborder="0" allowfullscreen="true" loading="lazy"></iframe></div>',
        esc_attr( $bvid )
    );
    return apply_filters( 'embed_bilibili', $embed, $matches, $attr, $url, $rawattr );
}

配套 CSS(响应式 16:9):

.video-wrap {
    position: relative;
    padding-bottom: 56.25%;
    height: 0;
    overflow: hidden;
    max-width: 100%;
}
.video-wrap iframe {
    position: absolute;
    top: 0; left: 0;
    width: 100%; height: 100%;
}

三、禁用 oEmbed(性能优化常见操作)

完全禁用(包括”别人嵌入我的文章”):

// 停用 oEmbed 发现
remove_action( 'wp_head', 'wp_oembed_add_discovery_links' );

// 移除前台的 wp-embed.min.js
add_action( 'wp_enqueue_scripts', function() {
    wp_deregister_script( 'wp-embed' );
}, 100 );

// 禁用 REST 的 oEmbed 端点
add_filter( 'rest_endpoints', function( $endpoints ) {
    unset( $endpoints['/oembed/1.0/embed'] );
    return $endpoints;
});

// 彻底关闭 oEmbed 解析(粘贴 URL 不再自动嵌入)
add_filter( 'embed_oembed_discover', '__return_false' );

只移除 JS 但保留嵌入功能(最常用,效果最好):

add_action( 'wp_footer', function() {
    wp_deregister_script( 'wp-embed' );
} );

说明:wp-embed.min.js 只用于”嵌入本站文章时的自适应高度通信”和”嵌入内容的响应式调整”。文章里放 YouTube iframe 不依赖它。所以移除它是安全的。

四、阻止别人嵌入自己的文章(内容保护)

// 移除 oEmbed 发现链接
remove_action( 'wp_head', 'wp_oembed_add_discovery_links' );
remove_action( 'wp_head', 'wp_oembed_add_host_js' );

// 服务端拒绝
add_filter( 'rest_endpoints', function( $endpoints ) {
    unset( $endpoints['/oembed/1.0/embed'] );
    return $endpoints;
});

再在 Nginx 层加 X-Frame-Options(更彻底):

add_header X-Frame-Options "SAMEORIGIN" always;

但注意:这条会让所有 iframe 嵌入失效,包括你自己文章里的 YouTube 播放器。只想阻止外部站点嵌入,用 Content-Security-Policy:

add_header Content-Security-Policy "frame-ancestors 'self'" always;

五、清理 oEmbed 缓存

改了嵌入内容后前台没变,是 postmeta 缓存:

DELETE FROM wp_postmeta WHERE meta_key LIKE '_oembed_%';
wp post meta delete $(wp post list --format=ids) _oembed_time_xxx   # 逐个

批量脚本:

wp eval '
global $wpdb;
$n = $wpdb->query("DELETE FROM {$wpdb->postmeta} WHERE meta_key LIKE \"_oembed_%\"");
echo "deleted: {$n}\n";
'

六、用嵌入做懒加载(性能提升明显)

视频 iframe 是前台最大性能杀手之一。改成”点击才加载”:

add_filter( 'embed_oembed_html', function( $html, $url, $attr, $post_id ) {
    if ( is_admin() ) {
        return $html;
    }
    // 只在有 iframe 时处理
    if ( false === strpos( $html, '<iframe' ) ) {
        return $html;
    }
    $thumb = '';
    if ( preg_match( '/youtube\.com\/watch\?v=([\w-]+)/', $url, $m )
         || preg_match( '/youtu\.be\/([\w-]+)/', $url, $m ) ) {
        $thumb = 'https://i.ytimg.com/vi/' . $m[1] . '/hqdefault.jpg';
    }
    return sprintf(
        '<div class="lazy-embed" data-embed="%s" %s>
            <div class="lazy-embed-btn">▶ 点击加载视频</div>
         </div>',
        esc_attr( base64_encode( $html ) ),
        $thumb ? 'style="background-image:url(' . esc_url( $thumb ) . ')"' : ''
    );
}, 10, 4 );
document.addEventListener('click', function (e) {
    var box = e.target.closest('.lazy-embed');
    if (!box) return;
    box.insertAdjacentHTML('beforeend', atob(box.dataset.embed));
    box.classList.add('loaded');
});

常见踩坑与失败案例

  1. 案例一:某站在国内服务器上,编辑器里粘贴 YouTube 链接一直转圈,最后超时——WordPress 去请求 youtube.com/oembed 拿不到数据。国内站点应当禁用境外 oEmbed,改用 iframe 或国内视频源。
  2. 案例二:移除 wp-embed 脚本后,自己文章里嵌入的本站文章(跨文章嵌入)高度不对。这是 wp-embed.min.js 唯一的真实作用,遇到这种情况就别移除它。
  3. 禁用后旧文章里的嵌入失效:不会。embed_oembed_discover 只影响新解析,已缓存的 HTML 还在 postmeta 里,前台照常输出。只有清了缓存才会失效。
  4. 缓存的 oEmbed meta 堆积:每篇嵌入过的文章都有几条 _oembed_*,文章多了会膨胀 postmeta。定期清理。
  5. Twitter/X 嵌入在国内不显示:需要能访问 platform.twitter.com 的 JS。国内站点要么放弃,要么用截图+链接替代。
  6. X-Frame-Options 加了导致自己视频也打不开:用 CSP 的 frame-ancestors 代替,它只限制”谁可以嵌我”,不影响我嵌别人。
  7. oEmbed 的 HTTP 请求拖慢后台:编辑器加载时会做 oEmbed 发现请求。国内站点建议直接关掉发现:add_filter('embed_oembed_discover','__return_false')。

总结

oEmbed 的价值是”粘贴即嵌入”,代价是额外的 JS、外部 HTTP 请求、以及内容被外部嵌入。国内站点的务实做法:禁掉境外 oEmbed 发现(避免编辑器卡在请求 YouTube/Twitter),为 B 站等国内站点手工注册 handler,移除 wp-embed.min.js(除非用到跨文章嵌入),视频一律做点击懒加载。内容保护用 CSP 的 frame-ancestors,不要用 X-Frame-Options(会误伤自己的视频嵌入)。

U
UFO_PF作者

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

留下你的想法

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