ブログの構築システムとして、またはサイトのCMSとしても多大なシェアを誇るWordpressでは、記事投稿時にURLを貼り付けるだけで記事本文中へリンクボックスなどを設置できる「埋め込みという機能があります。
この埋め込みを使って設置されたリンクボックスは、URLの提供元が用意したリッチな見た目のものが掲載されることになります。

記事投稿の本文入力欄でペーストすると、専用のリンクボックスに置換される
「埋め込み」に対応したURLだと、貼り付けるだけでブログカード的なものを設置できます。

この埋め込みでリンクボックスを設置するには、上図で挙げたTwitterのように埋め込もうとしたURLが対応している必要があります。
Wordpressの「埋め込み」にサイトを対応させたいのであれば、サイトがそもそも(古くない)WordPressで構築されていれば、デフォルトで記事ページなどの埋め込み対応は有効化されています。また、テーマのカスタマイズなどで無効化している場合でも、テーマへ手を入れることで比較的容易に「Wordpressが求める仕様」での実装が可能です。(そのため、本記事では割愛しています。……が、実際にどの程度のカスタマイズがされているかによって再実装の難易度は変わってきますので、エンジニアの方に確認してみてください)

では逆に、サイトがWordpressで構築されていない場合は、どういう要件でシステムを実装すれば「埋め込み」に対応できるのか? 今回はこの「非WordpressサイトでWordpressの『埋め込み』に対応するための要件」をまとめてみましたので、対応を検討されている方は本記事を参考に、エンジニアの方に相談してみてください。
なお身も蓋もない結論を最初に書いておくと「WordPressの埋め込み用データを流用して作る」のが最も確実でオススメです。

  1. WordPressの「埋め込み」はoEmbed規格を使っている
  2. サイトを「埋め込み」に対応させるメリットとデメリット
    1. メリット1:「埋め込み」で設置されたリンクボックスはreferralなリンクになる
    2. メリット2:こちらが意図した内容のリンクボックスを提供できる
    3. デメリット:どの程度「埋め込み」を使ってもらえるかがわからない
  3. WordPressの「自動探知」が感知するための条件
    1. シェアされるページ側に必要なもの
    2. 埋め込み用データ(JSONまたはXML)
    3. 埋め込み用データ内から呼び出されるiframeの埋め込み専用ページ
  4. 細かい仕様の注意点

WordPressの「埋め込み」はoEmbed規格を使っている

WordPressの埋め込みはoEmbedという統一規格を基本として利用しています。
oEmbedというのは「埋め込み(シェア)」のための規格で、詳細な情報などについてはoEmbedの公式サイト(英語)をご参照いただければと思いますが、ざっくりとした概要は以下のようになっています。

  • そのページを埋め込もうとした際に必要となる「埋め込み用の諸々のデータ」を返すURLを設計
    • URLは「埋め込みたいURL」を示すURLスキーマと、URLスキーマを渡せば必要なデータを返すAPIエンドポイントで構成されます。
      例)http://flickr.com/services/oembed?url=http%3A//flickr.com/photos/bees/2362225867/
      「url=」の後がURLスキーマ(埋め込みたいページURL)で、その前がAPIエンドポイントです。URLスキーマはURLエンコードされている必要があります。
  • 「埋め込み用の諸々のデータ」をJSONまたはXML形式で用意
  • oEmbed公式のGitHubへ対応URLパターンなどをアップロードして申請

WordPressは上記に独自の調整を行っており、実はGitHubに上がっているすべてのプロバイダの埋め込みには対応しておらず、一部のプロバイダのみを対応範囲としています。

ただWordpressの4.4以降では「Wordpressで作成されたブログ記事同士のシェア」をしやすくするため、公式の対応範囲とは別に「埋め込もうとしているURLが『埋め込み』に対応しているかどうかを自動探知」して埋め込みを行ってくれる機能が実装されています。
今回の記事で解説するのは、この自動探知の条件を満たすための要件となります。

サイトを「埋め込み」に対応させるメリットとデメリット

メリット
  • 「埋め込み」で設置されたリンクボックスはreferralなリンクになる
  • こちらが意図した見た目のリンクボックスを提供できる
デメリット
  • どの程度「埋め込み」を使ってもらえるかがわからない

メリット1:「埋め込み」で設置されたリンクボックスはreferralなリンクになる

WordPressで記事を投稿するユーザーがURLを記事内でシェアしようとした場合、通常はユーザーが手動でリンクを設置する必要があります。
しかしこの際、Wordpressでは「別タブを開くtarget="_blank"」でリンクを設置すると、デフォルトではrel="noreferrer noopener"が付与されてしまう、というアクセス解析視点では非常に厄介な仕様があります。これはセキュリティ向上のためのもので、遷移先のページから元ページに対するJSを使った操作などを行えなくするための設定です。

rel="noreferrer"
遷移先でリファラーdocument.referrer)(=遷移元ページのURL)が参照できなくなる。
rel="noopener"
遷移先で遷移元ページのタブをJSで操作できなくなる。

正直noopenerはそうそう利用されていることはありませんので特に実害はありませんが、ここで問題になるのがrel="noreferrer"の存在です。
GAの参照元などは、通常はリファラーが参照されます。しかしrel="noreferrer"がついたリンクからの流入だとリファラーが消えてしまい、direct/noneになってしまいます。いわゆる昨今話題のダークトラフィックですね。

しかし「埋め込み」で設置されたリンクボックスではリファラーは消えませんので、referralなリンクとなります。
つまり「埋め込み」に対応することで、ダークトラフィックになる通常テキストリンクという手段のほかに、referralになる「埋め込み」という第2の選択肢ができるわけです。

メリット2:こちらが意図した内容のリンクボックスを提供できる

WordPressの自動探知による「埋め込み」では、基本的にiframeを用いて「埋め込み専用ページ」を表示させる形となります。そのため、この「埋め込み専用ページ」のレイアウトをそのまま見せることができるわけです。
例えば下記はWordpressがデフォルトで生成する埋め込み専用ページを埋め込んだ場合の例です。Wordpressの場合は記事URLの末尾に「/embed」を付けると各記事の埋め込み専用ページを見ることができます。

本記事の埋め込み専用ページ
例えば本記事でも、WordPress製なので埋め込み専用ページが自動生成されてます。

この埋め込み専用ページで、より魅力的な見せ方ができれば、流入を増やすことができるかもしれません。

なおド派手にしたりなどあまり奇抜な「見た目で勝負」な方向でがんばってしまうと、シェアするユーザーが「自分のブログ記事ページの見た目から浮いてしまうな……普通にリンクテキストで置くことにしよう」みたいに逆に避けられる可能性もあります。
それよりは、見た目は無難なものであっても、情報として有益なものを掲載するのが良いと思います。

例えば製品詳細ページを「埋め込み」に対応させるのであれば、製品名や写真などだけでなく、製品スペックなども含まれていた方が見る側によってより有益であると言えます。
さらには「参考にされた」レビューの上位1~2件の冒頭〇文字を含めたりする、なども良いかもですね。

デメリット:どの程度「埋め込み」を使ってもらえるかがわからない

さて、ではWordpressの「埋め込み」に対応した場合のデメリットはというと……ずばり、「埋め込み」でリンクをシェアしてくれるかがわからない、という点です。
せっかく工数をかけてシステム実装をしたとしても、こればかりは不明なため、正直なところ何とも言えません。

使われない理由としては

  • (自動探知による埋め込みは)強制的に同タブリンクになる(別タブリンクにできない)
  • そもそも「埋め込み」に対応していると気付かれない

などが考えられます。

前者についてはユーザーへ対して有益な埋め込み用データを提供することで、後者についてはサイト側での告知やシェアボタンなどへの宣伝を追加することで、多少の改善はできるかもしれません。

WordPressの「自動探知」が感知するための条件

自動探知による「埋め込み」に必要なものは、以下の3要素です。
もともとの「ページ側の改修」が1件、それと「埋め込み用データ」とその中のiframeで読み込まれて実際に埋め込まれた際にユーザーへ表示される「埋め込み専用ページ」の新規開発が2件です。

必要なもの
  • シェアされるページの改修
    • <head>タグ内へ<link>タグで「埋め込み用データ」への導線設置が必要
  • 埋め込み用データの実装
    • JSONまたはXMLまたは両方
  • 実際にiframeで表示される埋め込み専用ページの実装

シェアされるページ側に必要なもの

ユーザーがシェアしたい(埋め込みたい)URLのページでは、<head>タグ内へ<link>タグの設置が必要となります。
具体的には下記のようなものです。

headタグ内へlinkタグで「埋め込み用データ(JSONまたはXML)」へのリンクを設置
alternateのリンクタグで「埋め込み用データ」のURLを記述します。
WordPressのものの例
<link rel="alternate" type="application/json+oembed" href="https://(自ドメイン)/wp-json/oembed/1.0/embed?url=https%3A%2F%2F(自ドメイン)%2F(シェアしたいURLパス)" />
<link rel="alternate" type="text/xml+oembed" href="https://(自ドメイン)/wp-json/oembed/1.0/embed?url=https%3A%2F%2F(自ドメイン)%2F(シェアしたいURLパス)&format=xml" />

このrel="alternate"type="application/json+oembed"またはtype="text/xml+oembed"<link>タグで、後述の「埋め込み用データ」へのリンクを設置する形です。
この<link>タグを置くことで、Wordpressがこのページを見たときに「このURLを埋め込むにはこのhrefのURLを参照すればいいんだな」ということを伝えることができるわけです。

なお上記の「埋め込み用データ」はoEmbedの仕様に準じた「APIエンドポイント+URLスキーマ」で構成されています。一応、Wordpressの自動探知による「埋め込み」は厳密にはoEmbedそのものではないため、実は静的なURLなどでも感知できます。
ただ、複数ページを対応させようとした場合は動的な出力になるでしょうし、それであればoEmbedの仕様に準じて「APIエンドポイント+URLスキーマ」の形式にしておくのがオススメです。

その他、以下がポイントとなります。

  • JSONだけでも特に問題なく動作する
  • URLスキーマはURLエンコードが必要
  • (テストの際は)同じURLでテストし続けるのは更新が反映されない可能性あり

1つ目2つ目はそのままですが、実装しようとした際に注意が必要なのが3つ目です。
開発時には動作確認用のWordpressを用意して「埋め込み」が反応するかなどのテストをしながら行うと思いますが、ページのURL、および埋め込み用データのURLはWordpress側でキャッシュされている気配があります。そのため、同じURLでテストし続ける場合、テスト用記事は都度削除→新規作成などを繰り返して確認することを推奨します。または、更新するたびにURLを変えるとキャッシュされていたとしても回避できるので、確実です。

埋め込み用データ(JSONまたはXML)

シェアされるページ側から<link>タグで指定されたURLへアクセスした際に、JSON(またはXML)で埋め込み用データを返すシステムが必要です。
実際に返すデータの内容そのものは同じで良いため、JSONとXMLは書式(とエンコード)が違うだけ、という違いになります。

埋め込み時に必要となるJSONデータは下記のような形式となります。
(それぞれはWordpressで出力されたものです)

JSONのサンプル
{
	"version":"1.0",
	"provider_name":"\u30a2\u30e6\u30c0\u30f3\u30c6\u682a\u5f0f\u4f1a\u793e",
	"provider_url":"https:\/\/ayudante.jp",
	"author_name":"\u30a2\u30e6\u30c0\u30f3\u30c6 \u682a\u5f0f\u4f1a\u793e",
	"author_url":"https:\/\/ayudante.jp\/author\/daisaku",
	"title":"GTM\u306e\u30b3\u30df\u30e5\u30cb\u30c6\u30a3\u5909\u6570\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u300cParent Element Variable\u300d\u306b\u3064\u3044\u3066\u306e\u89e3\u8aac",
	"type":"rich",
	"width":600,
	"height":338,
	"html":"<blockquote class=\"wp-embedded-content\"><a href=\"https:\/\/ayudante.jp\/column\/2019-10-30\/11-00\/\">GTM\u306e\u30b3\u30df\u30e5\u30cb\u30c6\u30a3\u5909\u6570\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u300cParent Element Variable\u300d\u306b\u3064\u3044\u3066\u306e\u89e3\u8aac<\/a><\/blockquote>\n<script type='text\/javascript'>\n<!--\/\/--><![CDATA[\/\/><!--\n\t\t!function(d,l){\"use strict\";var e=!1,o=!1;if(l.querySelector)if(d.addEventListener)e=!0;if(d.wp=d.wp||{},!d.wp.receiveEmbedMessage)if(d.wp.receiveEmbedMessage=function(e){var t=e.data;if(t)if(t.secret||t.message||t.value)if(!\/[^a-zA-Z0-9]\/.test(t.secret)){var r,a,i,s,n,o=l.querySelectorAll('iframe[data-secret=\"'+t.secret+'\"]'),c=l.querySelectorAll('blockquote[data-secret=\"'+t.secret+'\"]');for(r=0;r<c.length;r++)c[r].style.display=\"none\";for(r=0;r<o.length;r++)if(a=o[r],e.source===a.contentWindow){if(a.removeAttribute(\"style\"),\"height\"===t.message){if(1e3<(i=parseInt(t.value,10)))i=1e3;else if(~~i<200)i=200;a.height=i}if(\"link\"===t.message)if(s=l.createElement(\"a\"),n=l.createElement(\"a\"),s.href=a.getAttribute(\"src\"),n.href=t.value,n.host===s.host)if(l.activeElement===a)d.top.location.href=t.value}}},e)d.addEventListener(\"message\",d.wp.receiveEmbedMessage,!1),l.addEventListener(\"DOMContentLoaded\",t,!1),d.addEventListener(\"load\",t,!1);function t(){if(!o){o=!0;var e,t,r,a,i=-1!==navigator.appVersion.indexOf(\"MSIE 10\"),s=!!navigator.userAgent.match(\/Trident.*rv:11\\.\/),n=l.querySelectorAll(\"iframe.wp-embedded-content\");for(t=0;t<n.length;t++){if(!(r=n[t]).getAttribute(\"data-secret\"))a=Math.random().toString(36).substr(2,10),r.src+=\"#?secret=\"+a,r.setAttribute(\"data-secret\",a);if(i||s)(e=r.cloneNode(!0)).removeAttribute(\"security\"),r.parentNode.replaceChild(e,r)}}}}(window,document);\n\/\/--><!]]>\n<\/script><iframe sandbox=\"allow-scripts\" security=\"restricted\" src=\"https:\/\/ayudante.jp\/column\/2019-10-30\/11-00\/embed\/\" width=\"600\" height=\"338\" title=\"“GTM\u306e\u30b3\u30df\u30e5\u30cb\u30c6\u30a3\u5909\u6570\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u300cParent Element Variable\u300d\u306b\u3064\u3044\u3066\u306e\u89e3\u8aac”\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\" class=\"wp-embedded-content\"><\/iframe>",
	"thumbnail_url":"https:\/\/ayudante.jp\/wp-content\/uploads\/2019\/10\/pic_191030_01_thum.png",
	"thumbnail_width":600,
	"thumbnail_height":400
}
XMLのサンプル
<?xml version="1.0"?>
<oembed>
	<version>1.0</version>
	<provider_name>G2DCC</provider_name>
	<provider_url>https://ayudante.jp</provider_url>
	<author_name>アユダンテ株式会社</author_name>
	<author_url>https://ayudante.jp/author/daisaku</author_url>
	<title>GTMのコミュニティ変数テンプレート「Parent Element Variable」についての解説</title>
	<type>rich</type>
	<width>600</width>
	<height>338</height>
	<html><blockquote class="wp-embedded-content"><a href="https://ayudante.jp/column/2019-10-30/11-00/">GTMのコミュニティ変数テンプレート「Parent Element Variable」についての解説</a></blockquote>
	<script type='text/javascript'>
	<!--//--><![CDATA[//><!--
			!function(d,l){"use strict";var e=!1,o=!1;if(l.querySelector)if(d.addEventListener)e=!0;if(d.wp=d.wp||{},!d.wp.receiveEmbedMessage)if(d.wp.receiveEmbedMessage=function(e){var t=e.data;if(t)if(t.secret||t.message||t.value)if(!/[^a-zA-Z0-9]/.test(t.secret)){var r,a,i,s,n,o=l.querySelectorAll('iframe[data-secret="'+t.secret+'"]'),c=l.querySelectorAll('blockquote[data-secret="'+t.secret+'"]');for(r=0;r<c.length;r++)c[r].style.display="none";for(r=0;r<o.length;r++)if(a=o[r],e.source===a.contentWindow){if(a.removeAttribute("style"),"height"===t.message){if(1e3<(i=parseInt(t.value,10)))i=1e3;else if(~~i<200)i=200;a.height=i}if("link"===t.message)if(s=l.createElement("a"),n=l.createElement("a"),s.href=a.getAttribute("src"),n.href=t.value,n.host===s.host)if(l.activeElement===a)d.top.location.href=t.value}}},e)d.addEventListener("message",d.wp.receiveEmbedMessage,!1),l.addEventListener("DOMContentLoaded",t,!1),d.addEventListener("load",t,!1);function t(){if(!o){o=!0;var e,t,r,a,i=-1!==navigator.appVersion.indexOf("MSIE 10"),s=!!navigator.userAgent.match(/Trident.*rv:11\./),n=l.querySelectorAll("iframe.wp-embedded-content");for(t=0;t<n.length;t++){if(!(r=n[t]).getAttribute("data-secret"))a=Math.random().toString(36).substr(2,10),r.src+="#?secret="+a,r.setAttribute("data-secret",a);if(i||s)(e=r.cloneNode(!0)).removeAttribute("security"),r.parentNode.replaceChild(e,r)}}}}(window,document);
	//--><!]]>
	</script><iframe sandbox="allow-scripts" security="restricted" src="https://ayudante.jp/column/2019-10-30/11-00/embed/" width="600" height="338" title="“GTMのコミュニティ変数テンプレート「Parent Element Variable」についての解説”" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" class="wp-embedded-content"></iframe></html>
	<thumbnail_url>https://ayudante.jp/wp-content/uploads/2019/10/pic_191030_01_thum.png</thumbnail_url>
	<thumbnail_width>600</thumbnail_width>
	<thumbnail_height>400</thumbnail_height>
</oembed>

※上記はあくまで2020年3月時点でのWordpress最新版のサンプルです。
念のため、実際に開発時に参考にされる際は、別途動作確認用に用意したWordpressで生成されるものを参照ください。

JSONとXML、各パラメータは共通となっています。
また各パラメータの値はエンコードしておく必要があります。

version
値は「1.0」で固定。必須
provider_name
サイト名など。任意。
provider_url
サイトURL。任意。
author_name
投稿者名。任意。
author_url
投稿者ページURL。任意。
title
ページタイトル。任意。
type
値は「rich」推奨。必須
width
「埋め込み」で設置されるリンクボックスの幅。必須
height
「埋め込み」で設置されるリンクボックスの高さ。必須
ただし実際に設置した際は、設置後にhtml内のiframe内のJSにより高さ調整が可能。
html
「埋め込み」時にページ側へ設置されるHTML内容。必須
ただし、Wordpressの自動探知では中身に必ず「<blockquote>~~</blockquote>」と「<iframe>~~</iframe>」が含まれている必要あり。
また<blockquote>タグ、<a>タグ、<iframe>タグ以外は使用が制限されている。
thumbnail_url
サムネイル画像のURL。任意。
thumbnail_width
サムネイル画像の幅。任意。
thumbnail_height
サムネイル画像の高さ。任意。

最低限必須なもののみ、だとversion1.0固定)typerich固定)widthheighthtmlの5つのみとなります。

ポイントとなるのが、実際に「埋め込み」時にHTMLとして使用されるhtmlプロパティの値です。
Wordpressの自動探知による「埋め込み」では、このhtmlプロパティの中身は

  • <blockquote>~~</blockquote>~~<iframe>~~</iframe>という構成になっていなければならない
  • <blockquote>タグ、<a>タグ、<iframe>タグ以外は使用できない
  • <blockquote>タグと<iframe>タグ以外は実際の「埋め込み」時に除去される
  • <blockquote>タグと<iframe>タグの属性などは実際の「埋め込み」時に置換される
    • <a>タグにtarget属性を指定していても除去される
    • class名なども置換される

というルールがあります。
実際に埋め込まれて表示される際はユーザーへは<iframe>が表示されて、(JSによる)セキュリティチェックに失敗した場合は<iframe>の代替として<blockquote>タグ(+子要素として<a>タグ)が表示される、という仕様になっています。

さて、前述のWordpressが吐き出すhtmlプロパティの値には<blockquote>タグと<iframe>タグの間にはJS<script>タグ)が入っています。
これはiframeで読み込まれた埋め込み専用ページへ対して、以下のような処理を行っています。

WordPressのhtmlプロパティ内に含まれている<script>タグ
<script type='text\/javascript'>\n<!--\/\/--><![CDATA[\/\/><!--\n\t\t!function(d,l){\"use strict\";var e=!1,o=!1;if(l.querySelector)if(d.addEventListener)e=!0;if(d.wp=d.wp||{},!d.wp.receiveEmbedMessage)if(d.wp.receiveEmbedMessage=function(e){var t=e.data;if(t)if(t.secret||t.message||t.value)if(!\/[^a-zA-Z0-9]\/.test(t.secret)){var r,a,i,s,n,o=l.querySelectorAll('iframe[data-secret=\"'+t.secret+'\"]'),c=l.querySelectorAll('blockquote[data-secret=\"'+t.secret+'\"]');for(r=0;r<c.length;r++)c[r].style.display=\"none\";for(r=0;r<o.length;r++)if(a=o[r],e.source===a.contentWindow){if(a.removeAttribute(\"style\"),\"height\"===t.message){if(1e3<(i=parseInt(t.value,10)))i=1e3;else if(~~i<200)i=200;a.height=i}if(\"link\"===t.message)if(s=l.createElement(\"a\"),n=l.createElement(\"a\"),s.href=a.getAttribute(\"src\"),n.href=t.value,n.host===s.host)if(l.activeElement===a)d.top.location.href=t.value}}},e)d.addEventListener(\"message\",d.wp.receiveEmbedMessage,!1),l.addEventListener(\"DOMContentLoaded\",t,!1),d.addEventListener(\"load\",t,!1);function t(){if(!o){o=!0;var e,t,r,a,i=-1!==navigator.appVersion.indexOf(\"MSIE 10\"),s=!!navigator.userAgent.match(\/Trident.*rv:11\\.\/),n=l.querySelectorAll(\"iframe.wp-embedded-content\");for(t=0;t<n.length;t++){if(!(r=n[t]).getAttribute(\"data-secret\"))a=Math.random().toString(36).substr(2,10),r.src+=\"#?secret=\"+a,r.setAttribute(\"data-secret\",a);if(i||s)(e=r.cloneNode(!0)).removeAttribute(\"security\"),r.parentNode.replaceChild(e,r)}}}}(window,document);\n\/\/--><!]]>\n<\/script>
  • iframeを表示させて良いかのセキュリティチェック
  • iframeのheight調整の受信側処理
  • iframe内リンクがクリックされた際のページ遷移処理

……が、実はWordpressによる「埋め込み」時に(あったら)削除され、全く同じものが再び挿入される、という処理がなされます。
つまりあってもなくても(Wordpressによって勝手に補完されるので)「Wordpressの自動探知による埋め込み」という点では必須ではありません。

ただし「Wordpress以外で」利用される可能性があるのであれば、この<script>タグも含めておかなければなりません。なぜならiframeはセキュリティ上、sandbox(そのままでは親フレームへの操作ができない)で設置される可能性が非常に高いためです。
その場合、埋め込みを行った外側のページ側で「中でリンククリックされた際にページ遷移させる処理」を組み込んでおかなければ、リンクボックスがクリックされても動作しなくなってしまいます。

まとめると、htmlプロパティの中身は以下のようにするのがオススメです。
※実際はエンコードして出力するようにしてください。
<script>タグの箇所は前述のサンプルよりも、開発テスト時に用意されたWordpressが生成したものを流用ください。

<blockquote><a href="(シェアされるページのURL)">(シェアされるページのページタイトル)</a></blockquote>
(Wordpressで出力されている<script>タグをそのままコピー&ペースト)
<iframe sandbox="allow-scripts" security="restricted" src="(シェアされるページの埋め込み専用ページURL)" width="600" height="(埋め込み専用ページの推奨height)" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" class="wp-embedded-content"></iframe>

前述のとおり動作に必須となる<script>タグは強制的にWordpress準拠のものに置換されてしまうため、それであれば下手に独自のものとするよりも「最初からWordpressが吐き出すのと同じものを入れておく」のが結局のところシンプルです。
また、後述しますが<iframe>タグで呼び出される「埋め込み専用ページ」内にも、それを前提とした対応する<script>タグが必要となります。

埋め込み用データ内から呼び出されるiframeの埋め込み専用ページ

iframeで表示される埋め込み専用ページの要件は、比較的シンプルです。
埋め込み用のレイアウトなどになっていることが大前提ですが、追加の要件としては下記の<script>タグを</body>タグ前などへ設置するのみです。

埋め込み専用ページに必要な要件
  • レスポンシブ対応していること
    • 埋め込んだページ側のCSSなどでiframeの大きさは変更される可能性があるため
  • リンクボックスとしての利用を想定したデザインになっていること
  • 【必須】</body>タグ前へ下記<script>タグが設置されていること
埋め込み専用ページの</body>タグ前へ設置が必要な<script>タグ
<script>タグの箇所は前述のサンプルよりも、開発テスト時に用意されたWordpressが生成したものを流用ください。
<script type="text/javascript">
	!function(u,c){"use strict";var r,t,e,i=c.querySelector&&u.addEventListener,b=!1;function f(e,t){u.parent.postMessage({message:e,value:t,secret:r},"*")}function n(){if(!b){b=!0;var e,t=c.querySelector(".wp-embed-share-dialog"),r=c.querySelector(".wp-embed-share-dialog-open"),i=c.querySelector(".wp-embed-share-dialog-close"),n=c.querySelectorAll(".wp-embed-share-input"),a=c.querySelectorAll(".wp-embed-share-tab-button button"),o=c.querySelector(".wp-embed-featured-image img");if(n)for(e=0;e<n.length;e++)n[e].addEventListener("click",function(e){e.target.select()});if(r&&r.addEventListener("click",function(){t.className=t.className.replace("hidden",""),c.querySelector('.wp-embed-share-tab-button [aria-selected="true"]').focus()}),i&&i.addEventListener("click",function(){l()}),a)for(e=0;e<a.length;e++)a[e].addEventListener("click",s),a[e].addEventListener("keydown",d);c.addEventListener("keydown",function(e){27===e.keyCode&&-1===t.className.indexOf("hidden")?l():9===e.keyCode&&function(e){var t=c.querySelector('.wp-embed-share-tab-button [aria-selected="true"]');i!==e.target||e.shiftKey?t===e.target&&e.shiftKey&&(i.focus(),e.preventDefault()):(t.focus(),e.preventDefault())}(e)},!1),u.self!==u.top&&(f("height",Math.ceil(c.body.getBoundingClientRect().height)),o&&o.addEventListener("load",function(){f("height",Math.ceil(c.body.getBoundingClientRect().height))}),c.addEventListener("click",function(e){var t,r=e.target;(t=r.hasAttribute("href")?r.getAttribute("href"):r.parentElement.getAttribute("href"))&&(f("link",t),e.preventDefault())}))}function l(){t.className+=" hidden",c.querySelector(".wp-embed-share-dialog-open").focus()}function s(e){var t=c.querySelector('.wp-embed-share-tab-button [aria-selected="true"]');t.setAttribute("aria-selected","false"),c.querySelector("#"+t.getAttribute("aria-controls")).setAttribute("aria-hidden","true"),e.target.setAttribute("aria-selected","true"),c.querySelector("#"+e.target.getAttribute("aria-controls")).setAttribute("aria-hidden","false")}function d(e){var t,r,i=e.target,n=i.parentElement.previousElementSibling,a=i.parentElement.nextElementSibling;if(37===e.keyCode)t=n;else{if(39!==e.keyCode)return!1;t=a}"rtl"===c.documentElement.getAttribute("dir")&&(t=t===n?a:n),t&&(r=t.firstElementChild,i.setAttribute("tabindex","-1"),i.setAttribute("aria-selected",!1),c.querySelector("#"+i.getAttribute("aria-controls")).setAttribute("aria-hidden","true"),r.setAttribute("tabindex","0"),r.setAttribute("aria-selected","true"),r.focus(),c.querySelector("#"+r.getAttribute("aria-controls")).setAttribute("aria-hidden","false"))}}i&&(!function e(){u.self===u.top||r||(r=u.location.hash.replace(/.*secret=([\d\w]{10}).*/,"$1"),clearTimeout(t),t=setTimeout(function(){e()},100))}(),c.documentElement.className=c.documentElement.className.replace(/\bno-js\b/,"")+" js",c.addEventListener("DOMContentLoaded",n,!1),u.addEventListener("load",n,!1),u.addEventListener("resize",function(){u.self!==u.top&&(clearTimeout(e),e=setTimeout(function(){f("height",Math.ceil(c.body.getBoundingClientRect().height))},100))},!1))}(window,document);
</script>

このJS<script>タグ)には、以下の処理などが含まれています。

  • ページ表示時に親フレームへ対してiframeの適正な高さheightを送信する処理
  • リンクがクリックされた際に(親フレームごと)ページ遷移するよう親フレームへ送信する処理
  • WordPressが生成する埋め込み専用ページに設置されている「シェア」ボタンクリック時の処理

WordPressの「埋め込み」で必須となるのは前者2つのみとなり、最後の1つはあくまで「Wordpressが生成した埋め込み専用ページの1UIの動作処理定義」なので、本来は不要なものです。
そのため開発実装時にエンジニアの方に相談して取り除いてもらうのも良いですが、元となっている上記JSそのものが圧縮されており、分解して不要部分を削除するのは難易度が高いです。それであれば、そのまま全文コピー&ペーストが無難になります。

ちなみにもともとのWordpressが生成している埋め込み専用ページはよく考えられていて、右下のシェアボタンの箇所をクリックすると「Wordpressで埋め込みする際の方法」と「Wordpress以外で埋め込みするための方法」を解説するモーダルが表示されます。ユーザー目線で非常に良いと思います。

WordPress製の埋め込みページで、右下のボタンを押した際の表示
右下のシェアボタンを押すと、シェア方法の解説が表示されます。Good UI。

なお、埋め込み専用ページの注意点として、埋め込んだページからはiframeはsandboxで呼び出されます。そのため、もろもろの制限が発生しています。例えば親フレーム(iframeの外側)へ対して.pushMessage関数以外での干渉ができないため、リンククリック時に(iframeの外側のWordpressが用意したJSと対応する)JS処理を行わないとページ遷移が行われない、というのもこの制限に該当します。
ですので、埋め込み専用ページ内では下手にJSなどでいろんなことをしようとはせず、あくまで「ユーザーが自ブログなどでシェアするときに提供するリンクボックス」という役割だけに集中して作成されるのが良いでしょう。

細かい仕様の注意点

文中に出てきたものも含め、実装時の注意点などを再度まとめました。

  • リンクをtarget="_blank"などにしていても、Wordpressが埋め込み処理をする際、勝手に置換されて消される
    • リンクボックスのリンクを別タブで開くものにするには、URL提供側ではなく、埋め込むユーザー側のWordpressでのカスタマイズが必要となります。そのため、干渉できません。
  • WordPress以外からの埋め込みを考慮に入れる
    • oEmbedをベースにした仕様ではあるもののoEmbed準拠ではないので、そのままでは対応はしておりません。が、もしもWordpressの仕様に準じる形での「埋め込み」に対応したツールがWordpress以外にもあった場合の想定はしておいた方が良いかと思います。
  • iframeはsandboxになる
    • iframeの内外で対応したJS処理を設置しないとリンククリックしてもページ遷移しない、といったケースが発生します。
      そのため、<script>タグはWordpressが生成するものをそのまま流用するのが確実かと。
  • 埋め込む処理時にWordpress側で埋め込む内容はいろいろと置換がされる
    • 埋め込み用データのhtmlプロパティ内では、使用できるHTMLタグは<blockquote>タグ、<a>タグ、<iframe>タグのみと、非常に強い制限があります。
      また、<blockquote>タグの中の<a>タグのtarget属性などは取り除かれてしまうため、別タブリンクなどに(こちら側から)変更することはできません。さらには<script>タグも取り除かれてWordpressが用意するものに差し替えられてしまいますので、オリジナルのJSなどは入れられません。
  • 埋め込む際にWordpress側で埋め込もうとしたURLのキャッシュなどが行われる
    • 開発中の動作確認などの際は、キャッシュにお気を付けください。
      テストする埋め込みURLを都度変更するか、記事を都度削除と新規作成を繰り返して行う必要があります。
  • URLを埋め込もうとしているユーザー側のブログがSSLの場合、非SSLなURLの埋め込みはiframeの表示はされず、<blockquote>の表示となる

なお、ここまでサンプルとしてJSONなどを掲載しておりますが、常に内容が最新版である保証もできません。
そのため、実際の実装開発時は別途最新版のWordpressをご用意いただいて、そちらで出力されるものをご利用ください。

「埋め込み」に対応する、ということは要は「WordPressユーザーによるURLシェアの手段を増やす」という施策です。
メリットはダークトラフィックの一部が解消できることと、シェアの見た目をこちらが意図したものにできること。デメリットはそもそも利用されるかが未知数なため、かかる工数に対する費用対効果が(おそらくは高確率で)悪いかもしれないこと。

ただ、「埋め込み」を使うユーザーにとっては重宝するものではありますので、特にユーザーからシェアされやすいタイプのサイトでは、工数見積もりが少なく済みそうであれば対応を検討しても良いかもしれません。
例えばブランドサイトの製品詳細ページや、観光情報サイトの観光スポットページ、飲食店サイトのページなどなど。対応いただけると個人的にはすごく嬉しいな、と思います。

逆に、URLがなくなってリンクボックスが消えてしまうとユーザーの不利益となりますので、期間限定のキャンペーンページのような「一定期間後にはURLが変わる(なくなる)ようなページ」は向いていないと考えられます。
工数の費用対効果が不安な以上、向き不向きなども考慮に入れる必要がありそうです。

あと、最後にお詫びを。
弊社サイトも実はWordpressで構築されているのですが、カスタマイズをバリバリにやった結果、埋め込みに非対応な造りとなってしまっていることをここに陳謝します。ごめんなさい。

この記事を書いた人
畑岡 大作
畑岡 大作
デジタルマーケティングエンジニア

マークアップエンジニアとして活躍しながら昨今はGTMのスペシャリストに。日本初となる書籍を出版し、多くの顧客サイトのGTM設計、運用、アドバイスなどを行う。セミナー登壇も多数。好きな物はラノベ、ゲームからラーメン、万年筆(のインク)と幅広い。