[Hugo] Docsy テーマのタグ一覧ページを Noindex にする方法

Hugo で Docsy テーマのタグ一覧ページを Noindex にする方法

この記事では Docsy テーマを題材としているが、考え方はその他のテンプレートでも適用可能。ただし、Hugo の テーマはそれぞれで実装方法の特色があり、(例えば WordPress のように)共通部品が整っているわけではないので注意する必要がある。

Docsy で robots index や noindex を実現している箇所

https://github.com/google/docsy/blob/v0.5.1/layouts/partials/head.html

layouts/partials/head.html

{{ if and hugo.IsProduction (ne $outputFormat "print") -}}
<meta name="robots" content="index, follow">
{{ else -}}
<meta name="robots" content="noindex, nofollow">
{{ end -}}

layouts/partials/head.html は Docsy により生成された各種ページで共通的に用いられている head タグ内の構成データである。v0.5.1 (2022/11/03) 現在、noindex を柔軟に挙動を変更する手段はない。

修正の考え方

themes/ ディレクトリに Docsy テーマを直接配置していている場合は、直接上書きしてしまえば当然だが簡単に挙動を変えることができる。しかしこの場合、Docsy テーマのアップデート(バージョンアップ)時には毎回、独自にカスタマイズした内容を反映しなければならず作業工数が発生してしまうのが欠点。そのため Hugo のベストプラクティスとしては外部のテーマ ( themes/ ディレクトリ内のテーマ、あるいは Hugo module により読み込んだデータ)はオリジナルのまま変更せず、自身のサイト生成のためのローカルファイル( layouts/ )内に同名のファイルを配置して上書きするのが良い。

修正方法

Hugo テンプレートの読み込み順序の仕様を活用して、上書きしたい部分を選定する。

Hugo's Lookup Order
Hugo's Lookup Order
https://gohugo.io/templates/lookup-order/
Hugo searches for the layout to use for a given page in a well defined order, starting from the most specific.

今回の場合、Docsy オリジナルの latouts/partials/head.html を分解しなければならない。今後のメンテナンスを効率的にするためにも、 noindex 関連の処理は別ファイルに分離することとする。

1. Headから robots 関連タグのロジックを分離する

layouts/partials/head.html

  • {{ partial "noindex.html" . }} としている部分がミソ
{{- /*  Tag ページを Noindex にしたかったので Override  */ -}}
{{- /*  Original: https://github.com/google/docsy/tree/v0.5.1/layouts/partials  */ -}}
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
{{ hugo.Generator }}
{{ range .AlternativeOutputFormats -}}
<link rel="{{ .Rel }}" type="{{ .MediaType.Type }}" href="{{ .Permalink | safeURL }}">
{{ end -}}

{{ $outputFormat := partial "outputformat.html" . -}}

{{ partial "noindex.html" . }}

{{ partialCached "favicons.html" . }}
<title>
  {{- if .IsHome -}}
    {{ .Site.Title -}}
  {{ else -}}
    {{ with .Title }}{{ . }} | {{ end -}}
    {{ .Site.Title -}}
  {{ end -}}
</title>
<meta name="description" content="{{ template "partials/page-description.html" . }}">
{{ template "_internal/opengraph.html" . -}}
{{ template "_internal/schema.html" . -}}
{{ template "_internal/twitter_cards.html" . -}}
{{ partialCached "head-css.html" . "asdf" -}}
<script
  src="https://code.jquery.com/jquery-3.6.0.min.js"
  integrity="sha384-vtXRMe3mGCbOeY7l30aIg8H9p3GdeSe4IFlP6G8JMa7o7lXvnz3GFKzPxzJdPfGK"
  crossorigin="anonymous"></script>
{{ if .Site.Params.offlineSearch -}}
<script defer
  src="https://unpkg.com/[email protected]/lunr.min.js"
  integrity="sha384-203J0SNzyqHby3iU6hzvzltrWi/M41wOP5Gu+BiJMz5nwKykbkUx8Kp7iti0Lpli"
  crossorigin="anonymous"></script>
{{ end -}}

{{ if .Site.Params.prism_syntax_highlighting -}}
<link rel="stylesheet" href="{{ "css/prism.css" | relURL }}"/>
{{ end -}}

{{ partial "hooks/head-end.html" . -}}

{{/* To comply with GDPR, cookie consent scripts places in head-end must execute before Google Analytics is enabled */ -}}
{{ if hugo.IsProduction -}}
  {{ $enableGtagForUniversalAnalytics := not .Site.Params.disableGtagForUniversalAnalytics -}}
  {{ if (or $enableGtagForUniversalAnalytics (hasPrefix .Site.GoogleAnalytics "G-")) -}}
    {{ template "_internal/google_analytics_gtag.html" . -}}
  {{ else -}}
    {{ template "_internal/google_analytics_async.html" . -}}
  {{ end -}}
{{ end -}}

layouts/partials/noindex.html

  • {{ if or (eq .Params.Noindex true) (eq .Kind "section") (eq .Section "tags") }} としている部分で tagsnoindex, follow としている
  • この例では3つのOR条件を設定している
    • .Params.Noindex は、コンテンツ(記事)の FrontMatter で noindex: true をしている場合には、その記事のみを noindex にできる
    • eq .Kind "section" は、section のトップページ(記事の一覧を表示する部分)を noindex にしたいケース
    • eq .Section "tags" は、今回の目的である Tag 一覧ページで noindex にしたいケース
{{ if or (eq .Params.Noindex true) (eq .Kind "section") (eq .Section "tags") }}
  <meta name="robots" content="noindex, nofollow">
{{ else }}
  {{ if hugo.IsProduction }}
  <meta name="robots" content="index, follow">
  {{ else }}
  {{ "<!-- [DEBUG] meta robots is follow in Production -->" | safeHTML }}
  <meta name="robots" content="noindex, nofollow">
  {{ end }}
{{ end }}

上記コードをベースにして、除外したい条件を指定することで挙動を変更することができる。

その他、If文で判定したいパラメタの一例は以下の記事も参照。

Hugo
Hugo
https://fand.jp/docs/hugo/
Hugo関連のメモ

2. Tagページの Head を置き換える

前述「1」の対応により完了となるが、自身のカスタマイズの適用範囲を最小限にしたいケースも考えられる。例えば以下の例:

  • 複数のテーマを組み合わせて使っていて、Docsy デフォルトでは利用していない
  • サイト全体には影響を与えたくないので、タグ関連ページに限定して設定を反映したい

その場合は以下の対応とする。

  • layouts/partials/head.html として Docsy と同名で上書きしたファイルを、重複しない別ファイルに分離する。
    • 重複さえしなければファイル名は何でも問題ないが layouts/partials/docsy/head.html とするのがシンプルでよい
  • layouts ディレクトリ直下に tags ディレクトリを新規作成し、このディレクトリ内に上書きしたいテンプレートを配置する
    • layouts/tags/baseof.html を作成して以下のコンテンツを作成する
    • ベースとなるテンプレートは docsy のレポジトリから複製している。 <head> 内部で読み込んでいる partial を docsy/head.html としている
<!doctype html>
<html itemscope itemtype="http://schema.org/WebPage" lang="{{ .Site.Language.Lang }}" class="no-js">
  <head>
    {{ partial "docsy/head.html" . }}
  </head>
  <body class="td-{{ .Kind }}{{ with .Page.Params.body_class }} {{ . }}{{ end }}">
    <header>
      {{ partial "navbar.html" . }}
    </header>
    <div class="container-fluid td-default td-outer">
      <main role="main" class="td-main">
        {{ block "main" . }}{{ end }}
      </main>
      {{ partial "footer.html" . }}
    </div>
    {{ partialCached "scripts.html" . }}
  </body>
</html>

Original source: v0.5.1 https://github.com/google/docsy/blob/v0.5.1/layouts/partials/head.html

このファイルも Hugo のバージョンアップ時には変更を反映しなければならないが、コード量が少なく、更新頻度は低いと思われるので許容可能な範囲として妥協。

やはり Hugo’s Lookup Order の理解が重要なので、柔軟なカスタマイズのためには概要を抑えておきたいところ。