oEmbed in WordPress
oEmbed is an open format created to simplify embedding content from one web page into another. Content can include: photos, videos, links, and other types of content.
oEmbed Content is video, audio, HTML, and other codes on your site that have been embedded from another site. For example, if you insert a link to a YouTube video in WP, it will be transformed into an iframe with the video.
Also read: Shortcodes in WordPress.
How It Works
The WP_Embed{} class is based on the WP_oEmbed{} class - it contains the list of registered oEmbed providers and is responsible for requesting (retrieving) and processing the Discovery link in the HTML head section.
At a very early stage, even before the mu_plugin_loaded hook, the WP_Embed class is initialized:
$GLOBALS['wp_embed'] = new WP_Embed();
The following hooks are created in the constructor WP_Embed::__construct():
function __construct() { // Hack to get the [embed] shortcode to run before wpautop(). add_filter( 'the_content', array( $this, 'run_shortcode' ), 8 ); add_filter( 'widget_text_content', array( $this, 'run_shortcode' ), 8 ); // Shortcode placeholder for strip_shortcodes(). add_shortcode( 'embed', '__return_false' ); // Attempts to embed all URLs in a post. add_filter( 'the_content', array( $this, 'autoembed' ), 8 ); add_filter( 'widget_text_content', array( $this, 'autoembed' ), 8 ); // After a post is saved, cache oEmbed items via Ajax. add_action( 'edit_form_advanced', array( $this, 'maybe_run_ajax_cache' ) ); add_action( 'edit_page_form', array( $this, 'maybe_run_ajax_cache' ) ); }
Frontend oEmbed URL Processing
The post or widget text content is checked for the presence of the shortcode [embed]
or a URL on a separate line. For this, as seen from the code above, two methods are triggered on the the_content hook:
-
WP_Embed::run_shortcode( $post_content ) — processes the
[embed]
shortcode. -
WP_Embed::autoembed( $post_content ) — processes the URL (on a separate line) as the
[embed]
shortcode.Then, each found URL (on a separate line or in a shortcode) is passed to the method WP_Embed::shortcode( $attr, $url ). Then:
-
Internal Handler is Checked | Embed
The URL is passed to the method WP_Embed::get_embed_handler_html( $rawattr, $url ). This method checks the passed URL for internal handlers, which are registered by the function wp_embed_register_handler().
-
If a handler is found, the URL is passed to the handler function and the result is returned. The WP_Embed::shortcode() method's operation ends here.
- If no handler is found, WP_Embed::shortcode() continues to check for oEmbed (external) handlers.
-
-
External Handler is Checked | oEmbed
- If there is a cache for the URL, the parsing is interrupted and the result is taken from the cache.
-
If there is no cache, the URL is passed to the function wp_oembed_get( $url, $attr ), then to the method WP_oEmbed::get_provider(), and then all registered providers (WP_oEmbed::$providers) are compared with this URL. If there is a match, a request is made to obtain the oEmbed insertion from another site (the WP_oEmbed class is responsible for this request). In the end, regardless of the result, the URL processing is cached.
With this approach, the cache is only created on the first request (when it doesn't exist) and is not updated. Cache updating only occurs when the post is updated in the admin panel.
-
Admin Panel oEmbed URL Processing
oEmbed caching is also created and (important) updated when the post is updated on the post editing page in the admin panel. This is done via an AJAX request. Such an AJAX request is created only when the post is updated in the admin panel - see WP_Embed::maybe_run_ajax_cache().
- wp_ajax_oembed_cache()
- WP_Embed::cache_oembed( $post_id )
- Then, the processing goes according to the scheme described above, the post content is passed to the methods: WP_Embed::run_shortcode( $post_content ) and WP_Embed::autoembed( $post_content ).
- WP_Embed::shortcode( $attr, $url ).
- and so on.
- WP_Embed::shortcode( $attr, $url ).
- Then, the processing goes according to the scheme described above, the post content is passed to the methods: WP_Embed::run_shortcode( $post_content ) and WP_Embed::autoembed( $post_content ).
- WP_Embed::cache_oembed( $post_id )
For example, let's consider how WordPress processes links to other sites on WordPress to embed content from another site. The general principle of all this is described above, and below we'll look at how it works in the admin panel.
Block Editor
When a link is inserted into the block editor, a JS event triggers an AJAX GET request to the route domain/wp-json/oembed/1.0/proxy
, which registers the method WP_oEmbed_Controller::register_routes(). Example of the sent data:
url: https://wp-punk.com/how-to-deal-with-date-and-time-in-wordpress/ _locale: user
The response is generated by the method WP_oEmbed_Controller::get_proxy_item():
{ "version": "1.0", "provider_name": "WP Punk", "provider_url": "https://wp-punk.com", "author_name": "Max Denisenko", "author_url": "https://wp-punk.com/author/mxadmin/", "title": "How to deal with Date and Time in WordPress", "type": "rich", "width": 600, "height": 338, "html": " <blockquote class=\"wp-embedded-content\" data-secret=\"DGdp4yAkb8\"> <a href=\"https://wp-punk.com/how-to-deal-with-date-and-time-in-wordpress/\">How to deal with Date and Time in WordPress</a> </blockquote> <iframe sandbox=\"allow-scripts\" security=\"restricted\" src=\"https://wp-punk.com/how-to-deal-with-date-and-time-in-wordpress/embed/#?secret=DGdp4yAkb8\" width=\"600\" height=\"338\" title=\"“How to deal with Date and Time in WordPress” — WP Punk\" data-secret=\"DGdp4yAkb8\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\" class=\"wp-embedded-content\"> </iframe> ", "thumbnail_url": "https://wp-punk.com/wp-content/uploads/2023/01/Date-and-Time.jpg", "thumbnail_width": 600, "thumbnail_height": 338 }
The data is cached in the *_options table.
Visual Editor
When using the visual editor and inserting a link, a JS event triggers a POST AJAX request to the file admin-ajax.php, where the function wp_ajax_parse_embed() works on the wp_ajax_parse_embed hook.
Example of sent data:
post_ID: 31 type: embed shortcode: [embed]https://oddstyle.ru/instrukciya-po-rabote-s-wordpress-rukovodstvo-dlya-novichkov[/embed] maxwidth: 549 action: parse-embed
The content of the shortcode
field is passed to WP_Embed::run_shortcode(). Read above in the section "How It Works" for what happens next.
Example of returned data:
{ "success":true, "data":{ "body":" <blockquote class=\"wp-embedded-content\" data-secret=\"DGdp4yAkb8\"> <a href=\"https://wp-punk.com/how-to-deal-with-date-and-time-in-wordpress/\">How to deal with Date and Time in WordPress</a> </blockquote> <iframe sandbox=\"allow-scripts\" security=\"restricted\" src=\"https://wp-punk.com/how-to-deal-with-date-and-time-in-wordpress/embed/#?secret=DGdp4yAkb8\" width=\"600\" height=\"338\" title=\"“How to deal with Date and Time in WordPress” — WP Punk\" data-secret=\"DGdp4yAkb8\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\" class=\"wp-embedded-content\"> </iframe> ", "attr":{ "width":600, "height":338 }, "head":"<script src=\"https://wp-test.ru/wp-includes/js/wp-embed.js\"></script>", "sandbox":true } }
The data is cached in the wp_postmeta
or wp_posts
table.
oEmbed Request Caching
Post meta fields or the wp_posts
table can be used for caching requests.
If the shortcode or link is called from the post content, the cache will be stored in the meta fields of the current post.
If the current post does not exist, the cache will be stored in the wp_posts
table under the post type oembed_cache
.
The cache is valid for 1 day (86400 seconds). This value can be changed using the oembed_ttl hook.
IMPORTANT! Checking the cache expiration time and updating the cache only occurs when the post is updated from the post editing page in the admin panel. The following hooks initialize the cache update in the admin panel:
add_action( 'edit_form_advanced', array( $this, 'maybe_run_ajax_cache' ) ); add_action( 'edit_page_form', array( $this, 'maybe_run_ajax_cache' ) );
If for some reason it was not possible to obtain the embedding HTML, for example, a 404 response was returned or something other than 200, then the marker {{unknown}}
will be added to the cache instead of the HTML.
oEmbed Security
Embedding someone else's code on your site opens up the possibility of an XSS attack. For example, the received code may contain malicious code that can access your site's cookies and can do a lot of other things.
To protect yourself from such security vulnerabilities, WordPress outputs the embedding in the <iframe> tag. It also cleans the received iframe code from unwanted attributes. In particular, the embedding code allows only three HTML tags, which are checked by the function wp_kses():
$allowed_html = array( 'a' => array( 'href' => true, ), 'blockquote' => array(), 'iframe' => array( 'src' => true, 'width' => true, 'height' => true, 'frameborder' => true, 'marginwidth' => true, 'marginheight' => true, 'scrolling' => true, 'title' => true, ), ); $html = wp_kses( $html, $allowed_html );
As we can see, the embedding code can only contain three tags and a limited number of parameters for these tags.
See the code of the function wp_filter_oembed_result(), which by default is hooked to oembed_dataparse and is applied to all received embedding codes.
WordPress as an oEmbed Provider
Since version 4.4, WordPress has become an oEmbed provider. Now, posts from one WordPress site can be embedded into another website.
To do this, you need to add the post URL to the post content. This can be done in two ways:
- Add the URL on a separate line.
- Insert the URL into the shortcode
[embed]
. For example,[embed]http://dom.com/adress[/embed#93;
.
WordPress will automatically process the specified URL (determine if it is an oEmbed provider), embed the data, and create a request cache to avoid making this request every time the page loads.
Embeds represent a brief version of the page. For example, if you add /embed
to the end of the URL of any post, you will be taken to the embed page (which should appear on another site when embedded). Here's an example of such a page: https://wp-kama.com/handbook/codex/oembed.
Here's an example of what an embed looks like:
oEmbed in WordPress
How to Modify the HTML Embed Code?
To do this, you need to create a file called embed.php
in the theme - more about template hierarchy.
If there is no such file in the theme, the embed code is controlled by the following files:
Each of these files has hooks that can be used to modify individual parts of the HTML embed page.
oEmbed Functions
- get_post_embed_url() — Retrieves the URL to embed a specific post in an iframe.
- get_post_embed_html() — Retrieves the embed code for a specific post.
- is_embed() — Is the query for an embedded post?
- wp_embed_register_handler() — Registers an embed handler.
- wp_oembed_get() — Embeds the object by specified URL. Tries to get the HTML code from the passed URL by using supported WordPress oEmbed providers.
- wp_oembed_add_provider() — Adds a URL format and oEmbed provider URL pair.
- wp_oembed_remove_provider() — Removes an oEmbed provider.
See Full list of Embed functions.
oEmbed Hooks
- embed_cache_oembed_types - allows changing the types of posts for which oembed links (shortcodes) should be processed.
- oembed_ttl — allows changing the time to live (TTL) of the cache.
- embed_oembed_html — allows changing the already cached HTML.
- oembed_dataparse — allows changing the content (HTML) created when embedding supported oEmbed URLs.
- embed_oembed_discover — allows specifying whether to follow the URL and search for the embedding
<link>
tag on the remote site.
See Full list of hooks.
Supported Providers
Below is a list of oEmbed providers that WordPress supports out of the box. Additional providers can be added to this list using the function wp_oembed_add_provider().
Provider | Flavor | Since |
---|---|---|
Dailymotion | dailymotion.com | 2.9.0 |
Flickr | flickr.com | 2.9.0 |
Scribd | scribd.com | 2.9.0 |
Vimeo | vimeo.com | 2.9.0 |
WordPress.tv | wordpress.tv | 2.9.0 |
YouTube | youtube.com/watch | 2.9.0 |
Crowdsignal | polldaddy.com | 3.0.0 |
SmugMug | smugmug.com | 3.0.0 |
YouTube | youtu.be | 3.0.0 |
twitter.com | 3.4.0 | |
instagram.com | 3.5.0 | |
instagr.am | 3.5.0 | |
Slideshare | slideshare.net | 3.5.0 |
SoundCloud | soundcloud.com | 3.5.0 |
Dailymotion | dai.ly | 3.6.0 |
Flickr | flic.kr | 3.6.0 |
Spotify | spotify.com | 3.6.0 |
Imgur | imgur.com | 3.9.0 |
Meetup.com | meetup.com | 3.9.0 |
Meetup.com | meetu.ps | 3.9.0 |
Animoto | animoto.com | 4.0.0 |
Animoto | video214.com | 4.0.0 |
Issuu | issuu.com | 4.0.0 |
Mixcloud | mixcloud.com | 4.0.0 |
Crowdsignal | poll.fm | 4.0.0 |
TED | ted.com | 4.0.0 |
YouTube | youtube.com/playlist | 4.0.0 |
Tumblr | tumblr.com | 4.2.0 |
Kickstarter | kickstarter.com | 4.2.0 |
Kickstarter | kck.st | 4.2.0 |
Cloudup | cloudup.com | 4.3.0 |
ReverbNation | reverbnation.com | 4.4.0 |
VideoPress | videopress.com | 4.4.0 |
reddit.com | 4.4.0 | |
Speaker Deck | speakerdeck.com | 4.4.0 |
twitter.com/timelines | 4.5.0 | |
twitter.com/moments | 4.5.0 | |
facebook.com | 4.7.0 | |
twitter.com/user | 4.7.0 | |
twitter.com/likes | 4.7.0 | |
twitter.com/lists | 4.7.0 | |
Screencast | screencast.com | 4.8.0 |
Amazon | amazon.com (com.mx, com.br, ca) | 4.9.0 |
Amazon | amazon.de (fr, it, es, in, nl, ru, co.uk) | 4.9.0 |
Amazon | amazon.co.jp (com.au) | 4.9.0 |
Amazon | amazon.cn | 4.9.0 |
Amazon | a.co | 4.9.0 |
Amazon | amzn.to (eu, in, asia) | 4.9.0 |
Amazon | z.cn | 4.9.0 |
Someecards | someecards.com | 4.9.0 |
Someecards | some.ly | 4.9.0 |
Crowdsignal | survey.fm | 5.1.0 |
Instagram TV | instagram.com | 5.1.0 |
Instagram TV | instagr.am | 5.1.0 |
TikTok | tiktok.com | 5.4.0 |
Removing Expired oEmbed Cache
For cleaning the database on large sites, it may make sense to periodically run such a function to delete expired cache.
/** * Remove expired oEmbed Cache. * * @param int $ttl The cache lifetime in seconds. * */ function kama_delete_expired_oembed_cache( $ttl = MONTH_IN_SECONDS ){ global $wpdb; // META $query_data = $wpdb->get_results( "SELECT * FROM $wpdb->postmeta WHERE meta_key LIKE '_oembed_time_%' ORDER BY meta_value+0 DESC" ); $res = []; foreach( $query_data as $data ){ $post = get_post( $data->post_id ); $info = date( 'd-m-Y', $data->meta_value ) ." - $post->ID: $post->post_title"; if( time() > $data->meta_value + $ttl ){ $oembed_meta_key = str_replace( '_oembed_time_', '_oembed_', $data->meta_key ); delete_post_meta( $data->post_id, $data->meta_key ); delete_post_meta( $data->post_id, $oembed_meta_key ); $res['DELETED'][] = $info; } else { $res['NOT DELETED'][] = $info; } } // POSTS $min_allowed_date = date( 'Y-m-d H:i:59', time() - $ttl ); $posts = $wpdb->get_results( "SELECT * FROM $wpdb->posts WHERE post_type = 'oembed_cache' AND post_modified_gmt < '$min_allowed_date' ORDER BY post_modified_gmt DESC" ); foreach( $posts as $post ){ $res['DELETED POSTS'][] = $post->post_modified_gmt; wp_delete_post( $post->ID, 'force_delete' ); } return $res; } $res = kama_delete_expired_oembed_cache(); print_r( $res );
Internal Embed Handler (with Caching)
First, I'll note that this is a hack - WP is not designed for this. But thanks to hooks, it can be done.
To do this, use the hook pre_oembed_result from the method WP_oEmbed::get_html():
// ... $pre = apply_filters( 'pre_oembed_result', null, $url, $args ); if ( null !== $pre ) { return $pre; } // ...
You need to register an internal handler through the function wp_embed_register_handler(). But in the handler function, return false and move the handler function to the mentioned hook pre_oembed_result.
So, the code will be something like this:
add_action( 'init', 'myembed_provider_register' ); add_filter( 'pre_oembed_result', 'myembed_provider_handler', 10, 3 ); function myembed_provider_register(){ wp_embed_register_handler( 'myembed', '~https://foo\.bar\.com/(\w+)~i', '__return_false' ); } function myembed_provider_handler( $null, $url, $args ){ $html = '{{unknown}}'; // process $url, make an HTTP request. See WP HTTP API // Return iframe or HTML code return $html; }
oEmbed for Arbitrary Text
Build In PostIf we need to process the shortcode [embed]
or auto-embedding a link in the text, then this text will need to be processed separately. Basic processing using the do_shortcodes() or apply_shortcodes() functions does not include oEmbed. By default, such processing is only done for the the_content
hook.
So, we have 2 options:
Option 1
Simple, but may not be suitable due to the excessive load on the the_content hook - it usually has a bunch of other things that may be unnecessary.
$text = ' Some text to check custom shortcode adding. [embed]https://my-youtube.com/watch?v=lWzMBLoLIAc[/embed] https://my-youtube.com/watch?v=uDQwKtkXV-0 '; $text = apply_filters( 'the_content', $text ); echo $text;
Option 2: Point Approach
We only do what we need with the text:
$text = ' Some text to check custom shortcode adding. [embed]https://my-youtube.com/watch?v=lWzMBLoLIAc[/embed] https://my-youtube.com/watch?v=uDQwKtkXV-0 '; $text = $GLOBALS['wp_embed']->run_shortcode( $text ); // shortcode $text = $GLOBALS['wp_embed']->autoembed( $text ); // oEmbed URLs //$text = apply_shortcodes( $text ); $text = wpautop( $text ); echo $text;
oEmbed in WordPress Comments
Build In PostThe code below allows using oEmbed in WordPress comments.
Correction for AJAX functionality:
Disabling oEmbed
Read about it in a separate article