ComplianzのクッキーバナーをEU圏のユーザーにだけ表示し、日本からのアクセスでは一瞬も表示させない方法を解説します。キャッシュプラグインと完全に共存するJavaScript方式で、コードはコピペでそのまま使えます。
Complianzの基本設定については、TCDの記事が詳しく解説しています。ただし、そこで紹介されているPHPとGeoLite2データベースによる国判定は、WP RocketやLiteSpeed Cacheなどのキャッシュプラグインを使っている環境では正しく機能しません。
筆者のブログでもWP Fastest Cacheを使っていた際にこの問題に直面しました。最初のアクセス時にキャッシュされたHTMLが、別の国のユーザーにもそのまま配信されてしまい、国判定が意味をなさなくなるのです。
この記事では、クライアントサイドのJavaScriptで国判定を行うことで、キャッシュの影響を完全に回避する方法を紹介します。
図.日本からアクセス(バナー非表示)
図.EU圏からVPNでアクセス(バナー表示)
PHP方式がキャッシュ環境で破綻する理由
サーバーサイドのPHP判定は、ページがキャッシュされた時点で機能しなくなります。
たとえば、ドイツからのアクセスでページがキャッシュされると、バナー表示込みのHTMLが保存されます。次に日本からアクセスしても、サーバーはキャッシュ済みの同じHTML(バナー表示あり)を返します。逆のケースも同様で、日本のアクセスでキャッシュが生成されると、ドイツからアクセスしてもバナーが表示されません。
DONOTCACHEPAGE定数で回避する方法もありますが、キャッシュプラグインによっては無視されるケースがあります。つまり、サーバーサイドで国判定してHTMLを出し分ける方式は、キャッシュとは根本的に相性が悪いのです。
解決策はシンプルです。サーバーが返すHTMLを全ユーザー共通にし、国判定をクライアント側のJavaScriptで行います。これならキャッシュされたHTMLが誰に配信されても、ブラウザ上で正しく判定できます。
JavaScript方式の完成コード
functions.phpまたはCode Snippetsプラグインに追加してください。WP Rocket対応済み、タイムゾーンフォールバック付きです。
|
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 |
// 0. WP Rocket: GeoIP判定のJSを遅延対象から除外 add_filter('rocket_delay_js_exclusions', function($exclusions) { $exclusions[] = 'cmplz-geo-init'; return $exclusions; }); // 1. デフォルトでバナーを非表示(初回から一瞬も表示しない) add_action('wp_head', function() { echo '<style id="cmplz-geo-hide">#cmplz-cookiebanner-container{display:none !important;}</style>'; }, 0); // 2. 国判定:EU圏の場合のみバナーを表示 add_action('wp_footer', function() { ?> <script data-cfasync="false"> /* cmplz-geo-init */ (function() { var 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']; // EU圏のタイムゾーン(API全滅時のフォールバック判定用) var euTimezones = [ 'Europe/', 'Atlantic/Canary', 'Atlantic/Faroe', 'Atlantic/Madeira', 'Atlantic/Reykjavik', 'Arctic/Longyearbyen' ]; function isEuTimezone() { try { var tz = Intl.DateTimeFormat().resolvedOptions().timeZone || ''; for (var i = 0; i < euTimezones.length; i++) { if (tz.indexOf(euTimezones[i]) === 0) return true; } } catch(e) {} return false; } function showBanner() { var el = document.getElementById('cmplz-geo-hide'); if (el) el.remove(); } function setNonEuCookies() { document.cookie = "cmplz_consent_status=allow;path=/;max-age=31536000"; document.cookie = "cmplz_statistics=allow;path=/;max-age=31536000"; document.cookie = "cmplz_marketing=allow;path=/;max-age=31536000"; document.cookie = "cmplz_preferences=allow;path=/;max-age=31536000"; document.cookie = "cmplz_functional=allow;path=/;max-age=31536000"; } // すでに判定済みの場合(API呼び出しなし) if (document.cookie.indexOf('cmplz_geo_checked=1') !== -1) { if (document.cookie.indexOf('cmplz_geo_eu=1') !== -1) { showBanner(); } return; } function handleResult(isEu) { document.cookie = "cmplz_geo_checked=1;path=/;max-age=86400"; if (isEu) { document.cookie = "cmplz_geo_eu=1;path=/;max-age=86400"; showBanner(); } else { setNonEuCookies(); } } function handleCountry(country) { if (!country) { // 国コードが空: タイムゾーンでフォールバック handleResult(isEuTimezone()); return; } country = country.trim().toUpperCase(); handleResult(euCountries.indexOf(country) !== -1); } function tryGeoIP() { var controller = typeof AbortController !== 'undefined' ? new AbortController() : null; var timeout = setTimeout(function() { if (controller) controller.abort(); }, 3000); var opts = controller ? { signal: controller.signal } : {}; fetch('https://ipwho.is/', opts) .then(function(r) { return r.json(); }) .then(function(data) { clearTimeout(timeout); handleCountry(data.country_code); }) .catch(function() { clearTimeout(timeout); // フォールバックAPI fetch('https://api.country.is/') .then(function(r) { return r.json(); }) .then(function(data) { handleCountry(data.country); }) .catch(function() { // 全API失敗: タイムゾーンで判定 handleResult(isEuTimezone()); }); }); } tryGeoIP(); })(); </script> <?php }, 999); |
コードの仕組みを解説
処理の流れは「CSSでバナーを隠す → JSでEU圏か判定 → EU圏ならCSSを外してバナー表示」の3ステップです。
CSSでデフォルト非表示にする
|
1 |
echo '<style id="cmplz-geo-hide">#cmplz-cookiebanner-container{display:none !important;}</style>'; |
ページ読み込み直後、<head>内でバナーを非表示にするCSSを出力します。どの国からアクセスしても、初期状態ではバナーは見えません。id="cmplz-geo-hide"を付与しているのは、後からJavaScriptでこのstyle要素を削除できるようにするためです。
Cookieキャッシュで2回目以降はAPI不要
|
1 2 3 4 5 6 |
if (document.cookie.indexOf('cmplz_geo_checked=1') !== -1) { if (document.cookie.indexOf('cmplz_geo_eu=1') !== -1) { showBanner(); } return; } |
判定結果は24時間Cookieに保存されます。2回目以降のアクセスではAPIを呼ばずに即座に判定が完了するため、ページ表示速度への影響はありません。
GeoIP APIによる国判定(タイムアウト・フォールバック付き)
メインAPIのipwho.isに3秒のタイムアウトを設定し、失敗した場合はapi.country.isにフォールバックします。
両方のAPIが失敗した場合は、ブラウザのタイムゾーンで判定します。Intl.DateTimeFormat().resolvedOptions().timeZoneがEurope/で始まればEU圏、Asia/TokyoなどならEU圏外と判定します。この仕組みにより、WP Rocketの「Delay JavaScript execution」や広告ブロッカーがAPIをブロックしても、日本のユーザーにバナーが誤表示されることはありません。
WP Rocket対応
WP Rocketの「Delay JavaScript execution」は、すべてのJSをユーザー操作(スクロールやクリック)まで遅延させる機能です。バナー制御のJSが遅延されると、判定そのものが実行されなくなります。
|
1 2 3 4 |
add_filter('rocket_delay_js_exclusions', function($exclusions) { $exclusions[] = 'cmplz-geo-init'; return $exclusions; }); |
このフィルターで、GeoIP判定スクリプトを遅延対象から除外しています。scriptタグ内の/* cmplz-geo-init */コメントが除外キーワードとして機能します。data-cfasync="false"はCloudflare Rocket Loader向けの除外指定です。
EU圏の判定対象について
コードではEU加盟27ヶ国に加え、EEA加盟3ヶ国(アイスランド、リヒテンシュタイン、ノルウェー)を対象にしています。イギリスはBrexit後もUK-GDPRが適用されるため含めています。スイスも2023年9月施行のnDSG(新データ保護法)によりGDPRに準じた同意取得が求められるため、対象に加えています。不要であれば、配列から'GB'や'CH'を削除してください。
動作の流れ
日本からのアクセスではAPI判定・Cookieキャッシュどちらのルートでもバナーは非表示のまま。EU圏からのアクセスでのみバナーが表示されます。API全滅時もタイムゾーンで正しく判定されます。
| シナリオ | 動作 |
|---|---|
| 日本から初回アクセス | CSS非表示 → JS判定 → EU圏外 → 同意Cookie設定 → GA動作 |
| 日本から2回目以降 | CSS非表示 → Cookie確認 → EU圏外 → バナー非表示のまま(API呼び出しなし) |
| EU圏から初回アクセス | CSS非表示 → JS判定 → EU圏 → style削除 → バナー表示 |
| EU圏から2回目以降 | CSS非表示 → Cookie確認 → EU圏 → style削除 → バナー表示(API呼び出しなし) |
| API全滅(日本) | CSS非表示 → API失敗 → タイムゾーン判定 → Asia/Tokyo → EU圏外 → バナー非表示 |
| API全滅(EU圏) | CSS非表示 → API失敗 → タイムゾーン判定 → Europe/* → EU圏 → バナー表示 |
PHP方式との比較
JavaScript方式はキャッシュ完全対応・GeoLite2 DB不要・日本での初回バナー非表示と、キャッシュプラグインを使う環境ではすべての面でPHP方式を上回ります。
| 項目 | PHP方式 | JavaScript方式(本記事) |
|---|---|---|
| キャッシュ共存 | 問題あり | 完全対応 |
| 日本での初回表示 | 一瞬表示される場合あり | 一瞬も表示されない |
| GeoLite2 DB | 必要(定期更新も必要) | 不要 |
| サーバー負荷 | やや高い | 低い(クライアント処理) |
| 外部API依存 | なし | あり(フォールバック付き) |
| WP Rocket対応 | 不要 | 除外フィルター1行で対応 |
| API全滅時 | 影響なし(ローカルDB) | タイムゾーンで判定 |
キャッシュプラグインを使っている環境であれば、JavaScript方式一択です。
動作確認の手順
日本からとEU圏から(VPN使用)の2パターンで確認しましょう。
日本からの確認
- ブラウザのキャッシュとCookieをクリア
- サイトにアクセス
- バナーが表示されないことを確認
- DevTools(F12)のConsoleで以下を実行
|
1 |
document.cookie.includes('cmplz_statistics') |
trueと表示されれば正常です。
Google Analyticsの動作確認
DevToolsの「Network」タブを開き、フィルターにgoogleと入力してページをリロードします。googletagmanager.comへのリクエストが200または204で返ってくれば、Analyticsは正常に動作しています。
EU圏からの確認(VPN使用)
ProtonVPN(無料プランあり)やWindscribe(月間10GBまで無料)でEU圏のサーバーに接続し、Cookieをクリアしてからサイトにアクセスしてください。バナーが表示されれば成功です。
VPNで国を切り替えたのにバナーが表示されない場合は、前回の判定結果がCookieに残っています。以下をConsoleで実行してからリロードしてください。
|
1 2 3 |
document.cookie = "cmplz_geo_checked=;path=/;max-age=0"; document.cookie = "cmplz_geo_eu=;path=/;max-age=0"; location.reload(); |
注意点
運用にあたって、以下の3点に注意してください。
Complianzのバージョン:本記事のコードはComplianz 7.x系で動作確認しています。メジャーアップデートでCookie名が変更される可能性があるため、更新時はcmplz_系のCookie名を確認してください。
GeoIP APIのレート制限:ipwho.isは無料で利用できますが、高トラフィックサイトではレート制限に達する可能性があります。ただし、Cookieキャッシュにより実際のAPI呼び出しは初回訪問者のみに限定されるため、一般的なブログ規模であれば問題ありません。
WP Rocketのキャッシュクリア:コード設置後は必ずWP Rocketの「キャッシュをクリア」を実行してください。古いキャッシュにはこのスクリプトが含まれていないため、クリアしなければ動作しません。
まとめ
ComplianzのバナーをEU圏だけに表示するなら、キャッシュプラグインと共存できるJavaScript方式が最も確実です。
CSSでデフォルト非表示にしてからJSで国判定を行う方式のため、日本からのアクセスではバナーが一瞬も表示されません。GeoIP APIが全滅してもタイムゾーンでフォールバック判定するため、WP Rocketや広告ブロッカーの影響も受けません。判定結果はCookieにキャッシュされるので、2回目以降はAPI呼び出しゼロで即座に判定が完了します。








コメント