Shopify Liquidcustomer.tags がタグ変更後5分間揺らぐ原因と公式推奨の回避策

はじめに

Shopifyのタグベースのアクセス制御を実装している、もしくは検討している方で、こんな悩みを抱えたことはないでしょうか。

  • Adminで顧客タグを変えた直後、Liquid{{ customer.tags }} が新旧の値で揺らぎ続ける
  • ハードリロードのたびに表示が変わる
  • 5〜10分待つと安定するが、その間のmerchant動作確認で混乱する
  • アプリ側の問題なのか、Shopify側の問題なのか切り分けがつかない

このような方のための記事です。

結論からいうと、これは Shopifyストアフロントが Cloudflare ベースのCDNで配信されている ことに起因する仕様で、Liquidでレンダリングされた customer.tags の出力がCDN層でキャッシュされているのが原因です。アプリ側で完全に制御できる範囲ではなく、Shopifyサポートからも仕様通りの動作という公式回答が出ています。

この記事では、現象の再現方法、Shopifyサポートから得られた原因の説明、そして公式に推奨される回避策(pre-hide パターン)の実装まで解説します。


現象:5分かかって安定する customer.tags

Shopify Adminで顧客タグを変更した直後、ストアフロントのLiquid{{ customer.tags }} を読むと、ハードリロードのたびに新旧の値がランダムに返ってきます。約5分経過するとすべてのリクエストが新しい値で安定する、という挙動です。

アプリ介在なしでの再現手順

実際にこの現象は アプリを一切介在させなくても、Liquid単体で再現します。テーマの任意のテンプレート(例: templates/page.liquid)に以下を追加してください。

<div style="position: fixed; top: 0; left: 0; background: yellow; z-index: 99999; padding: 10px; font-family: monospace;">
  customer.tags: {{ customer.tags | json }}<br>
  customer.id: {{ customer.id }}<br>
  時刻: {{ "now" | date: "%H:%M:%S" }}
</div>

ログイン状態の顧客でページを開きながら、Adminで同じ顧客のタグを1回だけ変更します。直後からハードリロードを繰り返すと、新旧の値が交互に返ってきます。

観測結果

実際にVIPタグを1回だけ削除した直後の挙動が以下です。

19:28:09  ["Login with Shop","Shop","VIP"]
19:28:29  ["Login with Shop","Shop"]        ← 反映
19:28:48  ["Login with Shop","Shop","VIP"]  ← 復活
19:29:05  ["Login with Shop","Shop"]
19:29:25  ["Login with Shop","Shop","VIP"]  ← 再び復活

1分16秒で4回フリップ、約5分後にVIP無しで安定。Adminから見ると変更は即座に完了しているのに、ストアフロント側だけが揺らぎ続けます。

ハードリロードのたびにタグ表示が切り替わる様子
ハードリロードのたびにタグ表示が切り替わる様子
ハードリロードのたびにタグ表示が切り替わる様子
ハードリロードのたびにタグ表示が切り替わる様子

原因:CloudflareベースのCDNキャッシュ

Shopifyサポートから得た公式回答によると、これは不具合ではなく 仕様通りの動作 です。

整理すると以下のとおりです。

  • Shopifyストアフロントは Cloudflareベースの CDN で配信されている
  • 複数のエッジノードに分散してキャッシュされる
  • Liquid{{ customer.tags }} がレンダリングされた出力CDN層でキャッシュされる
  • Adminでタグを変更しても、すべてのエッジノードが同時にキャッシュを無効化するわけではない
  • 全ノードが再検証されるまで、古い値と新しい値が交互に表示される
  • 約5分のウィンドウは CDNの標準TTL動作 と一致する

つまり、ストアフロントが返してくる customer.tags は「ログインしている顧客の現在のタグ」ではなく、「直近にどこかのエッジノードでLiquidが評価された時点のタグがキャッシュされたもの」 ということになります。

Cloudflare CDNキャッシュの挙動解説
Cloudflare CDNキャッシュの挙動解説

確認した事実

確認項目

結果

アプリ介在なしで再現

Liquid単体で発生

Admin UI / GraphQL tagsAdd 経路

どちらでも同じ

Login with Shop / メール+コード

どちらでも同じ

Admin API直接取得

即時に新しい値が返る

揺らぎが収束する時間

約5分

特に重要なのが、Admin APIは即時に新しい値を返す という点です。

つまりサーバーサイドのデータは正しく更新されており、揺らぎが起きるのはストアフロントLiquidのレイヤーだけ。

ここが回避策の出発点になります。


公式に推奨される回避策:pre-hide パターン

Shopifyサポートからの推奨アプローチは以下のとおりです。

サーバー側でレンダリングされたLiquidの値に依存するのではなく、ページ読み込み後にApp Proxyを通じてクライアント側でタグを取得する。App Proxyのレスポンスに基づき、JavaScriptを使用してUI要素を制御する。クライアント側の確認が完了した後にのみ制限コンテンツをレンダリングすることで、表示の切り替わりを完全に回避できます。

具体的なフローは次のようになります。

  1. ページ初期表示時、コンテンツを visibility: hidden で隠す(CSS
  2. App Proxy経由でAdmin APIを叩き、最新の顧客タグを取得
  3. レスポンスに応じてロック画面 or コンテンツを表示
  4. その時点で初めて visibility を解除

これにより、Liquidのキャッシュに依存せず即時反映され、レンダリング後の表示切り替え(ちらつき)も発生しません。

代償として初期表示までApp Proxyのラウンドトリップ分(数百ms)の遅延は出ます。

Shopify CDN キャッシュ回避策ガイド
Shopify CDN キャッシュ回避策ガイド

実装イメージ

App Proxy 側のレスポンス(例: /apps/your-app/customer-tags)で、Adminから取得した最新タグを返すエンドポイントを用意します。

テーマ側ではセクション全体をpre-hideしておきます。

<div id="gated-content" style="visibility: hidden;">
  {% comment %} 制限コンテンツ {% endcomment %}
</div>

<script>
  fetch('/apps/your-app/customer-tags')
    .then(res => res.json())
    .then(({ tags }) => {
      const allowed = tags.includes('VIP');
      const el = document.getElementById('gated-content');
      if (allowed) {
        el.style.visibility = 'visible';
      } else {
        el.outerHTML = '<div class="locked">VIP会員限定コンテンツです</div>';
      }
    });
</script>

ポイントは、Liquidcustomer.tags を一切評価していない こと。

サーバーサイドレンダリング時点では「全員ロック状態」のHTMLを返し、クライアント側でApp Proxy経由のリアルタイム判定だけを信頼する設計です。


設計上の3つの選択肢

タグベースのアクセス制御を実装する際、選択肢は実質3つあります。

方式

即時性

UX

実装コスト

Liquidのみ

5分待ち

ちらつきなし

App Proxy後にDOM補正

即時

ちらつき有

App Proxy後に表示開始(pre-hide

即時

ロード待ち有

実務で重要なのが、Shopifyが公式に推奨しているのは3番目の pre-hide方式 だという点です。

Liquidのみ」方式は実装は楽ですが、merchantが動作確認したときに「タグ変えたのにロックが解除されない」と問い合わせが来ます。

実際にロック系アプリ(Locksmithなど)が「タグ反映に5〜10秒かかります」と謳っているのはbest caseの数字で、実態は5分かかることもあるという認識を持っておくと安全です。

DOM補正」方式は、初期表示が早い代わりに、ロックがかかった瞬間に既に内容が見えてしまうリスクがあります。

本気のアクセス制御では使えません。


merchant向けの告知文言

公式に推奨される文言は存在しないので、設定画面やドキュメントに次のような注意書きを入れておくのが現実的です。

顧客のタグが変更された場合、Shopifyストアへの安定反映は5分程度かかります。

その間、ロックが安定しない場合があります。

これはShopifyプラットフォームのCDNキャッシュによる仕様で、アプリ側での完全な制御範囲外です。

merchantが「タグを変更してすぐ確認 → 揺らぎを目撃 → 不具合だと思って問い合わせ」のループに入る前に、この説明を仕込んでおくとサポート工数が大幅に削減できます。


教訓:Shopifyストアフロントは動的データもCDNキャッシュされている

この調査でハッキリしたのは、Shopifyストアフロントの動的データは CDNレイヤーでキャッシュされている という事実です。

これは公式ドキュメント上ほぼ言及されていない仕様で、サポートに問い合わせて初めて確認できる類の情報です。

押さえておきたいポイントは3つあります。

  • customer.tags のような頻繁に変更される値を、Liquidで評価して即時反映 を期待してはいけない
  • 即時性が必要な機能は、App Proxy + Admin API + クライアント側レンダリング が公式推奨パターン
  • このCDNキャッシュ仕様はLocksmithなど既存ロックアプリ含めて全員が直面している共通の制約

実際にRYXShopifyアプリ開発でもこの現象に遭遇し、Shopifyサポートとのやり取りを通じて公式回答を得たうえでpre-hideパターンに切り替えました。タグベース制御アプリを実装している方は、最初からこの設計で組んでおくと無駄なデバッグ時間をゼロにできます。


さいごに

Shopifyストアフロントの動的データがCDNキャッシュされている事実は、ドキュメントを読んでいるだけでは絶対に到達できない情報です。

タグベースの制御に限らず、customer.metafields や顧客固有の動的表示にも同じ制約が当てはまるはずなので、即時性が要件にある場合はサーバーサイドLiquidに頼らずクライアントサイドでApp Proxy経由の判定 を最初から設計に組み込むのが安全です。

このブログでは他にもShopifyアプリ開発の実装記録や、海外ドキュメントから読み取れない実務知見を公開していますので、あわせてご覧いただければ幸いです。

ご希望に合わせた実装については、お問い合わせからお気軽にご相談ください。