WordPressの安全な外部リンクとリダイレクト:固定・透明・復元可能

2011 年の旧記事は、古いプラグイン、ZIP の手動アップロード、アフィリエイトリンク、訪問者プロファイルを伴うクリック追跡を説明していました。これらをそのまま再現してはいけません。現在の安全な原則は、可能なら外部へ直接リンクし、サイト内の短いパスが必要な場合だけ、固定パスを固定 HTTPS 宛先へ対応させ、行き先と商業的関係の有無を読者に明示することです。

このガイドは 2026 年 9 月 1 日に更新したもので、トラフィック、顧客、収益を約束しません。出典確認のため、限定的に伏せ字化した歴史的原文を末尾に保存していますが、当時のサービス、ダウンロード、手順を推奨するものではありません。

まず、リダイレクトが本当に必要かを確認する

外部への直接リンクは通常、最も単純で透明であり、プラグイン、キャッシュ、ログ、障害点を減らせます。「ここをクリック」ではなく「Example の製品ドキュメントを見る」のように行き先が分かるリンク文言を使います。明確な理由がなければ新しいウィンドウを強制せず、必要なら事前に伝え、rel="noopener" を使います。

サイト内リダイレクトが妥当なのは、将来移動する可能性がある長期リソースの固定アドレス、過去の短縮リンクの修復、または関係を明示した固定商用リンクなど、限定され説明可能な用途だけです。宛先の偽装、プラットフォーム規則の回避、リスクの隠蔽、訪問者が渡す任意宛先の受け入れに使ってはいけません。

3 つの実装方法

方法適する場面利点主な責任
外部への直接リンク既定の選択透明、リダイレクトチェーンなし、保守が最小宛先と開示を定期確認
公式ディレクトリの保守中プラグイン多数のルートに UI、インポート、エクスポートが必要非開発者にも管理しやすい更新、最小権限、ログの無効化・縮小
Web サーバーの固定ルール安定した対応が少数で、サーバーを管理できるWordPress 非依存で処理経路が短い設定レビュー、テスト、公開、ロールバック
小さな WordPress 実装サイトコードとともに管理する少数の対応挙動を監査でき、安全な WordPress リダイレクトを使えるコード、衝突、キャッシュの保守

プラグインを選ぶ場合は、WordPress 管理画面につながる公式プラグインディレクトリから、使用中の WordPress/PHP に対応し現在も保守される版をインストールします。Redirection はこの更新日時点でリリースが続く掲載例ですが、安全保証や商業的推奨ではありません。旧記事の第三者 ZIP、失効したダウンロード先、不明なミラーは使いません。

ステータスコードは「SEO スイッチ」ではない

状態意味メソッドの扱いこの用途での指針
302一時的後続リクエストを GET に変える場合がある変更可能な固定・GET 専用マッピングの既定値
307一時的メソッドと本文を保持保持が必要な場合だけ。本文の端点は非 GET を拒否
301恒久的クライアントが POSTGET に変える場合がある本当に長期不変で十分テストした宛先だけ
308恒久的メソッドと本文を保持同じく本当に恒久的なルールだけ

意味は HTTP Semantics(RFC 9110)に基づきます。ブラウザー、検索エンジン、CDN は恒久リダイレクトをキャッシュし、一時的なものもキャッシュ方針の影響を受けます。新規ルールは 302 で始め、検証後にだけ恒久化を判断します。変更時にはアプリケーションと CDN のキャッシュも削除します。

方法 A:プラグインを固定的かつ最小限に使う

ステージングでインストール・更新し、各短縮パスに完全一致の元パス固定 HTTPS 宛先を設定します。正規表現、ワイルドカード、クエリーパラメーターから宛先を読む機能は有効にしません。?url=https://…?next=…javascript:data: など非 HTTP(S) スキームを許すルートは作りません。不明な短縮パスは訪問者入力へ転送せず 404 のままにします。

既存ルールを先にエクスポート・バックアップします。/go/ がページ、投稿、カテゴリー、メディア、言語接頭辞、別プラグインと衝突しないか確認します。管理権限は必要な担当者だけに限定します。クリックログは既定で無効にし、最小化・削除要件を満たせないプラグインなら統計機能を使いません。公開後も復元可能な設定エクスポートとロールバック手順を残します。

方法 B:Web サーバーの固定ルールを 1 つ使う

次の 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 実装

次のコードは、レビューとステージング後にサイト所有の小さなプラグインとして使えます。管理 UI はなく、GET だけを受け入れ、短縮パス上の全クエリー文字列を拒否し、コード内の対応表だけから宛先を得て、HTTPS と完全一致ホスト名を要求します。一時的にその外部ホストだけを許可し、`wp_safe_redirect()`を呼んだ後、フィルターを外して終了します。

<?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 の Q&Aを参照し、他地域では該当規則を確認します。

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 プラグインは、同プラットフォームのプライバシー通知、エクスポーター、消去機能も実装すべきです。

ステージングでのテストと受け入れ条件

先にデータベース、プラグイン設定、サーバー設定をバックアップし、1 ルートだけ変更します。例のホストをステージングドメインに置き換えて実行します。

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 宛先へ 1 回だけ転送されること、POSTurl クエリーが転送されないこと、不明 slug が 404 になること、最終ページが期待どおりであること、ループがないこと、訪問者のクエリーが伝播しないことです。ログイン中/外、モバイル、キャッシュ有効/無効、CDN エッジを別々にテストします。コードがステージングに未導入なら、このコマンドは現サイトの挙動だけを試し、サンプル実装を証明しません。

多言語サイトでは /go/example-docs を各言語で共有するか決めます。/en//ja/ などの接頭辞があるなら、言語パスを飲み込むワイルドカードではなく、完全一致対応を個別に列挙します。パーマリンクや宛先の変更後は、WordPress リライトルール、プラグイン優先順位、ページ slug、リバースプロキシ、CDN の衝突とリダイレクトチェーンを調べます。

ロールバックと停止条件

公開前にルールのエクスポート、コード版、旧設定を保存し、302 で限定的に始めます。誤った宛先、ループ、消えないキャッシュ、不明パスの捕捉、リクエストパラメーターの転送、開示範囲外のログ、宛先ドメインの移転、セキュリティ警告があれば、直ちにそのルールを無効化し、キャッシュを削除して直接リンクへ戻します。統計を守るためにロールバックを遅らせません。

公開前の最小チェックリスト:

  • 直接リンクで十分ではないか。
  • 元は予約済みの完全一致 slug、宛先は固定 HTTPS URL と完全一致ホストか。
  • GET、クエリー指定の宛先、非 HTTP(S) スキーム、不明 slug を拒否するか。
  • slug/言語接頭辞、キャッシュ/CDN、ループ、宛先所有者を確認したか。
  • 商業的関係をリンク近くで明示し、リンク文言を理解できるか。
  • IP/User-Agent ログは既定で無効で、分析は最小化され削除期限があるか。
  • バックアップ、設定エクスポート、レビュー担当、テスト証拠、1 手順のロールバックがあるか。

参考資料

歴史的原文アーカイブ(出典確認専用・実行禁止)

以下は 2011 年の source export にある可視本文全体で、限定的な伏せ字以外は改変していません。古い追跡情報や個人・アカウント状の識別子を再公開しないため、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