embed

PHP

【WordPress】URLによる投稿埋め込み(embed)機能の仕組み

更新日:2023/03/13

WordPressはURLを貼り付けるだけでブログカードをWebページに表示できる、投稿埋め込み(embed)機能があります。
しかしWebページによっては埋め込めないものがあります。
埋め込めるものと埋め込めないものの差が気になり、仕組みを調べてみました。

 

投稿内容の自動埋め込み

まずはURLを投稿埋め込みに置き換える仕組みを解説します。

投稿内容からのURL認識

投稿画面で入力した内容は、次の二つの正規表現とマッチング処理されます。
そしてどちらか一方にマッチングしたものが、投稿埋め込みの対象URLとなります。

】 |^(\s*)(https?://[^\s<>"]+)(\s*)$|im
】 |(<p(?: [^>]*)?>\s*)(https?://[^\s<>"]+)(\s*<\/p>)|i

【1】は、一行にURLのみ記述されているとマッチします。
ただし前後に複数の半角スペースが含まれていてもOKです。
全角スペースはNGです。

【2】は、pタグに囲まれたURLがマッチします。
pタグにはクラス等が記述されていてもOKです。
こちらは一行制限はなくて、pタグの前後に他のタグがあってもOKです。
また【1】と同じように、URLの前後に複数の半角スペースが含まれていてもOKです。

この機能を停止させる方法は、次のページで解説しています。
【WordPress】URLを自動でブログカードに変換する機能を停止させる方法

ショートコードでの投稿埋め込み

投稿埋め込みはショートコードでも行えます。
URLのみでの置き換えが上手くいかない時は、こちらを試してください。

[embed]https://ドメイン名/xxxxx/[/embed]

または、srcパラメーターでURL指定できます。

[embed src="hhttps://ドメイン名/xxxxx"]

 

自動投稿埋め込みの内容

認識された内部URLと外部URLは、共に次のようなタグに置き換えられます。

<blockquote class="wp-embedded-content" data-secret="YpMtqNvivc">
    <p>
     <a href="https://ドメイン名/?p=1">ページタイトル</a>
    </p>
</blockquote>
<p>
  <iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted" style="position: absolute; clip: rect(1px, 1px, 1px, 1px);" title="ページタイトル" src="https://ドメイン名/xxxx/embed/#?secret=hZji9OrrtW#?secret=YpMtqNvivc" data-secret="YpMtqNvivc" width="600" height="338" frameborder="0" marginwidth="0" marginheight="0" scrolling="no">
  </iframe><br />https://ドメイン名/?p=1
</p>

ここには実際に表示されるブログカードのタグは含まれていません。
ブログカードはiframeで非同期に読み込まれます。

このiframeは初期状態では、style="position: absolute; clip: rect(1px, 1px, 1px, 1px);"の効果で、左上から幅1px高さ1pxのみ表示されています。
ほぼ、非表示ですね。
その代わりにblockquoteタグ内のリンクが表示されています。

次に、下のタグで読み込まれたスクリプトがiframeの読み込み状況を監視します。

<script src='https://ドメイン名/wp-includes/js/wp-embed.min.js?ver=9.9.9' id='wp-embed-js'></script>

読み込みが完了すると非表示状態が無効化されます。
そしてblockquoteタグが非表示に変更されます。

 

外部URLの投稿埋め込み

内部URLについてはデータベースにタイトルやサムネイルの情報があるので、ブログカードの作成は容易です。

しかし外部URLは、外部のサーバーから情報を取得する必要があります。
その手順について、解説します。

手順

外部URLは、次の手順で投稿埋め込みデータを取得します。

外部URLがホワイトリストに入っている場合

  1. プログラムコードで登録済みのURLにアクセスして埋め込むhtmlを取得する

このページの投稿埋め込みのホワイトリストについてで解説しますが、ホワイトリストに登録されている外部URLは情報の取得先URLが登録されています。

取得したhtmlデータは、内容をチェックせずに埋め込まれます。

外部URLがホワイトリストに入っていない場合

  1. 外部URLにアクセスしてWebページのhtmlを取得する
  2. htmlからtype属性が"application/json+oembed"または"text/xml+oembed"のlinkタグを探して、href属性を取得する
  3. href属性のURLにアクセスして投稿埋め込み情報を取得し、情報からhtmlデータを取得する
  4. htmlデータをチェックして形式が合っていたら埋め込む

いずれかの処理でデータが取得できずに次に進めないときは、埋め込み処理されません。
外部URLの文字がそのまま残ります。

対象のlinkタグ

処理の2番目で参照するlinkタグは、次のようなものです。

<link rel="alternate" type="application/json+oembed" href="https://ドメイン名/index.php?rest_route=%2Foembed%2F1.0%2Fembed&#038;url=https%3A%2F%2Fドメイン名%2F%3Fp%3D1" />
<link rel="alternate" type="text/xml+oembed" href="https://ドメイン名/index.php?rest_route=%2Foembed%2F1.0%2Fembed&#038;url=https%3A%2F%2Fドメイン名%2F%3Fp%3D1&#038;format=xml" />

これらのタグのhref属性にアクセスして、投稿埋め込み情報を取得します。
"application/json+oembed"が優先されます。

投稿埋め込み情報の形式

WordPressで生成している投稿埋め込み情報は、次のようなデータです。

※WordPressではないサイトは、異なる情報で構成されている可能性があります。

type="application/json+oembed"

{
"version":"1.0",
"provider_name":"My Blog",
"provider_url":"https:\/\/ドメイン名",
"author_name":"\u3051\u30fc\u3061\u3083\u3093",
"author_url":"https:\/\/ドメイン名\/?author=1",
"title":"Hello world!",
"type":"rich",
"width":600,
"height":338,
"html":"<blockquote class=\"wp-embedded-content\" data-secret=\"2u5iwtGgry\"><a href=\"https:\/\/ドメイン名\/?p=1\">Hello world!<\/a><\/blockquote><iframe sandbox=\"allow-scripts\" security=\"restricted\" src=\"https:\/\/ドメイン名\/?p=1&embed=true#?secret=2u5iwtGgry\" width=\"600\" height=\"338\" title=\"“Hello world!” — My Blog\" data-secret=\"2u5iwtGgry\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\" class=\"wp-embedded-content\"><\/iframe><script>\n\/*! This file is auto-generated *\/\n!function(c,l){\"use strict\";var e=!1,o=!1;if(l.querySelector)if(c.addEventListener)e=!0;if(c.wp=c.wp||{},c.wp.receiveEmbedMessage);else if(c.wp.receiveEmbedMessage=function(e){var t=e.data;if(!t);else if(!(t.secret||t.message||t.value));else if(\/[^a-zA-Z0-9]\/.test(t.secret));else{for(var r,s,a,i=l.querySelectorAll('iframe[data-secret=\"'+t.secret+'\"]'),n=l.querySelectorAll('blockquote[data-secret=\"'+t.secret+'\"]'),o=0;o<n.length;o++)n[o].style.display=\"none\";for(o=0;o<i.length;o++)if(r=i[o],e.source!==r.contentWindow);else{if(r.removeAttribute(\"style\"),\"height\"===t.message){if(1e3<(s=parseInt(t.value,10)))s=1e3;else if(~~s<200)s=200;r.height=s}if(\"link\"===t.message)if(s=l.createElement(\"a\"),a=l.createElement(\"a\"),s.href=r.getAttribute(\"src\"),a.href=t.value,a.host===s.host)if(l.activeElement===r)c.top.location.href=t.value}}},e)c.addEventListener(\"message\",c.wp.receiveEmbedMessage,!1),l.addEventListener(\"DOMContentLoaded\",t,!1),c.addEventListener(\"load\",t,!1);function t(){if(o);else{o=!0;for(var e,t,r,s=-1!==navigator.appVersion.indexOf(\"MSIE 10\"),a=!!navigator.userAgent.match(\/Trident.*rv:11\\.\/),i=l.querySelectorAll(\"iframe.wp-embedded-content\"),n=0;n<i.length;n++){if(!(r=(t=i[n]).getAttribute(\"data-secret\")))r=Math.random().toString(36).substr(2,10),t.src+=\"#?secret=\"+r,t.setAttribute(\"data-secret\",r);if(s||a)(e=t.cloneNode(!0)).removeAttribute(\"security\"),t.parentNode.replaceChild(e,t);t.contentWindow.postMessage({message:\"ready\",secret:r},\"*\")}}}}(window,document);\n<\/script>\n",
"thumbnail_url":"https:\/\/ドメイン名\/wp-content\/uploads\/xxxx.jpg",
"thumbnail_width":200,
"thumbnail_height":164
}

記事にアイキャッチ画像が登録されていないときは、"thumbnail_url""thumbnail_width""thumbnail_height"が出力されません。

最後の "html" の内容が、投稿埋め込み用のhtmlです。

type属性が"text/xml+oembed"のlinkタグのhref属性にアクセスすると、次のようなデータを取得できます。

type="text/xml+oembed"

<?xml version="1.0"?>
<oembed>
<version>1.0</version>
<provider_name>My Blog</provider_name>
<provider_url>https://ドメイン名</provider_url>
<author_name>&#x3051;&#x30FC;&#x3061;&#x3083;&#x3093;</author_name>
<author_url>https:/ドメイン名/?author=1</author_url>
<title>Hello world!</title>
<type>rich</type>
<width>600</width>
<height>338</height>
<html>&lt;blockquote class="wp-embedded-content" data-secret="1Xekd319fw"&gt;&lt;a href="https://ドメイン名/?p=1"&gt;Hello world!&lt;/a&gt;&lt;/blockquote&gt;&lt;iframe sandbox="allow-scripts" security="restricted" src="https://ドメイン名/?p=1&amp;embed=true#?secret=1Xekd319fw" width="600" height="338" title="&#x201C;Hello world!&#x201D; &#x2014; My Blog" data-secret="1Xekd319fw" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" class="wp-embedded-content"&gt;&lt;/iframe&gt;&lt;script&gt;
/*! This file is auto-generated */
!function(c,l){"use strict";var e=!1,o=!1;if(l.querySelector)if(c.addEventListener)e=!0;if(c.wp=c.wp||{},c.wp.receiveEmbedMessage);else if(c.wp.receiveEmbedMessage=function(e){var t=e.data;if(!t);else if(!(t.secret||t.message||t.value));else if(/[^a-zA-Z0-9]/.test(t.secret));else{for(var r,s,a,i=l.querySelectorAll('iframe[data-secret="'+t.secret+'"]'),n=l.querySelectorAll('blockquote[data-secret="'+t.secret+'"]'),o=0;o&lt;n.length;o++)n[o].style.display="none";for(o=0;o&lt;i.length;o++)if(r=i[o],e.source!==r.contentWindow);else{if(r.removeAttribute("style"),"height"===t.message){if(1e3&lt;(s=parseInt(t.value,10)))s=1e3;else if(~~s&lt;200)s=200;r.height=s}if("link"===t.message)if(s=l.createElement("a"),a=l.createElement("a"),s.href=r.getAttribute("src"),a.href=t.value,a.host===s.host)if(l.activeElement===r)c.top.location.href=t.value}}},e)c.addEventListener("message",c.wp.receiveEmbedMessage,!1),l.addEventListener("DOMContentLoaded",t,!1),c.addEventListener("load",t,!1);function t(){if(o);else{o=!0;for(var e,t,r,s=-1!==navigator.appVersion.indexOf("MSIE 10"),a=!!navigator.userAgent.match(/Trident.*rv:11\./),i=l.querySelectorAll("iframe.wp-embedded-content"),n=0;n&lt;i.length;n++){if(!(r=(t=i[n]).getAttribute("data-secret")))r=Math.random().toString(36).substr(2,10),t.src+="#?secret="+r,t.setAttribute("data-secret",r);if(s||a)(e=t.cloneNode(!0)).removeAttribute("security"),t.parentNode.replaceChild(e,t);t.contentWindow.postMessage({message:"ready",secret:r},"*")}}}}(window,document);
&lt;/script&gt;
</html>
</oembed>
<thumbnail_url>https://ドメイン名/wp-content/uploads/xxxx.jpg</thumbnail_url>
<thumbnail_width>200</thumbnail_width>
<thumbnail_height>164</thumbnail_height>

こちらも記事にアイキャッチ画像が登録されていないときは、<thumbnail_url><thumbnail_width><thumbnail_height>が出力されません。

最後の <html> の内容が、投稿埋め込み用のhtmlです。

htmlデータチェック

ホワイトリストに登録されていないサイトから取得したhtmlは、blockquoteタグの後にiframeタグが記述されているもののみ許可されています。

具体的には、次の正規表現にマッチするかどうかチェックされます。

'|(<blockquote>.*?</blockquote>)?.*(<iframe.*?></iframe>)|ms'

blockquoteiframeの間や前後に他のタグが記述されていても受け入れられます。
ただし、この二つ以外のタグは削除されます。
WordPressの埋め込み情報にscriptタグが含まれていますが、これも削除されます。

htmlデータチェックは内部URLでも行われます。
htmlの形式を大幅に変更するときは、自サイトのURLをホワイトリストに登録する必要があります。

 

投稿埋め込みのホワイトリストについて

外部URLの投稿埋め込み機能のホワイトリストについて、解説します。

ホワイトリストの既定値

WordPressのバージョン6.1.1は、規定のホワイトリストとして次のような設定がおこなわれています。

マッチングURL投稿埋め込み(embed)情報取得先URL
#https?://((m|www)\.)?youtube\.com/watch.*#ihttps://www.youtube.com/oembed
#https?://((m|www)\.)?youtube\.com/playlist.*#ihttps://www.youtube.com/oembed
#https?://((m|www)\.)?youtube\.com/shorts/*#ihttps://www.youtube.com/oembed
#https?://youtu\.be/.*#ihttps://www.youtube.com/oembed
#https?://(.+\.)?vimeo\.com/.*#ihttps://vimeo.com/api/oembed.{format}
#https?://(www\.)?dailymotion\.com/.*#ihttps://www.dailymotion.com/services/oembed
#https?://dai\.ly/.*#ihttps://www.dailymotion.com/services/oembed
#https?://(www\.)?flickr\.com/.*#ihttps://www.flickr.com/services/oembed/
#https?://flic\.kr/.*#ihttps://www.flickr.com/services/oembed/
#https?://(.+\.)?smugmug\.com/.*#ihttps://api.smugmug.com/services/oembed/
#https?://(www\.)?scribd\.com/(doc|document)/.*#ihttps://www.scribd.com/services/oembed
#https?://wordpress\.tv/.*#ihttps://wordpress.tv/oembed/
#https?://(.+\.)?polldaddy\.com/.*#ihttps://api.crowdsignal.com/oembed
#https?://poll\.fm/.*#ihttps://api.crowdsignal.com/oembed
#https?://(.+\.)?survey\.fm/.*#ihttps://api.crowdsignal.com/oembed
#https?://(www\.)?twitter\.com/\w{1,15}/status(es)?/.*#ittps://publish.twitter.com/oembed
#https?://(www\.)?twitter\.com/\w{1,15}$#ihttps://publish.twitter.com/oembed
#https?://(www\.)?twitter\.com/\w{1,15}/likes$#ihttps://publish.twitter.com/oembed
#https?://(www\.)?twitter\.com/\w{1,15}/lists/.*#ihttps://publish.twitter.com/oembed
#https?://(www\.)?twitter\.com/\w{1,15}/timelines/.*#ihttps://publish.twitter.com/oembed
#https?://(www\.)?twitter\.com/i/moments/.*#ihttps://publish.twitter.com/oembed
#https?://(www\.)?soundcloud\.com/.*#ihttps://soundcloud.com/oembed
#https?://(.+?\.)?slideshare\.net/.*#ihttps://www.slideshare.net/api/oembed/2
#https?://(open|play)\.spotify\.com/.*#ihttps://embed.spotify.com/oembed/
#https?://(.+\.)?imgur\.com/.*#ihttps://api.imgur.com/oembed
#https?://(www\.)?issuu\.com/.+/docs/.+#ihttps://issuu.com/oembed_wp
#https?://(www\.)?mixcloud\.com/.*#ihttps://www.mixcloud.com/oembed
#https?://(www\.|embed\.)?ted\.com/talks/.*#ihttps://www.ted.com/services/v1/oembed.{format}
#https?://(www\.)?(animoto|video214)\.com/play/.*#ihttps://animoto.com/oembeds/create
#https?://(.+)\.tumblr\.com/.*#ihttps://www.tumblr.com/oembed/1.0
#https?://(www\.)?kickstarter\.com/projects/.*#ihttps://www.kickstarter.com/services/oembed
#https?://kck\.st/.*#ihttps://www.kickstarter.com/services/oembed
#https?://cloudup\.com/.*#ihttps://cloudup.com/oembed
#https?://(www\.)?reverbnation\.com/.*#ihttps://www.reverbnation.com/oembed
#https?://videopress\.com/v/.*#https://public-api.wordpress.com/oembed/?for= . $host
#https?://(www\.)?reddit\.com/r/[^/]+/comments/.*#ihttps://www.reddit.com/oembed
#https?://(www\.)?speakerdeck\.com/.*#ihttps://speakerdeck.com/oembed.{format}
#https?://(www\.)?screencast\.com/.*#ihttps://api.screencast.com/external/oembed
#https?://([a-z0-9-]+\.)?amazon\.(com|com\.mx|com\.br|ca)/.*#ihttps://read.amazon.com/kp/api/oembed
#https?://([a-z0-9-]+\.)?amazon\.(co\.uk|de|fr|it|es|in|nl|ru)/.*#ihttps://read.amazon.co.uk/kp/api/oembed
#https?://([a-z0-9-]+\.)?amazon\.(co\.jp|com\.au)/.*#ihttps://read.amazon.com.au/kp/api/oembed
#https?://([a-z0-9-]+\.)?amazon\.cn/.*#ihttps://read.amazon.cn/kp/api/oembed
#https?://(www\.)?a\.co/.*#ihttps://read.amazon.com/kp/api/oembed
#https?://(www\.)?amzn\.to/.*#ihttps://read.amazon.com/kp/api/oembed
#https?://(www\.)?amzn\.eu/.*#ihttps://read.amazon.co.uk/kp/api/oembed
#https?://(www\.)?amzn\.in/.*#ihttps://read.amazon.in/kp/api/oembed
#https?://(www\.)?amzn\.asia/.*#ihttps://read.amazon.com.au/kp/api/oembed
#https?://(www\.)?z\.cn/.*#ihttps://read.amazon.cn/kp/api/oembed
#https?://www\.someecards\.com/.+-cards/.+#ihttps://www.someecards.com/v2/oembed/
#https?://www\.someecards\.com/usercards/viewcard/.+#ihttps://www.someecards.com/v2/oembed/
#https?://some\.ly\/.+#ihttps://www.someecards.com/v2/oembed/
#https?://(www\.)?tiktok\.com/.*/video/.*#ihttps://www.tiktok.com/oembed
#https?://([a-z]{2}|www)\.pinterest\.com(\.(au|mx))?/.*#ihttps://www.pinterest.com/oembed.json
#https?://(www\.)?wolframcloud\.com/obj/.+#ihttps://www.wolframcloud.com/oembed
#https?://pca\.st/.+#ihttps://pca.st/oembed.json

ホワイトリストのマッチングと情報取得

埋め込み処理対象のURLは、前表のマッチングURLで順番にマッチングされます。

マッチしたら、前表の投稿埋め込み(embed)情報取得先URLに次のパラメーターを付与します。

maxwidth : 最大幅
maxheight : 最大高さ
url : 対象の外部URL
dnt : 1
■例
処理対象のURL: https://youtu.be/xxxxx

マッチング: #https?://youtu\.be/.*#i

情報取得URL: https://www.youtube.com/oembed?maxwidth=300&maxheight=300&url=https://youtu.be/xxxxx&dnt=1

情報取得URLが生成できたら、そのURLにアクセスして投稿埋め込み情報を取得します。

その情報からhtmlデータを取得して、投稿に埋め込みます。

ホワイトリストの追加

ホワイトリストへの追加は、wp_oembed_add_provider()を使用します。

wp_oembed_add_provider()の構文

wp_oembed_add_provider( $format, $provider, $regex = false )
  • $format :
    URLとマッチングさせる正規表現またはワイルドカード(*)を使用した文字列

  • $provider :
    投稿埋め込み(embed)情報の取得先URL

  • $regex :
    $formatが正規表現ならtrue

WordPressサイトをホワイトリストに追加する場合、次のようにします。

wp_oembed_add_provider('https://ドメイン/*'
   ,'https://ドメイン/index.php?rest_route=%2Foembed%2F1.0%2Fembed',false);

 

最後に

投稿埋め込みについてまとめてみました。
これで、どのようなWebページが埋め込みできるか理解できると思います。

WordPressはHTMやCSSの知識も必要。総合的な知識を身につけよう。

更新日:2023/03/13

書いた人(管理人):けーちゃん

スポンサーリンク

記事の内容について

null

こんにちはけーちゃんです。
説明するのって難しいですね。

「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。

掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。

ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php

 

このサイトは、リンクフリーです。大歓迎です。