https://d226lax1qjow5r.cloudfront.net/blog/blogposts/server-side-analytics-with-jamstack-sites/blog_nuxt_server-side-analytics_1200x600.png

Jamstackサイトによるサーバーサイド分析

最終更新日 November 30, 2020

所要時間:1 分

Jamstackのサイトにはバックエンドがない。そのため、アナリティクスを収集する機能がブロッカーに対して特に脆弱なのだ。この問題を解決しよう。

この例には Netlify 関数が含まれています。 Netlify リダイレクトルールが含まれています。

問題点

トラッカーやトラッキングピクセルは、ウェブサイトを訪れたりメールを開いたりしたユーザーの行動や訪問を捕捉するために設計されたHTMLコードです。ウェブサイトの利用状況や、時にはコンバージョンを追跡するのに便利です。

問題は、一部のトラッカーは動作が遅く、しばしば侵入的であるということだ。広告ブロッカーは、ウェブページのパフォーマンスを低下させる広告やトラッキング・ピクセルを止めるため、あるいはユーザー体験を向上させるために考案された。

副次的な効果として、多くのサイトオーナーが自分のサイトで何が有効で、何が有効でないのかがわからなくなってしまった。物理的な追跡特性は、例えば、サインアップのリンクに記事の識別子を追加して、サインアップがどこから発生したかを確認するなど、特定の指標を追跡するためにまだ使うことができる。

これでは、コンバージョンを決定するための重要な要件である、コンテンツが閲覧されているかどうかを正確に判断することはできない。

ソリューション

サーバーサイドアナリティクスは、ユーザーアクティビティを追跡する一般的な方法となっています。従来のアナリティクスのような範囲はありませんが(ページ上のインタラクションを簡単に追跡することはできません)、ユニークページビューのような重要な詳細を把握することができます。

ホスティングプラットフォーム NetlifyやCloudflare、Fastlyのようなエッジプロバイダーは、そのソリューションの一部としてサーバーサイドのアナリティクスを提供しています。しかし、分析のためにプロバイダーを使用する場合、その情報をどのように保管できるかが制限され、内部レポートが制限されることがよくあります。

そのため、独自のサーバーサイド分析を行いたい人もいる。そのために、Googleはいくつかの言語用のクイック・スタート・パッケージを用意している。 universal-analytics.

ここでは universal-analyticsとNetlify Functionを使います。

ネットリファイ機能

Netlify Functionsは基本的にAWSのLambdaファンクションであり、AWSは含まれていない。AWSの開発者エクスペリエンスには多くの不満があるが、Netlifyはユーザーエクスペリエンスをビジネスモデルに変えた。Netlify Functionsも例外ではなく、設定されたディレクトリにJavaScriptやGoを書き、数ステップで公開することができる。エンドポイントはファイル名やフォルダ名で導き出され、親アプリケーションの依存関係を使用することも、独自の責任を負うこともできます。

超シンプルな関数は次のようなものだ:

// functions/hello-world/index.js

exports.handler = (event, context) => {
  console.log('Only the server will see this!')

  return {
    statusCode: 200,
    body: 'Hello, world!',
  }
}

デプロイされると、このようなURLでアクセスできるようになる: https://your-app.netlify.app/.netlify/functions/hello-world

しかし、このように返事を返した後も続けることもできる:

// functions/hello-world/index.js

exports.handler = (event, context, callback) => {
  callback(null, {
    statusCode: 200,
    body: 'Hello, world!',
  })

  console.log('The server will still see this!')
}

さて、この機能を使ってGoogleにアナリティクスを送信することができる。

:関数に依存関係を追加する場合は、プラグインに @netlify/plugin-functions-install-coreプラグインを netlify.tomlプラグインを追加する必要があります。このプラグインは、関数がデプロイされるときに関数のすべての依存関係がインストールされることを保証します。

まず universal-analyticsをインストールする必要があるので、以下のコマンドを実行する前に関数のディレクトリにいることを確認してほしい。

npm install universal-analytics

Google AnalyticsのIDがあることを確認し、それを Netlify 環境変数.

さて、これを関数で使うことができる。

// functions/hello-world/index.js

const ua = require('universal-analytics')
const visitor = ua(process.env.GOOGLE_ANALYTICS_ID)

exports.handler = (event, context, callback) => {
  callback(null, {
    statusCode: 200,
    body: 'Hello, world!',
  })

  const { queryStringParameters: data } = event

  try {
    if (data) {
      visitor.pageview(data).send()
    }
  } catch (error) {
    console.log(error) // eslint-disable-line
  }
}

これで、ブラウザからURLのデータを直接グーグルに送ることができる: https://your-app.netlify.app/.netlify/functions/hello-world?dp=/my-custom-page

送信できるデータにはこれらのパラメータが含まれるが、これに限定されるわけではない:

{
  dp, // path e.g. /my-custom-page
  dt, // title of the page
  dh, // hostname e.g. https://netlify.com
  dr, // referrer e.g. https://netlify.com/as-a-referrer or /a-link
  ua, // user agent e.g. very obscure string meaning "chrome on mac"
  cs, // utm_source
  cm, // utm_medium
  cn, // utm_campaign
  ck, // utm_term
  cc, // utm_content
}

以下は 許容されるパラメータの完全なリスト.

イメージ」を追加する

ルーター・ミドルウェアやAJAXリクエストのようなものを使ってこのスクリプトを呼び出すだけで、多くの場合、まともなレポートには十分かもしれないが、それでもブラウザやブラウザの広告ブロッカーによってXHRリクエストと認識され、ブロックされる可能性がある。

私が使うことにした(典型的な過剰設計の)解決策は、トラッキングピクセルの方法に似ている。しかし、目に見える構造画像を返すので、広告ブロッカーは今のところ完全に無視している。

Screenshot of AdBlock Plus ignoring the tracker on Vonage LearnScreenshot of AdBlock Plus

Netlify関数からSVG画像を返し、それをimageタグを使ってページに配置します。

この画像を使ってみよう。

この画像の出典はこちら:

<svg id="svg1923" width="733" xmlns="http://www.w3.org/2000/svg" height="733">
<circle cy="366.5" cx="366.5" r="366.5"/>
<circle cy="366.5" cx="366.5" r="336.5" fill="#fede58"/>
<path d="m325 665c-121-21-194-115-212-233v-8l-25-1-1-18h481c6 13 10 27 13 41 13 94-38 146-114 193-45 23-93 29-142 26z"/>
<path d="m372 647c52-6 98-28 138-62 28-25 46-56 51-87 4-20 1-57-5-70l-423-1c-2 56 39 118 74 157 31 34 72 54 116 63 11 2 38 2 49 0z" fill="#871945"/>
<path d="m76 342c-13-26-13-57-9-85 6-27 18-52 35-68 21-20 50-23 77-18 15 4 28 12 39 23 18 17 30 40 36 67 4 20 4 41 0 60l-6 21z"/>
<path d="m234 323c5-6 6-40 2-58-3-16-4-16-10-10-14 14-38 14-52 0-15-18-12-41 6-55 3-3 5-5 5-6-1-4-22-8-34-7-42 4-57.6 40-66.2 77-3 17-1 53 4 59h145.2z" fill="#fff"/>
<path d="m378 343c-2-3-6-20-7-29-5-28-1-57 11-83 15-30 41-52 72-60 29-7 57 0 82 15 26 17 45 49 50 82 2 12 2 33 0 45-1 10-5 26-8 30z"/>
<path d="m565 324c4-5 5-34 4-50-2-14-6-24-8-24-1 0-3 2-6 5-17 17-47 13-58-9-7-16-4-31 8-43 4-4 7-8 7-9 0 0-4-2-8-3-51-17-105 20-115 80-3 15 0 43 3 53z" fill="#fff"/>
<path d="m504 590s-46 40-105 53c-66 15-114-7-114-7s14-76 93-95c76-18 126 49 126 49z" fill="#f9bedd"/>
</svg>

さて、この関数から画像を返す:

// functions/hello-world/index.js

const ua = require('universal-analytics')
const visitor = ua(process.env.GOOGLE_ANALYTICS_ID)

exports.handler = (event, context, callback) => {
  callback(null, {
    statusCode: 200,
    headers: {
      'Content-Type': 'image/svg+xml',
    },
    body: `<svg id="svg1923" width="733" xmlns="http://www.w3.org/2000/svg" height="733">
<circle cy="366.5" cx="366.5" r="366.5"/>
<circle cy="366.5" cx="366.5" r="336.5" fill="#fede58"/>
<path d="m325 665c-121-21-194-115-212-233v-8l-25-1-1-18h481c6 13 10 27 13 41 13 94-38 146-114 193-45 23-93 29-142 26z"/>
<path d="m372 647c52-6 98-28 138-62 28-25 46-56 51-87 4-20 1-57-5-70l-423-1c-2 56 39 118 74 157 31 34 72 54 116 63 11 2 38 2 49 0z" fill="#871945"/>
<path d="m76 342c-13-26-13-57-9-85 6-27 18-52 35-68 21-20 50-23 77-18 15 4 28 12 39 23 18 17 30 40 36 67 4 20 4 41 0 60l-6 21z"/>
<path d="m234 323c5-6 6-40 2-58-3-16-4-16-10-10-14 14-38 14-52 0-15-18-12-41 6-55 3-3 5-5 5-6-1-4-22-8-34-7-42 4-57.6 40-66.2 77-3 17-1 53 4 59h145.2z" fill="#fff"/>
<path d="m378 343c-2-3-6-20-7-29-5-28-1-57 11-83 15-30 41-52 72-60 29-7 57 0 82 15 26 17 45 49 50 82 2 12 2 33 0 45-1 10-5 26-8 30z"/>
<path d="m565 324c4-5 5-34 4-50-2-14-6-24-8-24-1 0-3 2-6 5-17 17-47 13-58-9-7-16-4-31 8-43 4-4 7-8 7-9 0 0-4-2-8-3-51-17-105 20-115 80-3 15 0 43 3 53z" fill="#fff"/>
<path d="m504 590s-46 40-105 53c-66 15-114-7-114-7s14-76 93-95c76-18 126 49 126 49z" fill="#f9bedd"/>
</svg>`,
  })

  const { queryStringParameters: data } = event

  try {
    if (data) {
      visitor.pageview(data).send()
    }
  } catch (error) {
    console.log(error) // eslint-disable-line
  }
}

先ほど使用したURLを使ってサイトに追加する:

<img 
  alt="An SVG file of a very smiley Emoji"
  title="An SVG Emoji"
  width="128" 
  src="/.netlify/functions/hello-world?dp=/my-custom-page"
/>

運が良ければ、こんな感じになるだろう:

Screenshot of the smiley included in pageAn image of a smiley

リダイレクト・ルール

だから、私の邪悪な計画のアイデアの最も悪賢い創意工夫の部分は、この次の部分かもしれない。広告ブロッカーが、クエリ文字列パラメータを持つ非画像URLを画像ソースとしてあまり怪しくないと感じ取り、とにかくブロックしてしまうかもしれないと少し妄想していたのだ。

入力 Netlify リダイレクト.

プロジェクトのルートで netlify.tomlを使えば、あるパスを別のパスでプロキシできる。

[[redirects]]
  from = "/images/smiley-face.svg"
  to = "/.netlify/functions/hello-world"
  status = 200
  force = true

これで、画像パスを使ってスマイリーフェイスを含めることができる。

<img 
  alt="An SVG file of a very smiley Emoji"
  title="An SVG Emoji"
  width="128" 
  src="/images/smiley-face.svg?dp=/my-custom-page"
/>

クエリー文字列は好きなようにエンコードできるが、関数内でデコードすることを忘れないでほしい。

結論

アナリティクスは、マーケティングやコンテンツクリエイターにとってスーパーパワーだ。収集したデータに基づいて目標を調整することで、コミュニティにより良いサービスを提供することができます。

トラッカーについては、プライバシーやスピードに関する懸念がしばしばあります。しかし、私たちがそうであると信じているように、あなたが誠実に行動している限り、アナリティクスは他の誰よりも視聴者に利益をもたらす。

これは、サーバーサイドのアナリティクス(ブロッカーはブロックできない)を実現するちょっとしたいい方法だ。

シェア:

https://a.storyblok.com/f/270183/250x250/451101b4f0/lukeoliff.png
Luke Oliffヴォネージの卒業生

フレンドリーな技術教育者、家族思い、多様性チャンピオン、たぶんちょっと議論しすぎ。元バックエンドエンジニア。JavaScript(フロントエンドまたはバックエンド)、素晴らしいVue.js、DevOps、DevSecOps、JamStackのことなら何でも。DEV.toのライター