Pick Up
Tags
# 節税 # iDeCo # フリーランス # 個人事業主 # プログラミング # 筋トレ # MySQL # つみたてNISA # Node.js # Vue # Nuxt # Vuetify # Netlify # 確定申告 # OGP # プロテイン

自作ブログでOGPが出ない問題に直面した。

5/10/2021

# Vue # Nuxt # OGP

当サイトはNuxtで作成したブログです。
ブログサービスを作成するにあたり、見た目やロジックの実装を一通り終えた後、
SEO対策の一環でOGP(Twitterカード)の設定も入れたのですが、うまく動作しない問題に直面しました😱

色々トラブルシュートした結果、原因がわかったため共有します🙋‍♂️

基本的に短い記事を書くと宣言していますが、
今回の記事はちょっと長めになります🙇‍♂️

そもそもOGPとは?


OGPとは「Open Graph Protcol」の略で、
TwitterなどのSNSでシェアした際に、WEBページのタイトルや説明文、
イメージ画像などを正しく伝えるためのhtml要素のことです。

画像
↑こういうやつです。
Twitterをやってる方であれば、
タイムライン上で見かけたことがあるのではないでしょうか。

SNSでURLやブログ記事を共有する際に、
OGPの有無が、アクセス率に大きな差が出るので、
ブログサービスを自作するような場合には、確実に用意したいところです🏃‍♂️

そんなOGPが、うまく表示されてくれない問題が発生した!というお話になります。

OGPの設定方法


NuxtでのOGP設定自体は、そこまで難しくはありません。

ブログサービスの場合は、TOPページのベースとなるOGPの他に、
記事ごとの専用のOGPも用意できるのが理想的かと思います。

画像

↑記事ごとのOGPはこんな感じですね!

OGPの仕組みとしては、HTMLの<head>要素の中に、<meta>タグを定義し、
表示に必要な要素を記述することで、OGPが表示されることになります。
Nuxtの場合、これらの要素をNuxtの設定ファイルと、
記事表示用のvueファイルに記述することで設定が可能です👍

まず、TOPページのOGP設定や、全体の共通設定については、
nuxt.config.ts(.js)に記述します。

nuxt.config.ts
head: {
  meta: [
    { property: 'og:site_name', 'サイト名' },
    { hid: 'og:type', property: 'og:type', content: 'website' },
    { hid: 'og:url', property: 'og:url', content: 'サイトURL' },
    { hid: 'og:title', property: 'og:title', content: 'サイトタイトル' },
    { hid: 'og:description', property: 'og:description', content: 'サイトの説明' },
    { hid: 'og:image', property: 'og:image', content: '画像のパス(絶対パス)' },
    { name: 'twitter:card', content: 'Twitterカードのタイプ' },
    { name: 'twitter:creator', content: 'Twitterのアカウント名' }
  ]
}

head.metaプロパティを指定することで、
全ページ共通の<head>要素を設定することができます。

記事ごとのOGP設定については、記事表示用のvueファイルに定義します。

_slug.vue
export default {
  head () {
    return {
      meta: [
        // typeはTOPページと記事別でわけるため再定義
        {
          hid: 'og:type',
          property: 'og:type',
          content: 'article'
        },
        // 画像は記事ごとに用意するのでここで再定義
        {
          hid: 'og:image',
          property: 'og:image',
          content: `${this.$config.baseUrl}/ogp/article/${this.article.slug}.jpg`
        },
        // タイトルも記事専用のものを用意するので再定義
        {
          hid: 'og:title',
          property: 'og:title',
          content: this.article.title + ' | ' + this.$config.firstTitle
        },
        // 説明文も同じく再定義
        {
          hid: 'og:description',
          property: 'og:description',
          content: this.article.description
        },
        // 記事のURLが必要なので再定義
        {
          hid: 'og:url',
          property: 'og:url',
          content: `${this.$config.baseUrl}/blog/${this.article.slug}`
        }
      ]
    }
  }
}

特筆すべき点だけ確認していきます。

hid:


hid:は重複する要素に対して設定します。
TOPページ用の設定と、記事別の設定を見た時に、
og:typeなど、いくつかの要素が重複していることがわかります。
TOPページと記事ページでOGPの内容を変えたい場合はそれぞれで同じ要素を記述し、
content:に固有の値を記述することで実現できますが、hid:を設定しないとエラーとなります。

TOPページと記事ごとで要素を切り替えたい場合は、hid:を設定するようにしてください👍

og:type


ページの種類を指定する項目です。 共通設定の方ではwebsite、記事別の設定ではarticleを設定しましょう。

og:url


リンクのURLを設定する項目です。
TOPページと記事とでURLはそれぞれ設定するようにします。

og:title


OGPのタイトルに使用されます。
TOPページではサイト名、記事の方では記事名をセットするのがよろしいかと思います👍

og:description


サイトまたは記事の説明文を記載できます。
SNSなどによって表示できる文字数が異なりますが、
80-90文字程度にするのがよいとされているようです!

og:image


OGPにおいて重要な、画像の格納先を設定します。
TOPページ用と記事用でそれぞれ用意できるとよろしいかと思います👍

上記の例では、static/ogp/article/記事固有のパス配下に
記事別のOGP画像を配置して、記事ページにアクセスした際にその記事の画像を取得するようにしています。
static配下に保管したファイルはhttp://hoge.com/ogp/article~のような指定でアクセスできます🙋‍♂️

twitter:card


Twitter用の設定です。OGPの表示形式を指定します。
公式ガイドを見る限り、
summary、summary_large_image、app、playerの4パターンを指定できるようです。
私はsummary_large_imageを選択しています。
上に大きい画像、下にタイトルや説明文が載る形式ですね。

表示確認。。。


設定は終わったので、早速チェックしてみます。

chromeで見る限り、<meta>タグは正しく設定されているようなのでOK。

画像
OGPの表示確認は、
OGP確認
Twitter Card validator
といった便利サイトがあるのでこちらで確認してみると。。
画像
表示されない。。
WARN: No metatags found 「metaタグがありません」と言われている。
いや、OGP用の<meta>タグはちゃんとあったはずだが。。

一方で、TOPページの方はOGPが問題なく表示されることを確認。

画像

ということで、記事ごとのOGPが表示されない問題が発生orz
基本的にTOPページを共有することはあまりなく、
記事別のOGPを出すことが主目的なので、これはまずい状況です😱

トラブルシュート


実際にはもっといろいろなことを試したのですが、
やってみた結果ダメだった対処方法を参考までにご紹介。

Twitter用のOGPタグを追加してみる


公式ガイドに載っていないためおそらく現在は廃止されているのだと思いますが、
以下のようなアトリビュートがあるようだったので追記してみた。

_slug.vue
export default {
  head () {
    return {
      meta: [
        // <中略>
        {
          hid: 'twitter:description',
          name: 'twitter:description',
          content: this.article.description
        },
        {
          hid: 'twitter:image',
          name: 'twitter:image',
          content: `${this.$config.baseUrl}/ogp/article/${this.article.slug}.jpg`
        },
        {
          hid: 'twitter:url',
          name: 'twitter:url',
          content: `${this.$config.baseUrl}/blog/${this.article.slug}`
        }
      ]
    }
  }
}

なんだかそれっぽいので動いてくれるんじゃないかと思い再施行するもアウト。
同様にWARN: No metatags found が返却されてきました。

_redirectsファイルを置いてみる


調査の過程で、
_redirectsファイルを配置することで表示できたという情報もありました。
static配下に以下のようなファイルを作成することで、
存在しないURLへ遷移した場合に、TOPページへリダイレクトさせることができます。

static/_redirects
/* /index.html 200

その上で、再度記事ページのOGPをチェック。。

画像

表示された!けど、なぜかTOPページのOGPが表示されている。。
記事用のOGP画像や説明文はちゃんと用意しているので、これではダメですね😱

常にTOPページのOGPが表示されては困るので、_redirectsファイルはいったん削除。。

でもそもそも、何故記事ページのURLでTOPのOGPが表示されるのか。
存在しないURLへアクセスした時に、TOPページにリダイレクトされる処理のはず。。

記事ページが 「存在しない状態」 となっている??

辿り着いた原因


結論としては、前項の最後に書いた通り、
TOPページはあるけど、記事ページはない状態
となっていたことが原因でした。

このサイトはnuxt generateというコマンドでビルド・公開しています。
このコマンドは、pages配下のvueファイルから静的なHTMLファイルを生成して
静的ホスティング用のディレクトリをエクスポートしてくれるというものなのですが、
Nuxtの仕様で、 動的なルーティングは無視する というものがあります。

動的なルーティングとは、
今回の記事ページ_slug.vueのように、
記事共通で利用しつつ、URLはそれぞれ独自に規定できるような仕組みです。

動的ルーティングを利用したページについては、
TOPページなどから<nuxt-link>を通して遷移することはできますが、
直接そのページに遷移しようとすると、404エラーになります。

記事ページのURLを直接ブラウザにセットして遷移してみると、 404エラーとなりました。。(Netlifyを使っているので、このようなページになりました)

画像

ではどうするか、、、ですが、

動的ルーティングは、あらかじめ生成したいURL群が明確な状態であれば、
nuxt.config.ts(.js)へ設定を追記することで、静的ファイルを生成することができます。
これらの設定もちゃんと定義していたつもりだったのですが、ここがミスっていました💦

記事ページの静的ファイルを生成する


動的ルーティングの静的ファイル設定は、
generateプロパティに設定していきます。
間違っていた設定と修正後を掲載。

nuxt.config.ts
generate: {
  // 修正前
  async routes () {
    const { $content } = require('@nuxt/content')
    const files = await $content('articles').only(['path']).fetch()

    return files.map((file: any) => file.slug === '/index' ? '/' : file.path)
  }
  // 修正後
  async routes () {
    const { $content } = require('@nuxt/content')
    const files = await $content('articles').only(['slug']).fetch()

    return files.map((file: any) => file.slug === '/index' ? '/' : '/blog/' + file.slug)
  }
}

以下の2箇所が謝っていました
・取得項目
・URL設定

await $content('articles').only(['path']).fetch()

only(['path'])とすることで、データまるごとではなく、
指定した項目のみを取得することができます。
pathには対象のmarkdownファイルのpath情報が入っているのですが、
それをそのままURLの末尾にくっつけて静的ルーティングを作っちゃっています。

return files.map((file: any) => file.path === '/index' ? '/' : file.path)

markdownファイルを置いているpathが、
記事URLと一致するように設計されていればこれでOKなのですが、
私の場合は違っていたのでこれだとアウトでした😱
ここをonly(['slug'])(slugには拡張子抜きのファイル名がセットされています)
に修正し、/blog/ファイル名で静的ルーティングを生成するように修正しています。

return files.map((file: any) => file.slug === '/index' ? '/' : '/blog/' + file.slug)

この修正を入れた上で、再トライした結果、無事表示されました。。

ちなみに、 この時点で_redirectsファイルを復活させました。
存在しないリンクに対してはTOPにリダイレクトしてくれるので、記事ごとのOGPが正しく動作する場合にはいい仕事をしてくれます。

さいごに


お疲れ様でした。
Nuxtでブログを作成する際には、動的ルーティングのgenerate設定が必須だったということですね。
私の場合は設定自体は仕込んでいたものの、
謝った記述になっていたせいで調査に結構な時間を要しましたorz

headの設定を入れてもOGPがうまく表示されない場合は、

generateの設定が入っているか、入っていたとしても設定がミスっていないか

この点をぜひチェックしてみるようにしてください・・!

経験上、headgenerateさえ正しく設定できていれば、
OGPはちゃんと出てくれるはずです🙇‍♂️

目次