Embed Extension

This extension can embed rich content (like videos, tweets, etc.) from other websites.

The syntax is very simple - simply place any https:// URL on its own line like this:

Check out this video!

https://www.youtube.com/watch?v=dQw4w9WgXcQ

If the link points to embeddable content, it will be replaced with the rich HTML needed to embed it:

<p>Check out this video:</p>
<iframe width="200" height="113" src="https://www.youtube.com/embed/dQw4w9WgXcQ?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

Installation

This extension is bundled with league/commonmark. This library can be installed via Composer:

composer require league/commonmark

You’ll also need to install a third-party OEmbed library - see the Adapter section below.

Usage

Configure your Environment as usual and add the EmbedExtension provided by this package:

use Embed\Embed;
use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\Embed\EmbedExtension;
use League\CommonMark\MarkdownConverter;

// Define your configuration
$config = [
    'embed' => [
        'adapter' => new OscaroteroEmbedAdapter(), // See the "Adapter" documentation below
        'allowed_domains' => ['youtube.com', 'twitter.com', 'github.com'],
        'fallback' => 'link',
    ],
];

// Configure the Environment with all whatever other extensions you want
$environment = new Environment($config);
$environment->addExtension(new CommonMarkCoreExtension());

// Add this extension
$environment->addExtension(new EmbedExtension());

// Instantiate the converter engine and start converting some Markdown!
$converter = new MarkdownConverter($environment);
echo $converter->convert('https://www.youtube.com/watch?v=dQw4w9WgXcQ');

Configuration

This extension supports the following configuration options under the embed configuration:

adapter option

Any instance of EmbedAdapterInterface - see the “Adapter” section below.

allowed_domains option

This option defines a list of hosts that you wish to allow embedding content from. For example, setting this to ['youtube.com'] would only allow videos from YouTube to be embedded. It’s extremely important that you only include websites you trust since they’ll be providing HTML that is directly embedded in your website.

Any subdomains of these domains will also be allowed. For example, ['youtube.com'] would allow embedding from youtube.com or www.youtube.com.

This check is applied only to the URL that appears in the Markdown. It does not constrain the HTTP requests the adapter makes while fetching the embed. In particular, the embed/embed library will follow HTTP redirects and will fetch oEmbed endpoints advertised by the pages it loads, and those may live on other hosts. Listing a domain here therefore means trusting wherever that domain redirects to and whichever oEmbed endpoints it points at. If your application can reach internal services, see Restricting outbound requests below.

As an additional safety measure, we recommend that you also use a Content Security Policy (CSP) to prevent unexpected content from being embedded.

By default, this option is an empty array ([]), which means that all domains are allowed.

fallback option

This option defines the behavior when a URL cannot be embedded, either because it’s not in the list of allowed_domains, or because the adapter could not find embeddable content for that URL.

There are two possible values for this option:

Adapter

league/commonmark doesn’t know how to obtain the embeddable HTML for a given URL - this must be done by an external library.

embed/embed Adapter

We do provide an adapter for the popular embed/embed library. if you’d like to use that. We like this library because it supports fetching multiple URLs in parallel, which is ideal for performance, and it supports a wide range of embeddable content.

To use that library, you’ll need to composer require embed/embed and then pass new OscaroteroEmbedAdapter() as the adapter configuration option, as shown in the Usage section above.

Note: embed/embed requires a PSR-17 implementation to be installed. If you do not have one installed, the library will not work. By default these libraries are detected automatically:

Need to customize the maximum width/height of the embedded content? You can do that by instantiating the service provided by embed/embed, configuring it as needed, and passing that customized instance into the adapter:

use Embed\Embed;
use League\CommonMark\Extension\Embed\Bridge\OscaroteroEmbedAdapter;

// Configure the Embed library itself
$embedLibrary = new Embed();
$embedLibrary->setSettings([
    'oembed:query_parameters' => [
        'maxwidth' => 800,
        'maxheight' => 600,
    ],
    'twitch:parent' => 'example.com',
    'facebook:token' => '1234|5678',
    'instagram:token' => '1234|5678',
    'twitter:token' => 'asdf',
]);

// Inject it into our adapter
$config = [
    'adapter' => new OscaroteroEmbedAdapter($embedLibrary),
];

// Instantiate your CommonMark environment and converter like usual
// ...

Restricting outbound requests

The allowed_domains option only checks the URL in the Markdown. Once a URL passes that check, embed/embed fetches it with its own HTTP client, which follows redirects by default and then requests any oEmbed endpoint discovered in the response. Both of those requests may target hosts that are not in allowed_domains.

You can disable redirects by configuring the library’s CurlClient and injecting it through a Crawler. Note that these settings must be applied to the CurlClient - passing them to Embed::setSettings() has no effect on the HTTP layer:

use Embed\Embed;
use Embed\Http\Crawler;
use Embed\Http\CurlClient;
use League\CommonMark\Extension\Embed\Bridge\OscaroteroEmbedAdapter;

$client = new CurlClient();
$client->setSettings([
    'follow_location' => false, // or keep redirects but cap them with 'max_redirs' => 1
    'ignored_errors' => [],     // don't swallow HTTP errors
]);

$embedLibrary = new Embed(new Crawler($client));

$config = [
    'adapter' => new OscaroteroEmbedAdapter($embedLibrary),
];

Disabling redirects does not stop the separate oEmbed endpoint requests. To validate every outbound request (for example, to block private IP ranges or enforce your own allowlist on each hop), pass a PSR-18 client that performs those checks to the Crawler instead of CurlClient. Network egress restrictions on the server remain the most robust defense regardless of library configuration.

Custom Adapter

If you prefer to use a different library, you’ll need to implement our EmbedAdapterInterface yourself with whatever OEmbed library you choose.

Tips

If you need to wrap the HTML in a container tag, consider using the HtmlDecorator renderer:

$environment->addRenderer(Embed::class, new HtmlDecorator(new EmbedRenderer(), 'div', ['class' => 'embeded-content']));

Edit this page