GDPR対応のためにComplianzを入れた直後、スマホのメニューがおかしくなりました。
ハンバーガーメニューを開いてリンクをタップし、ブラウザの戻るボタンを押すと、メニューが画面を覆ったまま前のページが表示されます。iPhoneのSafariでも、AndroidのChromeでも同じでした。Complianzを無効化すると、その場で直ります。
ここから、Complianzの設定でどうにかしようとして、代わりのプラグインを探して、最後は同意バナーを自分で書くことになりました。
前半は直らなかった記録、後半は自作したバナーのコードと仕組みです。
この記事でわかること
ComplianzとCocoonの組み合わせで、ブラウザバック時にスマホメニューが閉じなくなる症状は、無料版で触れる設定の範囲では直せませんでした。
代わりに、GeoIP判定(フォールバック付き)、Google Consent Modeへの連携、WP Consent API対応を入れたGDPRバナーをfunctions.phpに書いて置き換えています。コードは記事の後半にそのまま載せています。
検証環境:WordPress 6.8.3 / Cocoon 2.8.9 / Complianz 7.4.5 / Xserver / PHP 8.3.21(2026年1月時点の記録です。現行の各バージョンでは再確認していません)
戻るボタンでメニューが開いたまま戻ってくる
本来なら、メニューを開いてリンクをタップし、戻るボタンで戻ってくると、メニューは閉じた状態で前のページが表示されます。
Complianzが有効だと、戻ってきた時点でメニューが開いたままになっていて、ページの中身が読めません。読者は毎回、自分でメニューを閉じる必要があります。下の1枚目がその状態で、2枚目がComplianzを無効化したときの同じ操作の結果です。


再現した条件は次のとおりです。
| 項目 | 内容 |
|---|---|
| テーマ | Cocoon |
| プラグイン | Complianz GDPR/CCPA Cookie Consent(無料版) |
| 端末・ブラウザ | iPhone Safari、Android Chrome(どちらでも再現) |
| 操作 | ハンバーガーメニュー → リンクをタップ → ブラウザの戻る |
最初に疑ったのは、自分でCode Snippetsに追加していたGeoIP判定のスニペットでした。これを無効化しても症状は変わらず、Complianz本体を無効化したところで直りました。
とはいえComplianzは広く使われているプラグインで、どのテーマでもこうなるなら騒ぎになっているはずです。Complianz単体のバグというより、Cocoonとの組み合わせで起きている競合だろう、と見当をつけました。
両方のソースを読み切る時間はなかったので、原因の候補を3つ立てて、順に潰すことにしました。1つ目はJavaScriptで、Complianzが解析系スクリプトをブロックする仕組みが、Cocoonのメニュー開閉のスクリプトにまで及んでいる可能性です。2つ目はCSSで、Complianzのバナーやモーダル用のスタイルが、z-indexやposition、displayのあたりでメニューに干渉している可能性。3つ目はbfcache(戻る操作を速くするためにページの状態を丸ごと保持するブラウザの仕組み)で、戻ってきたときの状態の復元にComplianz側の処理が絡んでいる可能性です。
Complianzの中で試したことと、代わりのプラグイン
1つ目の仮説が当たっていれば、Cocoon関連のスクリプトをブロック対象から外せば直るはずです。Complianzには cmplz_whitelisted_script_tags というフィルターがあるので、これで除外を指定しました。

| 1 2 3 4 5 6 | add_filter('cmplz_whitelisted_script_tags', function($tags) { $tags[] = 'cocoon'; $tags[] = 'javascript.js'; $tags[] = 'jquery'; return $tags; }); |
キーワードを何通りか変えましたが、症状は変わりませんでした。
次に、スクリプトの除外を細かく制御できるという「Script Center」を探しました。設定画面にそれらしいタブが見当たらず、調べるとPremium(有料版)限定の機能でした。この症状ひとつのために年間ライセンスを払うのはためらわれて、ここは見送っています。
無料版で触れる設定は、統合機能(Integration)の無効化、スクリプトブロックの設定、バナーを出すタイミング、キャッシュ関連と、ひととおり切り替えて確かめました。どれも効かず、この時点でComplianzで直すのはあきらめました。
代わりのプラグインも調べました。条件は、無料で使えること、Site KitのConsent Modeとつながること、Cocoonで同じことが起きないことです。
| プラグイン | よかった点 | 引っかかった点 |
|---|---|---|
| CookieYes | Site Kitのドキュメントで案内されている | 無料プランは月5,000PVまでで、このブログでは足りない |
| GDPR Cookie Compliance | Site Kitとの統合機能がある | 統合は有料アドオン |
| Cookiebot | 企業向けでConsent Modeに対応 | フル機能は有料プラン |
無料の範囲ではどれも足りない部分があり、そのうえ、入れ替えたところでCocoonと相性が悪くない保証はありません。プラグインを替えて同じ確認を繰り返すより、Cookieの同意バナーくらいなら自分で書いたほうが早い、と考えを切り替えました。
GDPRバナーを自作する
同意バナーに要る機能を書き出すと、思っていたより少なく済みます。EU圏からのアクセスかを判定する、バナーを出して同意か拒否かを選んでもらう、選んだ結果をCookieに保存する、Google Consent Modeに同意状態を伝える、の4つです。
自作ならスクリプトをブロックする仕組み自体がないので、テーマの動作に割り込むものがありません。PV制限もなく、文言や見た目も自由に変えられ、何かあったときに原因を追うのも自分のコードの中だけで済みます。
スマホで表示すると下のようになります。

functions.php(子テーマ)か、Code Snippetsプラグインに貼って使います。バナーのHTMLを優先度5、判定と同意管理のスクリプトを優先度999で、どちらも wp_footer に出しています。
最初に書いたコードから、バナーの英文だけ差し替えています。もとは「閲覧を続けると同意したものとみなす」という文で、GDPRの同意としては弱いと判断したためです。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | // ===================================================== // GDPR自作バナー(Site Kit Consent Mode対応) // ===================================================== // 1. バナーHTML + CSS add_action('wp_footer', function() { ?> <div id="gdpr-banner" style="display:none; position:fixed; bottom:0; left:0; right:0; background:#1a1a2e; color:#fff; padding:20px; z-index:999999; box-shadow:0 -2px 10px rgba(0,0,0,0.3);"> <div style="max-width:1200px; margin:0 auto; display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:15px;"> <p style="margin:0; flex:1; min-width:280px; font-size:14px; line-height:1.6;"> We use cookies for analytics and advertising only if you accept. You can change your choice at any time. <a href="/privacy-policy-en/" style="color:#6eb5ff; text-decoration:underline;">Privacy Policy</a> </p> <div style="display:flex; gap:10px; flex-wrap:wrap;"> <button id="gdpr-accept" style="background:#4CAF50; color:#fff; border:none; padding:12px 24px; cursor:pointer; border-radius:4px; font-size:14px; font-weight:bold;">Accept</button> <button id="gdpr-decline" style="background:#666; color:#fff; border:none; padding:12px 24px; cursor:pointer; border-radius:4px; font-size:14px;">Decline</button> </div> </div> </div> <div id="gdpr-settings-link" style="display:none; position:fixed; bottom:20px; left:20px; z-index:999998;"> <button id="gdpr-open-settings" style="background:#1a1a2e; color:#fff; border:none; padding:10px 16px; cursor:pointer; border-radius:20px; font-size:12px; box-shadow:0 2px 8px rgba(0,0,0,0.3);"> Cookie Settings </button> </div> <?php }, 5); // 2. JavaScript(国判定 + 同意管理) add_action('wp_footer', function() { ?> <script> (function() { const euCountries = ['AT','BE','BG','HR','CY','CZ','DK','EE','FI','FR','DE','GR','HU','IE','IT','LV','LT','LU','MT','NL','PL','PT','RO','SK','SI','ES','SE','IS','LI','NO','GB','CH']; const banner = document.getElementById('gdpr-banner'); const acceptBtn = document.getElementById('gdpr-accept'); const declineBtn = document.getElementById('gdpr-decline'); const settingsLink = document.getElementById('gdpr-settings-link'); const openSettingsBtn = document.getElementById('gdpr-open-settings'); let isEuUser = false; function updateConsent(granted) { const status = granted ? 'granted' : 'denied'; if (typeof gtag === 'function') { gtag('consent', 'update', { 'analytics_storage': status, 'ad_storage': status, 'ad_user_data': status, 'ad_personalization': status }); } if (typeof wp_set_consent === 'function') { wp_set_consent('statistics', granted ? 'allow' : 'deny'); wp_set_consent('marketing', granted ? 'allow' : 'deny'); } } function saveConsent(granted) { const value = granted ? 'granted' : 'denied'; document.cookie = "gdpr_consent=" + value + ";path=/;max-age=31536000;SameSite=Lax"; updateConsent(granted); banner.style.display = 'none'; if (isEuUser) { settingsLink.style.display = 'block'; } } function getConsent() { const match = document.cookie.match(/gdpr_consent=(granted|denied)/); return match ? match[1] : null; } function showBanner() { banner.style.display = 'block'; settingsLink.style.display = 'none'; } acceptBtn.addEventListener('click', () => saveConsent(true)); declineBtn.addEventListener('click', () => saveConsent(false)); openSettingsBtn.addEventListener('click', showBanner); const hasNonEu = document.cookie.includes('geo_region=non-eu'); const hasEu = document.cookie.includes('geo_region=eu'); const existingConsent = getConsent(); if (hasNonEu) { if (!existingConsent) { saveConsent(true); } else { updateConsent(existingConsent === 'granted'); } return; } if (hasEu) { isEuUser = true; if (existingConsent) { updateConsent(existingConsent === 'granted'); settingsLink.style.display = 'block'; } else { showBanner(); } return; } function tryGeoIP() { fetch('https://ipwho.is/') .then(r => r.json()) .then(data => { handleCountry(data.country_code); }) .catch(() => { fetch('https://api.country.is/') .then(r => r.json()) .then(data => { handleCountry(data.country); }) .catch(() => { isEuUser = true; showBanner(); }); }); } function handleCountry(country) { if (!country) { isEuUser = true; showBanner(); return; } country = country.trim().toUpperCase(); if (euCountries.includes(country)) { document.cookie = "geo_region=eu;path=/;max-age=86400;SameSite=Lax"; isEuUser = true; showBanner(); } else { document.cookie = "geo_region=non-eu;path=/;max-age=86400;SameSite=Lax"; saveConsent(true); } } tryGeoIP(); })(); </script> <?php }, 999); |
コードの読みどころ
バナーのCSSはすべてインラインで書いています。Cocoonのスタイルシートとぶつからないようにするためです。並びはFlexboxで flex-wrap: wrap を付けているので、スマホでは自動で折り返します。初期状態は display: none で、スクリプト側が国を判定してからEU圏のときだけ表示します。
国の判定には無料のGeoIP APIを2つ使っています。ipwho.isが失敗したらapi.country.isに切り替え、両方とも失敗したときや国コードが取れなかったときは、EU圏とみなしてバナーを出します。判定に失敗したらバナーを出す側に倒す、という順番です。
判定の対象は euCountries の配列で、EU加盟27か国に、EEAのアイスランド・リヒテンシュタイン・ノルウェー、UK GDPRのあるイギリス、独自のデータ保護法があるスイスを足しています。
同意を撤回できるように、同意か拒否を選んだあともEU圏の読者には左下に「Cookie Settings」ボタンを出し、押すとバナーがもう一度開きます。

状態は2つのCookieで持っています。
| Cookie | 値 | 有効期限 | 役割 |
|---|---|---|---|
geo_region | eu / non-eu | 24時間 | 国判定の結果。残っている間はAPIを呼ばない |
gdpr_consent | granted / denied | 1年 | 同意か拒否か |
geo_region が残っている間は判定をやり直さないので、APIへの問い合わせは読者1人につき24時間に1回までです。DevToolsで見ると、2つのCookieが並んでいるのが確認できます。

EU圏外と判定した読者には、自動で gdpr_consent=granted を入れています。これはGDPRの対象外として扱うという、このサイトの判断です。米国の州法やブラジルのLGPDのように、地域ごとに別のルールがあるので、EU圏外なら何をしてもよいという意味ではありません。読者が多い国に合わせて、ここは各自で決めてください。
同意の状態は updateConsent() の中で、gtag('consent', 'update', …) を使ってGoogleに伝えています。analytics_storage、ad_storage、ad_user_data、ad_personalizationの4つを、同意ならgranted、拒否ならdeniedにそろえます。同じ関数の中で wp_set_consent() も呼んでいて、こちらはWP Consent API経由でSite Kitに同じ状態を渡すためのものです。このコードが送るのはupdateだけで、初期値(default)はSite Kit側のConsent Modeが設定する前提にしています。
Site Kitとの連携と動作確認
Site Kit側は、Consent Modeを有効にしてWP Consent APIプラグインを入れれば連携できます。管理画面の「Site Kit」→「Settings」→「Admin Settings」でConsent Modeをオンにし、「WP Consent API」プラグインをインストールして有効化します。

これで、自作バナーで選んだ状態がSite Kit経由でGoogle Analyticsに届きます。Site KitでConsent API関連の警告が出たときの話は「Site KitでConsent API警告が出た話」にまとめています。
動作確認は、日本から見た場合と、VPNでEU圏から見た場合の2通りで行いました。どちらも、始める前にCookieを消しておくのが前提です。日本からは、Cookie(とくに geo_region と gdpr_consent)を消してサイトを開き、バナーが出ないことを見ます。DevTools(F12)のConsoleで document.cookie.includes('gdpr_consent') を実行して true が返れば、自動で同意が入っています。
EU圏からは、ProtonVPN(無料プランあり)やWindscribe(無料枠あり)でドイツやイタリアのサーバーにつないでから開きます。バナーが出ること、Acceptでバナーが消えて左下にCookie Settingsが出ること、それを押すとバナーがまた開くこと、Declineで拒否できることを順に確かめます。
VPNで国を切り替えたのにバナーが出ないときは、前回の geo_region=non-eu が残っています。Consoleで次を実行するとCookieが消えて再読み込みされます。
| 1 2 3 | document.cookie = "geo_region=;path=/;max-age=0"; document.cookie = "gdpr_consent=;path=/;max-age=0"; location.reload(); |
自分のサイトに合わせて変えるところは、だいたい次の3か所です。
| 変えたいもの | コードの場所 |
|---|---|
| 色 | background:#1a1a2e(バナー背景)、background:#4CAF50(Accept)、background:#666(Decline) |
| プライバシーポリシーのURL | href="/privacy-policy-en/" |
| 対象の国 | euCountries の配列。たとえばイギリスを外すなら 'GB' を消す |
自作で引き受けること
プラグインをやめた分、プラグインの開発者が肩代わりしてくれていたことは自分で持つことになります。
まず外部APIへの依存です。国判定はipwho.isとapi.country.isに頼っているので、どちらかのサービスが止まったり仕様が変わったりすれば、コードを直す必要があります。2段構えにしているので1つが落ちても判定は続き、両方落ちてもバナーを出す側に倒れますが、無料APIの利用条件やレート制限は各サービスの規約で確認しておいてください。判定結果をCookieに残すので問い合わせは読者の数に近い回数で済みますが、アクセスの多いサイトでは上限に当たる可能性があります。
次に、国を判定するために、同意を得る前の時点で読者のブラウザから外部APIへ問い合わせが飛ぶことです。IPアドレスが第三者のサービスに渡るので、プライバシーポリシーにはこの問い合わせのことも書いておく必要があります。
最後に、GDPRまわりの要件が変わったときやバグが見つかったときに、直すのは自分です。プラグインならアップデートが届きますが、自作にはそれがありません。それでも、競合の原因を追ってプラグインの中を何時間も探るより、全部の仕組みが自分のコードの中にある状態のほうが、このサイトには合っていました。
この記事のコードは、法的な助言ではなく、このサイトでの実装例です。どの国の読者にどこまで同意を求めるかは、サイトの運営者ごとに判断が必要です。
Complianzを外して自作バナーに置き換えたあと、スマホでもう一度、メニューを開いてリンクをタップし、戻るボタンを押しました。
前のページは、メニューが閉じた状態で戻ってきました。



コメント