I'm working on project where I would like to directly embed PDFs onto web pages. We'll be using Google's PDF viewer as the embed tool and implementing via a macro.

Here is the macro as it stands now:

{% macro pdfEmbed(pdfLink, width="580", height="480") %}

    <iframe src="https://drive.google.com/viewerng/viewer?embedded=true&url={{ pdfLink }}" width="{{width}}px" height="{{height}}px"></iframe>

{% endmacro %}

For SEO purposes, I'd like to set a rel="canonical" on the PDF to point to the entry on which the PDF is embedded so Google knows to index the parent page and not the PDF. From what I've read, the general best practice for how to do this with embedded PDFs is via HTTP headers. See here for more detail: https://moz.com/blog/how-to-advanced-relcanonical-http-headers

I'm struggling with how to implement it in practice. I think the best method would be to use {% header %} tag within the macro but I'm not sure if there's a way to target that at just PDF files? If that doesn't work, .htaccess could be an option but then I lose the ability to dynamically set the canonical on the PDF based on the URL of the entry that houses the PDF.

What is the best way to do this in Craft? I’m running a Craft 3 site FYI.



So the problem you're running into is all about context. Twig operates in the context of the http request for the parent page; so any headers you add via Twig will be added to the page http header, which is not what you want.

This could be done server-side, as the article you reference mentions. But it becomes a bit more involved when we're dealing with dynamic content, which presumably we are if a CMS is in the mix.

Because what you need is something that will intercept the http requests for documents you want to affect the header of, and then set it based on the dynamic page where it's coming from.

You could accomplish this via a plugin by writing a controller that sets the headers and then returns the actual file. Then in your Twig code, you'd link to the controller not the PDF file.

If you're using SEOmatic for Craft CMS 3, you can use the built in seomatic.helper.seoFileLink() function to do all of this for you.

  • Thanks a lot for the reply, Andrew! I think you're right - the solution would need to be some sort of plugin, Craft 3 Yii module (I read your guide on this - super helpful, thank you!), or potentially with .htaccess using some combination of HTTP_HOST or Request_URI to dynamically set the canonical URL. Also, super cool feature for SEOmatic! That could make life a lot easier rather than having to build a plugin / module just for this purpose. I'll think about the best way to implement this based on your suggestions / different options. Really appreciate your help!
    – dpayne
    Mar 21 '18 at 17:49
  • Hey @andrew.welch, I've been messing around the with seoFileLink() function in SEOmatic and ran into a few issues with my use case. 1. The function causes the Google PDF preview to show a "no preview available" message, rather the PDF contents as it did before. Embedding via <embed> no longer shows the PDF either. 2. Clicking on links to the PDF that go through the function seem to always force a download, regardless of how I set INLINE (could be user error). Not sure if / when those are fixable but SEOmatic seems to be the best solution to solving this problem if those things can work.
    – dpayne
    Mar 22 '18 at 0:02
  • Do me a favor and file an issue with the code you're using, so I can test it? github.com/nystudio107/craft-seomatic/issues Mar 22 '18 at 3:14
  • So I completely refactored how the seoFileLink() function works in SEOmatic 3.0.0-beta.13 -- give it a whirl? github.com/nystudio107/craft-seomatic/releases/tag/… Mar 22 '18 at 14:31
  • Holy cow - looks like it works! It added the header link to the PDF and no longer forces a download from a link click. I'm going to experiment with the plugin some more but you may have gotten yourself a new customer :). Thanks so much!
    – dpayne
    Mar 22 '18 at 18:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.