A Poor Man's Vue-SEO Solution

Suppose you are just starting off with your latest pet project. Vue enabled you to turn your idea into reality. And now you want the search engines to start crawling your brand new site. Of course the landing page is also built with Vue. For regular visitors this page loads fast and responds snappy, thanks to the optimized bundle webpack created for you. Only the web crawlers tend to prefer raw html and don't care what the page looks like visually.

Not willing to spend ages of your precious spare time on a full fledged SSR solution like nuxt or an almost even invasive prerenderer. Why not try to reuse the templates your end user is presented with a touch of NGINX magic added to the mix.

Render static HTML from Vue templates

Include Vue templates

First of all we need to teach Handlebars how to include external files

const handlebars = require('handlebars')
const fs = require('fs')
const path = require('path')

handlebars.registerHelper('importVueTemplate', (relativePath) => {
  const absolutePath = path.join(__dirname, relativePath)
  const source = fs.readFileSync(absolutePath, 'UTF-8')

  // Peel out the template content
  const regex = /^<template>([^]*)<\/template>/

  let matches = regex.exec(source)

  return matches[1]
})

Render markdown content

const handlebars = require('handlebars')
const fs = require('fs')
const path = require('path')
const marked = require('marked')

handlebars.registerHelper('renderMdFile', (relativePath) => {
  const absolutePath = path.join(__dirname, relativePath)
  const source = fs.readFileSync(absolutePath, 'UTF-8')

  return marked(source, {
    sanitize: true,
    breaks: true,
    gfm: true,
    tables: true
  })
})

Handlebars template

<!DOCTYPE html>
<html>
  <head>
    <title>dochub - share the knowledge</title>
    {{! put all of your SEO tags here }}
  </head>
  <body>

    <header>
      {{#importVueTemplate '../src/components/LandingHeader.vue'}}{{/importVueTemplate}}
    </header>

    <main>
      {{#importVueTemplate '../src/components/LandingPage.vue'}}{{/importVueTemplate}}
    </main>
  </body>
</html>

Compile to html in a build step

let source = fs.readFileSync(path.join(__dirname, `/index.hbs`), 'UTF-8')
let template = handlebars.compile(source)

fs.writeFileSync(path.join(__dirname, '../dist/crawlers/index.html'), template({}))

Serve static HTML to web spiders

Now we have to intercept web crawler traffic and redirect this to the static page we just created.

location / {

  root /usr/share/nginx/html;

  # Enable history mode for Vue Router
  try_files $uri $uri/ /index.html;

  # Checking for top 8 web crawlers (see https://www.keycdn.com/blog/web-crawlers)
  if ($http_user_agent ~* (googlebot|bingbot|slurp|duckduckbot|baiduspider|yandexbot|sogou|exabot)) {
      root /usr/share/nginx/html/crawlers;
  }
}

Kees de Kooter

.. as simple as possible, but not simpler.

Write your comment…

Nice tip! Could you tag this post with Vue.js so that it gets more visibility?

Screenshot 2019-06-11 at 2.27.55 PM.png

Consider it done!

Reply to this…