WordPress 站外链接与安全跳转:透明、固定、可回滚

这篇 2011 年的文章曾介绍旧插件、手工上传 ZIP、联盟链接和访问者画像式点击跟踪。这些做法不应原样照搬。现在更稳妥的原则是:能直接链接就直接链接;确需站内短路径时,只把固定路径映射到固定的 HTTPS 目标,并清楚告诉读者将去哪里以及是否存在商业关系。

本文是维护于 2026 年 9 月 1 日的安全迁移指南,不是流量、客户或收入承诺。文末保留经有限脱敏的历史原文,供理解旧页面来历;其中的服务、下载地址和操作说明均不再推荐。

先问:真的需要跳转吗?

直接外链通常最简单、最透明,也减少插件、缓存、日志和失效点。使用能说明目的地的链接文字,例如“查看 Example 的产品文档”,不要只写“点击这里”。除非有明确理由,不要强制新窗口;若必须新开,应提前告知并使用 rel="noopener"

站内跳转只适合少数可说明的用途:管理一个将来可能迁移的长期资源地址、修复历史短链接,或使用已明确披露的固定商业链接。它不应用来伪装目标、绕过平台规则、隐藏风险,或接受访客传入的任意目标。

三种实现方式

方式 适用情形 优点 主要责任
直接外链 默认选择 透明、无跳转链、维护面最小 定期检查目标与披露
官方目录中的受维护插件 路由较多,需要界面、导入或导出 便于非开发者管理 跟进更新、限制权限、关闭或缩减日志
Web 服务器固定规则 少量稳定映射,能管理服务器配置 不依赖 WordPress,请求路径短 配置审查、测试、发布与回滚
小型 WordPress 实现 少量且与站点代码一起管理的映射 行为可审计,能使用 WordPress 安全跳转 代码维护、冲突与缓存测试

若选插件,只从 WordPress 后台连接的官方插件目录安装一个当前仍维护、与你的 WordPress/PHP 版本兼容的插件。目录中的 Redirection 是截至本文维护日期仍有更新记录的例子,但这不是安全担保或商业推荐。不要使用旧文中的第三方 ZIP、失效下载站或未知镜像。

状态码不是“SEO 开关”

状态码 含义 方法处理 本场景建议
302 临时 历史上可能把后续请求改成 GET 固定、仅接收 GET 的可变映射默认值
307 临时 保留请求方法与请求体 仅在确实需要保留方法时;本文端点拒绝非 GET
301 永久 客户端可能把 POST 改成 GET 只有目的地真正长期不变且已充分测试时
308 永久 保留请求方法与请求体 同样只用于真正永久的规则

这些语义来自 HTTP Semantics(RFC 9110)。浏览器、搜索引擎和 CDN 可能缓存永久跳转,甚至临时跳转也可能受缓存策略影响。第一次上线优先用 302,确认无误后再决定是否永久化;修改规则后同时清理应用缓存和 CDN 缓存。

方案 A:插件,但保持固定和克制

在暂存环境安装并更新插件,然后为每个短路径配置一个精确来源路径和一个固定 HTTPS 目标。不要启用正则、通配符或“从查询参数读取目标”之类的功能;不要建立 ?url=https://…?next=… 或支持 javascript:data: 等协议的路由。未知短路径应保持 404,而不是跳到用户提供的地址。

先导出/备份现有规则,检查 /go/ 命名空间是否与页面、文章、分类、媒体、翻译前缀或其他插件冲突。将管理权限限制给确实需要的管理员。默认关闭点击日志;若插件无法满足最小化和删除要求,就不使用其统计功能。发布后保留可恢复的配置导出和回滚步骤。

方案 B:Web 服务器上的一个固定规则

下面的 Caddy 示例只匹配无查询字符串的 GET /go/example-docs,目标完全写死。path 默认是精确匹配;不要添加 *,也不要把 {uri}、查询字符串或访客输入拼进目标。

@outbound_example {
    method GET
    path /go/example-docs
    query ""
}
redir @outbound_example https://docs.example.org/product 302

先在暂存配置中使用 `caddy validate` 验证,再依照现有受管服务的发布流程加载。Caddy 的 `redir`请求匹配器文档说明了这些字段。若站点实际由其他服务器托管,应使用该服务器的官方文档表达同一个“精确路径 → 固定目标”模型,而不是直接复制不适用的配置。

方案 C:最小、固定的 WordPress 实现

下面示例可作为一个自有的小插件,经代码审查后放入暂存站。它没有管理界面,只接受 GET,拒绝短路径上的任何查询字符串,只从代码里的映射表取目标,并要求 https 和精确主机名相符。它临时把那个精确外部主机加入允许列表,调用 `wp_safe_redirect()`,随后移除过滤器并 exit

<?php
/**
 * Plugin Name: Fixed Outbound Redirects
 */

function lazying_fixed_outbound_routes() {
    return array(
        'go/example-docs' => array(
            'url'  => 'https://docs.example.org/product',
            'host' => 'docs.example.org',
        ),
    );
}

add_action(
    'template_redirect',
    static function () {
        $method = isset( $_SERVER['REQUEST_METHOD'] )
            ? strtoupper( wp_unslash( $_SERVER['REQUEST_METHOD'] ) )
            : '';

        if ( 'GET' !== $method ) {
            return;
        }

        $request_uri = isset( $_SERVER['REQUEST_URI'] )
            ? wp_unslash( $_SERVER['REQUEST_URI'] )
            : '';
        $path        = wp_parse_url( $request_uri, PHP_URL_PATH );

        if ( ! is_string( $path ) ) {
            return;
        }

        $slug   = trim( $path, '/' );
        $routes = lazying_fixed_outbound_routes();

        if ( ! isset( $routes[ $slug ] ) ) {
            return;
        }

        $query = wp_parse_url( $request_uri, PHP_URL_QUERY );

        if ( is_string( $query ) && '' !== $query ) {
            nocache_headers();
            status_header( 404 );
            exit;
        }

        $route         = $routes[ $slug ];
        $destination   = $route['url'];
        $expected_host = strtolower( $route['host'] );
        $scheme        = strtolower( (string) wp_parse_url( $destination, PHP_URL_SCHEME ) );
        $host          = strtolower( (string) wp_parse_url( $destination, PHP_URL_HOST ) );
        $user          = wp_parse_url( $destination, PHP_URL_USER );
        $pass          = wp_parse_url( $destination, PHP_URL_PASS );

        if (
            'https' !== $scheme
            || $host !== $expected_host
            || null !== $user
            || null !== $pass
        ) {
            status_header( 500 );
            exit;
        }

        $allow_exact_host = static function ( $hosts ) use ( $host ) {
            $hosts[] = $host;
            return array_values( array_unique( $hosts ) );
        };

        add_filter( 'allowed_redirect_hosts', $allow_exact_host );
        nocache_headers();
        $sent = wp_safe_redirect( $destination, 302, 'Fixed Outbound Redirects' );
        remove_filter( 'allowed_redirect_hosts', $allow_exact_host );

        if ( $sent ) {
            exit;
        }

        status_header( 500 );
        exit;
    },
    0
);

`wp_safe_redirect()`会用 `wp_validate_redirect()` 检查允许的主机,但它不会自动终止执行,所以成功后仍需 exit。这里没有基于访客输入扩大主机列表,也没有通配主机。映射变更走代码审查;如果以后添加管理界面,应使用 Settings API,同时检查类似 manage_options 的适当权限并验证 nonce。Nonce 不是授权的替代品,公开的只读跳转本身也不需要 nonce。

目标要真实可见,商业关系要靠近链接披露

短路径不是隐身衣。链接文字和附近说明应让普通读者知道目的地。付费、赞助或联盟链接应在链接附近用清晰语言披露关系;不要只把说明藏在页脚或“条款”页面。美国读者可参考 FTC Endorsement Guides 问答。其他地区应核对适用规则。

Google 目前建议付费链接使用 rel="sponsored"nofollow 仍可接受,也可组合使用。这个属性不能替代面向人的披露。

<p><strong>Disclosure:</strong> I may receive a commission if you buy through this link.</p>
<p><a href="/go/example-docs" rel="sponsored nofollow">View Example's product documentation (paid link; opens Example)</a></p>

请把可见文字本地化,并保持事实准确;不要虚构点击量、客户数、转化率或收入。跳转次数也不等于真人、客户或成交。Google 的出站链接标记指南给出了当前的 sponsored/nofollow 用法。

点击日志:默认不收集

最小实现不记录点击。服务器访问日志仍可能包含路径、IP、User-Agent、来源页或查询字符串,应检查实际基础设施,而不是假设“插件关闭统计”就没有日志。若没有清楚、必要的用途,关闭这条路由的详细日志或尽早做不可逆聚合;默认不要保存完整 IP、User-Agent、来源 URL 或访客标识。

确需测量时,只保留满足目的所需的最小字段,例如短期的“日期 + 路由 + 聚合计数”,记录机器人和重复请求会使数字失真。写明用途、法律依据、保留期、接收方、访问/删除路径,并按适用法律评估同意或拒绝机制。涉及 Cookie、设备存储或链接装饰时,可参考 ICO 的存储与访问技术指南;若 WordPress 插件处理个人数据,还应实现其隐私建议、导出和删除机制

暂存测试与验收

先备份数据库、插件配置和服务器配置;只改变一个路由。在暂存域名替换示例主机后执行:

curl --silent --show-error --dump-header - --output /dev/null \
  https://staging.example.org/go/example-docs
curl --silent --show-error --dump-header - --output /dev/null \
  --request POST https://staging.example.org/go/example-docs
curl --silent --show-error --dump-header - --output /dev/null \
  'https://staging.example.org/go/example-docs?url=https://evil.example'
curl --silent --show-error --location --max-redirs 5 --output /dev/null \
  --write-out '%{http_code} %{url_effective}\n' \
  https://staging.example.org/go/example-docs

验收结果应当是:精确、无查询的 GET 仅跳一次到预期 HTTPS 目标;POST 与带 url 查询的请求不跳转;未知 slug 是 404;最终页面符合预期;无循环;响应没有意外携带访客查询参数。分别测试登录/未登录、移动端、缓存开启/关闭和 CDN 边缘节点。若暂存环境未安装这段代码,上述命令只是在测试当前站点,不能证明示例实现正确。

多语言站点要明确 /go/example-docs 是否跨语言共用。若存在 /en//ja/ 等前缀,请逐条添加精确映射,不要用通配符吞掉语言路径。永久链接或目标改变后,检查 WordPress 重写规则、插件优先级、页面 slug、反向代理及 CDN 是否造成冲突或跳转链。

回滚与停止条件

发布前保存规则导出、代码版本和原配置。先用 302 小范围上线。若出现错误目标、循环、缓存无法清除、未知路径被接管、请求参数被转发、日志超出披露范围,或目标域名易手/出现安全告警,应立即禁用该条规则、清理缓存并恢复直接链接。不要为了保住统计数据而延迟回滚。

上线前的最小清单:

  • 直接链接是否已经足够?
  • 来源是否为精确保留 slug,目标是否为固定 HTTPS URL 和精确主机?
  • 是否拒绝非 GET、任何查询目标、非 HTTP(S) 协议和未知 slug?
  • 是否检查 slug/语言前缀、缓存、CDN、循环和目标所有权?
  • 商业关系是否在链接附近清晰披露,链接文字是否可理解?
  • 是否默认不记录 IP/User-Agent,统计是否最小化并有删除期限?
  • 是否有备份、配置导出、审核人、测试证据和一键回滚路径?

参考资料

历史原文存档(仅供出处核对,不要照做)

以下是 2011 年源导出的完整可见正文,除有限脱敏外未改写。为避免重新传播旧跟踪或私人/账户式标识,已将 Taskcity 的联盟参数值(2 处)、旧站内跟踪路径(3 处)和 Filemarkets 的账户式下载路径(1 处)替换为方括号占位符。原始字符串仅保留在 source_export 和 Git 历史来源中。旧插件下载、推荐、访问者画像跟踪及商业说法均作为被否定的历史材料保留;链接可能失效、易手或不安全,不要访问或执行。

WordPress 怎样实现“站外链接”的跳转?
——WordPress短链接插件Pretty Link


**比如说我在一篇文章里添加了一个超链接,我想让这个超链接显示成本站的链接,(鼠标放上去变成小手,浏览器左下角显示的是本站的一个链接)但是点击后跳转成外链,这样如何实现?**

在[http://earnfs.sinaapp.com](http://earnfs.sinaapp.com/[historical redirect path redacted])站点中:

[http://www.taskcity.com/?dn=[historical referral value redacted]](http://www.taskcity.com/?dn=[historical referral value redacted])=>[http://earnfs.sinaapp.com/[historical redirect path redacted]](http://earnfs.sinaapp.com/[historical redirect path redacted])

**可以使用缩短连接插件**
**Pretty Link (推荐)**
**或**
**WordPress ShortUrl Plugin**

Petty Link可以生成基于你的域名的短网址,类似tinyurl.com, bit.ly等短网址服务的链接,并且这个链接基于你自己的域名,你可以对这些生成的网址链接进行点击跟踪,监测点户的来源地、浏览器、操作系统等,非常适合用来做推荐联盟链接等。

这款插件可以自定义URL,不过,前提是不能和已经存在的或者已经在使用的%postname%相同,使用Petty Link插件后,它会让你的网站变得更加的简洁、

**Petty Link插件安装使用:**

1. [下载Petty Link插件](http://filemarkets.com/file/[historical account/download path redacted]),解压后,把文件夹上传到wp-content/plugins/目录下,登录WordPress管理后台,点击“Plugins”找到上传的插件,激活该插件。
2.  激活该插件后,在后台页面的右侧最下方,会出现Pretty Link的图标,然后,在里面进行相关的设置。

**Pretty Link有以下选项:**
**Pretty Link:**页面中主要是当前添加的链接、点击量、组等相关信息
**Add New Link:**添加链接

![WordPress短链接插件Pretty Link](http://earn.yesmall.biz/wp-content/uploads/auto_save_image/2011/08/135337eEN.jpg)

**Groups:**创建组,让这些组使用这些pretty links
**Hits:**图示最近一段时间的点击量、流量IP、转向的链接,方便你的统计使用。
**Tools:**获取Pretty Link链接到工具栏安装书签,当你浏览网页,只要单击书签就可以创建Pretty Link
**Options:**设置相关信息

- **Link Option Defaults**:设置是否添加跟踪链接、nofollow及相关属性
- **PrettyBar Options**:设置Pretty Bar的是否显示标题、说明、分享链接及URL。
- **Reporting Options**:报告选项,不包含那些IP地址,在统计的时候对其进行排除。

**Pretty Link Pro**:设置PRO账户信息

这款插件的功能还是比较强的,这里这是简单的做了点介绍,更多更深的东西,还是需要大伙自己体会。

Leave a Reply