【WordPress】外部リンクだけに target=”_blank” と rel 属性を自動追加する方法

【WordPress】外部リンクだけに target="_blank" と rel 属性を自動追加する方法 WordPress

WordPressでは、投稿本文やウィジェットに含まれるリンクをそのまま出力すると、外部リンクも同一ウィンドウで開いてしまうため、ユーザーがサイトから離脱する原因になりがちです。

そこでこの記事では、投稿本文内の外部リンクにのみ target=”_blank” と rel=”noopener noreferrer” を自動的に付与する方法をご紹介します。

スポンサーリンク

なぜ rel=”noopener noreferrer” が必要なのか?

  • target=”_blank” を使うと、新しいタブでリンクが開きますが、セキュリティ上の理由から rel=”noopener noreferrer” の追加が推奨されます。
  • 特に noopener は、新しいタブで開いたページが元のページのウィンドウオブジェクトにアクセスするのを防止します。

the_content フィルターを使って外部リンクを加工

以下のコードを functions.php に追加します。

NG:ドメインの部分一致で誤判定することがある
function add_target_blank_to_external_links($content) {
  // サイトのドメインを取得(www あり・なしの両方に対応)
  $site_url = parse_url(home_url(), PHP_URL_HOST);

  // 正規表現でaタグを検出
  return preg_replace_callback(
    '/<a\s[^>]*href=["\'](.*?)["\'][^>]*>/i',
    function ($matches) use ($site_url) {
      $url = $matches[1];

      // 外部リンクか判定(内部ドメインを含まない)
      if (strpos($url, $site_url) === false && strpos($url, 'http') === 0) {
        $tag = $matches[0];
        if (strpos($tag, 'target=') === false) {
          $tag = str_replace('<a', '<a target="_blank"', $tag);
        }
        if (strpos($tag, 'rel=') === false) {
          $tag = str_replace('<a', '<a rel="noopener noreferrer"', $tag);
        }
        return $tag;
      }

      return $matches[0];
    },
    $content
  );
}
add_filter('the_content', 'add_target_blank_to_external_links');
// site_url が notcodingls.com のような別ドメインの部分文字列にも一致してしまい、
// 本来は外部リンクなのに内部リンクと誤判定される
strposによる部分文字列検索は別ドメインを誤って内部リンク扱いする
strpos($url, $site_url) === falseは、URL文字列の中に自サイトのドメイン名が部分文字列として含まれているかを見ているだけです。そのためnotcodingls.comcodingls.com.example-tracker.netのような、たまたまドメイン名を含む別のドメインまで「内部リンク」と誤判定され、target="_blank"rel="noopener noreferrer"が付与されません。parse_url()でリンク先のホスト名を正確に取り出し、サイトのホスト名と厳密に比較する必要があります。
OK:parse_url()でホスト名を正確に比較
function add_target_blank_to_external_links($content) {
  $site_host = normalize_host_for_link_check(parse_url(home_url(), PHP_URL_HOST));

  // 正規表現でaタグを検出
  return preg_replace_callback(
    '/<a\s[^>]*href=["\'](.*?)["\'][^>]*>/i',
    function ($matches) use ($site_host) {
      $url = $matches[1];
      $url_host = normalize_host_for_link_check(parse_url($url, PHP_URL_HOST));

      // 相対URL(ホストなし)や自サイトと同じホストは内部リンクとして扱う
      if ($url_host === '' || $url_host === $site_host) {
        return $matches[0];
      }

      $tag = $matches[0];
      if (strpos($tag, 'target=') === false) {
        $tag = str_replace('<a', '<a target="_blank"', $tag);
      }
      if (strpos($tag, 'rel=') === false) {
        $tag = str_replace('<a', '<a rel="noopener noreferrer"', $tag);
      }
      return $tag;
    },
    $content
  );
}
add_filter('the_content', 'add_target_blank_to_external_links');

// ホスト名の先頭の www. を取り除いて比較しやすくする
function normalize_host_for_link_check($host) {
  return preg_replace('/^www\./i', '', (string) $host);
}

処理内容のポイント

  • 正規表現で <a href=””> タグをすべて検出
  • parse_url() でリンク先と自サイトのホスト名をそれぞれ取り出し、厳密に比較して内部・外部を判定
  • 先頭の www. を取り除いてから比較することで、www有無の差を吸収
  • 外部リンクにだけ target=”_blank” と rel=”noopener noreferrer” を追加
  • すでに target や rel がある場合は追加しないことで二重付与を防止

注意点

  • HTMLが不正な場合や特殊な形式のリンク(JavaScriptリンクなど)には対応しません。
  • 他のフィルター(プラグインやテーマ)が the_content を加工している場合、競合する可能性があります。
  • 外部リンクの中に特定のドメイン(例:パートナーサイト)は除外したい、という要望がある場合は条件をさらに追加可能です。

よくある質問(FAQ)

QWordPressで外部リンクに自動的にtarget=”_blank”を付けるには?
Athe_contentフィルターでpreg_replace_callback()を使い、リンク先のホスト名が自サイトのホスト名と異なる場合にtarget=”_blank” rel=”noopener noreferrer”を追加します。または管理画面のエディターで挿入時に設定を自動化するJSを追加する方法もあります。
Qrel=”noopener noreferrer”が必要な理由は何ですか?
Atarget=”_blank”だけでは開かれたタブから元のページをwindow.openerで操作できるセキュリティリスクがあります。rel=”noopener”でこの問題を解決し、rel=”noreferrer”で参照元情報の送信も防止します。
Q内部リンクには適用せず外部リンクのみに適用する判定方法は?
Aparse_url($href, PHP_URL_HOST)でリンク先のホスト名を取り出し、parse_url(home_url(), PHP_URL_HOST)で取得した自サイトのホスト名と厳密に比較します。strpos()での部分一致judgment(例: strpos($href, site_url()) === false)は、サイトのドメイン名を含む別ドメインを誤って内部リンクと判定してしまうため避けてください。

まとめ

外部リンクに target=”_blank” と rel=”noopener noreferrer” を自動付与することで、ユーザーの離脱防止とセキュリティ向上の両立が実現できます。わざわざ手動で書き換えたり、エディタで意識しなくてもよくなるため、運用の効率化にも大きく貢献します。ドメインの判定は部分一致ではなく、parse_url()で正確にホスト名を比較することを心がけましょう。