This is Part 2 of the blog engine series I am writing about. You can head to Part 1 if you haven't read it yet.
Link to this sectionRecap
- Build a base ember app
- Install sass compilers, markdown to json convertors, a11y addons, and jpeg to webp convertors
- Added ember-responsive-image to make images scale properly for mobile devices thereby fetching smaller sized assets.
Link to this sectionHosting images yourself
As mentioned in the previous post about images being one of the primary sources for my travel blogs, I had to take a call between using 3rd party services like Cloudinary or use images as is. Using images without compressing leads to downloading heavier assets in mobile devices. Some images that are of the size of 2000x2000 resolutions would take more time to download. In order to prevent this, I used
ember-responsive-image was a good start to use responsive images for different devices. This is more of a build time optimisation where, the images are converted to webp and into varying sizes. I felt doing a build time optimisation to convert images to webp and creating multiple sizes would eventually slow down my build. Thereby my content writing productive would go for a toss - where I need to wait for minutes for the entire build to be done to see the pages.
So I decided to opt for a runtime optimisation where I would need to generate images based on the sizes I ask for which is similar to Cloudinary. There is a brilliant tool for this purpose and its free - Thumbor!
If you haven't tried Thumbor yet, you should give it a spin. And Thumbor is build in Python that needs a server that can run Python scripts to run on like Heroku, GCP, etc.
Until now, I was relying on a paid hosting service that was on a "shared hosting" plan which meant I can only run PHP scripts in it. This was a huge limitation for me despite the hosting services being slow at times.
I cancelled my paid hosting account, and ran Thumbor on a Heroku instance. One of the security features provided by Thumbor is to make sure the requests are authenticated using SHA encryption and a thumbor.conf file that can be configured to allow only requests from an allowed domain. Here's a page that details on why you should have your thumbor service secured.
With a few SECRET KEYs configured in my Heroku environment, I had to setup SHA encryption for requests and allow only authorised domains from accessing my Heroku service that runs this service. This meant I had to do this encryption in node. And in order to do this, I used an NPM package called thumbor-url-builder which helps build the image's encryption based on size + url + SECRET KEY.
Well, this introduced a new problem, now that this npm package generates a new URL altogther, how does the blog's asset know that this is the path. Most importantly, when running a development build, I didn't want to be hitting the Heroku service everytime I refreshed the page.
So, a simple workaround of making the assets to point to
/assets/... path and the thumbor service's Heroku endpoint for production made much more sense. However, we still haven't addressed the problem of identifying the right assets in production. Generally, an ember template would find out the right asset based on the asset-map that keeps track of the asset and its location along with its fingerprint.
Link to this sectionRevisiting ember-responsive-image
ember-responsive-image had a way to solve this for a different use-case. So, I chose a similar approach where, I mention a configuration for choosing which location of images need to be picked, where is my thumbor service hosted, how should the assets be rendered and on what basis.
In order to solve the location of images, during build time of the blog engine, the meta information about the image's url which was built using
thumbor-url-builder will be collected and stored under a
<script> tag of the index.html.
Despite this meta configuration on index.html, the general
<img> tags do not know which image path to refer to. So, similar to
ember-responsive-image, a helper would solve this problem. Similarly, the assets need to be dynamically fetched for different devices based on their resolution/device width. This led me to writing a component that generates as series of
<source> tags that run through each configured size and sets the right URL.
This way, the build complexity of generating images is non-existent anymore. Perhaps, these helpers and components could be used by anyone who runs their own Thumbor service. Eventually, I moved the helper, components and the node script that takes care of meta config generation, into a separate addon.
Link to this sectionHosting
Now that my thumbor service was running on a Heroku free account, it was time to find something for the blog engine. I could've chosen Heroku for this, but considering the fact that Netlify was extremely easy to configure for the frontend projects and all I had to worry about was to set my domain or DNS, the rest was easily taken care by Netlify.
I set up all the git configurations and authorisations and I was quickly ready to deploy the frontend code.
Link to this sectionCaching
If you had noticed, Heroku and Netlify are both free. However, Heroku comes with a caveat. If you are on the free plan, your Dynos turn off when not in use. And the next time someone visits the page, the Dynos will start kicking in and then the successive requests would be faster. But, as a user, they don't care about Dynos being turned ON or OFF. All they want is to stay on the page if the content is delivered faster. If the images are going to take forever in the first load, they are going to leave.
But here's a multi-level cache layer that I implemented. Thanks to @SivaSubramanian's blog on Thumbor with CDN caching, I signed up for Cloudflare free account and pointed that to my host domain.
So these are the levels of caching policy that I ended up having for a blog 😃
Link to this sectionNew load
- Cloudflare Caching
- Netlify Caching
Link to this sectionWarm load (Loading after you already visited the blog once)
- Service Worker for all assets and index.html (I ended up removing them for over-caching)
- Disk/Memory Cache for image assets
- Cloudflare Caching
- Netlify Caching
And the score on Lighthouse is 100 except Performance. That is something I will be covering in this blog series focussed on Performant Ember Apps.
Link to this sectionMedium.com migration
All the content of this blog engine was initially hosted in medium.com which was extremely easy to write content on to. But since I had almost done my blog engine now, I had to move the content from medium to here. This introduces a problem - the problem of SEO breakage. Some of my blogs were hosted in 2015 onwards, which meant Google had indexed them on top of their search results. This means I would lose the search index ranking.
2 problems were in front of me now:
- How do I migrate content from medium.com to
*.mdformat that my blog engine is built on?
- How do I retain SEO and not break it?
Link to this section1. Migrating content from medium.com to .md file
An amazing package to migrate medium.com to md files called "medium-to-markdown" was in to help this process. Following the installation procedures, I had migrated my posts - close to 20 - to md files in a matter of 5 minutes.
All I had to do then was to clean up the formatting in the md file output. And push to master. And that post was live in 2 minutes - thanks to Netlify's auto deploy.
Link to this section2. Retaining SEO
Move content from medium to md file in almost same URL file name. For example: medium.com/xyz-1231 to mydomain.com/xyz.
Host my posts in the new domain. Validate the page for formating.
Instead of deleting the same post from medium.com, I just changed a few settings related to canonical references.
rel="canonical"is an interesting HTML attribute. To know more about the same, read here.
It makes the google/bing/yahoo search bots to understand that this post is a clone of an originally posted link elsewhere.
In order to do this on medium.com, simply navigate to the Post Settings > Advance Settings and look out for Customize canonical link and check the box and add the post link that is in the new domain something like below: