Easily embed this site’s content on other sites

This site allows you to get the content of posts and pages by adding either /embed/ or ?embed to the URL, optionally including the post title, author, and/or tags.

This was inspired by a discussion in an Indieweb Homebrew Website Club Europe/London online meetup. I want to say shadowy web standards advocate Tantek Çelik brought it up, so, as these things tend to happen, I coded it up here while we were talking. It’s discussed on Indieweb’s wiki at https://indieweb.org/embed.

What do you mean, ‘Embed’?

Well, for instance, this post’s permalink is https://michaelkupietz.com/?p=10887. You can see just the text of this post’s content, without the sidebar and menus and other web page “furniture”, at https://michaelkupietz.com/embed/?p=10887 or https://michaelkupietz.com/embed/?p=10887&embed, so you can theoretically include this page’s content on your own site (long as you don’t mind ugly, 1993-looking unstyled HTML.) These are linked in the header of every page, as on this page you see Embed link up there in the small row of article info under the title.

You can append ?title, ?author or ?tags parameters to the embed URL too, to include the page title, author, and/or post tags at the top of the embed content, respectively. Just do something like “https://michaelkupietz.com/blah/blah/embed/?title&author&tags”.

How It Works Under The Hood (a WordPress-centric view)

This is done by adding the following code to the theme’s functions.php file:

// prevent stripping of /embed/ and embed parameters on redirects
function preserve_embed_parts($redirect_url, $requested_url) {
    if ((strpos($requested_url, '/embed/') !== false ||
         isset($_GET['embed'])) &&
        $redirect_url !== $requested_url) {
        
        // If original had /embed/, add it to redirect
        if (strpos($requested_url, '/embed/') !== false && strpos($redirect_url, '/embed/') === false) {
            return rtrim($redirect_url, '/') . '/embed/';
        }
        
        // If original had ?embed or &embed, add it to redirect
        if (isset($_GET['embed']) && strpos($redirect_url, 'embed') === false) {
            $separator = (strpos($redirect_url, '?') !== false) ? '&' : '?';
            return $redirect_url . $separator . 'embed';
        }
    }
    return $redirect_url;
}
add_filter('redirect_canonical', 'preserve_embed_parts', 10, 2);

// Apply template for embed patterns
function check_embed_template($template) {
    $uri = $_SERVER['REQUEST_URI'];
    $is_embed = (
        strpos($uri, '/embed/') !== false ||
        isset($_GET['embed'])
    );
    
    if ($is_embed) {
        $new_template = locate_template(array('template-content-only.php'));
        if ($new_template) {
            return $new_template;
        }
    }
    return $template;
}
add_filter('template_include', 'check_embed_template');

template-content-only.php

Then in the theme folder, there’s a template ‘template-content-only.php’, which is basically as follows:

<?php
/*
* Template Name: Content Only
*/
?><!DOCTYPE html><html><head>
<?php echo '<link rel="canonical" href="' .
    get_the_permalink() .
    '" /><!-- add canonical. -->' .
    "\n"; ?></head>
<body><?php while (have_posts()):
    the_post();
    if (isset($_GET["title"])) {
        echo '<h2 class="embed_title">';
        echo get_the_title();
        echo "</h2>";
    }
    if (isset($_GET["tags"])) {
        echo '<style>.screen-reader-text.screen-reader-text {border: 0;clip: rect(1px,1px,1px,1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);height: 1px;margin: -1px;overflow: hidden;padding: 0;position: absolute !important;width: 1px;word-wrap: normal !important;word-break: normal;
}</style>';
        echo "<i>Posted in ";
        /* [theme-specific code goes here to display post tags; omitted because you're not going to be able to use it, my theme is totally customized, so your theme's functions are different.] */
        echo "</i>";
    }
    if (isset($_GET['author'])) {
  echo '<p class="embed_author"><i>By ';
        echo get_the_author();
        echo'</i></p>';
    }
    the_content();
    $pl = get_the_permalink();
    echo '<p style="font-size:.75em";>Content originally from <a href="' .
        $pl .
        '" target="_new" rel="noopener">' .
        $pl .
        "</a>. &copy " .
        ($modified_date =
            esc_html(get_the_modified_date("Y")) . " Michael E. Kupietz</p>");
endwhile;
// End of the loop.
?>  </body>
            </html><?php

But Wait, There’s More! A caveat.

If you’re like me, and I think you are, you have Content Security Policy headers (I’d link to that, but there really aren’t any references that don’t make it sound much more complicated than it is) set up on your site to make sure nobody can inject scripts or wrap your site in an invisible iframe and clickjack your mom while she thinks she’s looking directly at your site. Well, like all good security measures, this will trip you up, because if you have it set right, it may prevent the end goal of embedding from working at all, because nobody will be allowed to put your site in an iframe.

This will happen if you have something like this in your .htaccess file:

Header set Content-Security-Policy "[a bunch of rules & garbage here]; frame-ancestors 'self'". You may have none instead of .

So we want to bend the rules, so if the url ends in /embed/ or has an ?embed url parameter, the frame-ancestors restrictions are lifted just for that page.

We do this by wrapping that line with the following, wherever in your .htaccess file the security headers are defined:

<If "%{THE_REQUEST} =~ m|/embed/?(?:[ ?#])| || %{QUERY_STRING} =~ m#(?:^|&)embed(?:&|$|=)#">
Header set Content-Security-Policy "[all your original rules and junk go here except for the frame-ancestors part]; frame-ancestors 'self' *"
</If>
<Else>
Header set Content-Security-Policy "[a bunch of rules & garbage here]; frame-ancestors 'self'" [this is just your original Header set Content-Security-Policy line, unchanged from what it was before because we want it to still apply to everything except the special Embed pages.]
</Else>

Huge web nerds will notice I used %{THE_REQUEST} instead of %{REQUEST_URI} like ChatGPT told you to when you wimped out and asked it how to do this. This is because ChatGPT doesn’t know that there’s a bug in Apache where %{REQUEST_URI} doesn’t work right in conditionals and will always fail and cause you to spend an entire day going in circles, rending your clothes and wailing in despair at the dark gods who came up with this system. The sole reference I’ve seen to this is at https://processwire.com/talk/topic/23000-apache2-blocks-dont-work-with-regex-matches-against-the-request_uri-variable/, as was kindly pointed out to me by one of the helpful Indieweb folks, but sure enough, the suggestion there, to use THE_REQUEST instead, worked.

So, Anyhoo

This gives you a very easy way to pull my content by whatever embedding method you prefer,e.g.:

<iframe style="width: 100%; height: 400px;" src="https://michaelkupietz.com/embed/?p=10887&title&tags"></iframe>

Which, being this very page, is displayed on the front end as:

I call this “The Infinity Iframe”.