WordPressでCookie同意バナーを自作する方法|GA4を同意後に読み込む安全な実装例

WordPressサイトに「Cookieを利用しています」という案内を表示し、ユーザーが同意した場合だけGoogle Analytics 4(GA4)を読み込みたいことがあります。この記事では、外部のCookie同意プラグインを使わず、WordPress用の小さな専用プラグインを作成して、「同意する」「拒否する」「Cookie設定を変更する」機能を実装する方法を紹介します。

今回の実装では、初回アクセス時にはGA4を読み込まず、「同意する」を押した場合だけGA4を読み込みます。「拒否する」を選択した場合は、このプラグインからGA4を読み込みません。ユーザーの選択はブラウザの localStorage に保存し、サーバー側にはランダムな同意ID・同意状態・日時だけをログとして保存します。

重要:この記事のコードは、すべてのCookieや外部サービスを自動的に制御するCMP(Consent Management Platform)ではありません。このプラグインから読み込むGA4だけを制御する簡易的な実装例です。Google Tag Manager、Google広告、YouTube、Meta Pixel、他のプラグインなどから別途スクリプトを読み込んでいる場合、それらは別途対応が必要です。

1 今回実装する機能

  • 初回アクセス時にCookie同意バナーを表示
  • 「同意する」「拒否する」の2ボタンを表示
  • 同意するまでGA4を読み込まない
  • 選択内容をlocalStorageへ保存
  • 次回アクセス時は保存済みの選択を利用
  • 「Cookie設定」ボタンから後で選択を変更可能
  • WordPressデータベースへ同意ログを保存
  • IPアドレスとUser-Agentは保存しない
  • 90日を超えたログをWP-Cronで削除

2 functions.phpではなく専用プラグインにする理由

単純なデザイン変更であれば子テーマの functions.php でも実装できますが、今回は独自テーブルの作成・Ajax処理・定期削除処理を行います。このようなサイトのデータ管理に関係する機能は、テーマを変更しても残せるよう専用プラグインに分けた方が管理しやすくなります。

WordPress公式でも、プラグインが独自テーブルを必要とする場合は、プラグイン有効化時にテーブルを作成する方法が案内されています。

3 ディレクトリ構造

wp-content/plugins/ の中に、次のフォルダとファイルを作成します。

  • simple-cookie-consent-ga4/
    • simple-cookie-consent-ga4.php
    • assets/
      • css/
        • cookie-consent.css
      • js/
        • cookie-consent.js

4 ① プラグイン本体を作成する

simple-cookie-consent-ga4.php を作成して、次のコードを保存します。

<?php
/**
 * Plugin Name: Simple Cookie Consent for GA4
 * Description: GA4を同意後に読み込む簡易Cookie同意バナーです。
 * Version: 1.0.0
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

define( 'WPTCC_VERSION', '1.0.0' );
define( 'WPTCC_RETENTION_DAYS', 90 );

/*
 * 自分のGA4測定IDへ変更してください。
 */
define( 'WPTCC_GA4_ID', 'G-XXXXXXXXXX' );


/* =========================================================
 * テーブル名
========================================================= */

function wptcc_table_name() {
	global $wpdb;

	return $wpdb->prefix . 'wpt_cookie_consent_log';
}


/* =========================================================
 * プラグイン有効化
 * テーブル作成+定期削除を登録
========================================================= */

register_activation_hook( __FILE__, 'wptcc_activate' );

function wptcc_activate() {
	global $wpdb;

	$table_name      = wptcc_table_name();
	$charset_collate = $wpdb->get_charset_collate();

	$sql = "CREATE TABLE $table_name (
		id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
		consent_id varchar(64) NOT NULL,
		status varchar(20) NOT NULL,
		created_at datetime NOT NULL,
		PRIMARY KEY  (id),
		KEY consent_id (consent_id),
		KEY created_at (created_at)
	) $charset_collate;";

	require_once ABSPATH . 'wp-admin/includes/upgrade.php';

	dbDelta( $sql );

	if ( ! wp_next_scheduled( 'wptcc_cleanup_logs' ) ) {
		wp_schedule_event(
			time() + HOUR_IN_SECONDS,
			'daily',
			'wptcc_cleanup_logs'
		);
	}
}


/* =========================================================
 * プラグイン停止時
 * Cronだけ解除
========================================================= */

register_deactivation_hook( __FILE__, 'wptcc_deactivate' );

function wptcc_deactivate() {
	wp_clear_scheduled_hook( 'wptcc_cleanup_logs' );
}


/* =========================================================
 * 90日を超えたログを削除
========================================================= */

add_action( 'wptcc_cleanup_logs', 'wptcc_cleanup_logs' );

function wptcc_cleanup_logs() {
	global $wpdb;

	$table_name = wptcc_table_name();

	$cutoff = gmdate(
		'Y-m-d H:i:s',
		time() - ( WPTCC_RETENTION_DAYS * DAY_IN_SECONDS )
	);

	$wpdb->query(
		$wpdb->prepare(
			"DELETE FROM {$table_name} WHERE created_at < %s",
			$cutoff
		)
	);
}


/* =========================================================
 * CSS・JavaScript読み込み
========================================================= */

add_action( 'wp_enqueue_scripts', 'wptcc_enqueue_assets' );

function wptcc_enqueue_assets() {

	$css_path = plugin_dir_path( __FILE__ ) . 'assets/css/cookie-consent.css';
	$js_path  = plugin_dir_path( __FILE__ ) . 'assets/js/cookie-consent.js';

	wp_enqueue_style(
		'wptcc-cookie-consent',
		plugin_dir_url( __FILE__ ) . 'assets/css/cookie-consent.css',
		array(),
		file_exists( $css_path ) ? filemtime( $css_path ) : WPTCC_VERSION
	);

	wp_enqueue_script(
		'wptcc-cookie-consent',
		plugin_dir_url( __FILE__ ) . 'assets/js/cookie-consent.js',
		array(),
		file_exists( $js_path ) ? filemtime( $js_path ) : WPTCC_VERSION,
		true
	);

	$config = array(
		'ajaxUrl'       => admin_url( 'admin-ajax.php' ),
		'nonce'         => wp_create_nonce( 'wptcc_save_consent' ),
		'measurementId' => WPTCC_GA4_ID,
	);

	wp_add_inline_script(
		'wptcc-cookie-consent',
		'window.WPTCookieConsent = ' . wp_json_encode( $config ) . ';',
		'before'
	);
}


/* =========================================================
 * Cookie同意バナー出力
========================================================= */

add_action( 'wp_footer', 'wptcc_render_banner', 100 );

function wptcc_render_banner() {

	if ( is_admin() ) {
		return;
	}

	$privacy_url = get_privacy_policy_url();

	echo '<div id="wptcc-banner" class="wptcc-banner" role="dialog" aria-labelledby="wptcc-title" aria-describedby="wptcc-description" hidden>';

	echo '<p id="wptcc-title" class="wptcc-title">Cookie・アクセス解析について</p>';

	echo '<p id="wptcc-description" class="wptcc-description">';
	echo '当サイトではアクセス解析のためGoogle Analyticsを利用します。';
	echo '「同意する」を選ぶまで、このプラグインからGA4は読み込みません。';
	echo '</p>';

	if ( $privacy_url ) {
		echo '<p class="wptcc-policy">';
		echo '<a href="' . esc_url( $privacy_url ) . '">プライバシーポリシー</a>';
		echo '</p>';
	}

	echo '<div class="wptcc-actions">';

	echo '<button type="button" class="wptcc-button wptcc-accept" data-wptcc-consent="accepted">';
	echo '同意する';
	echo '</button>';

	echo '<button type="button" class="wptcc-button wptcc-reject" data-wptcc-consent="rejected">';
	echo '拒否する';
	echo '</button>';

	echo '</div>';
	echo '</div>';

	echo '<button type="button" id="wptcc-open-settings" class="wptcc-settings">';
	echo 'Cookie設定';
	echo '</button>';
}


/* =========================================================
 * Ajax:Cookie同意ログ保存
========================================================= */

add_action(
	'wp_ajax_nopriv_wptcc_save_consent',
	'wptcc_save_consent'
);

add_action(
	'wp_ajax_wptcc_save_consent',
	'wptcc_save_consent'
);

function wptcc_save_consent() {

	check_ajax_referer(
		'wptcc_save_consent',
		'nonce'
	);

	$status = isset( $_POST['status'] )
		? sanitize_key( wp_unslash( $_POST['status'] ) )
		: '';

	if ( ! in_array(
		$status,
		array( 'accepted', 'rejected' ),
		true
	) ) {
		wp_send_json_error(
			array( 'message' => 'Invalid status.' ),
			400
		);
	}

	$consent_id = isset( $_POST['consent_id'] )
		? sanitize_text_field(
			wp_unslash( $_POST['consent_id'] )
		)
		: '';

	if ( ! preg_match(
		'/^[A-Za-z0-9_-]{16,64}$/',
		$consent_id
	) ) {
		wp_send_json_error(
			array( 'message' => 'Invalid consent ID.' ),
			400
		);
	}

	global $wpdb;

	$result = $wpdb->insert(
		wptcc_table_name(),
		array(
			'consent_id' => $consent_id,
			'status'     => $status,
			'created_at' => current_time( 'mysql', true ),
		),
		array(
			'%s',
			'%s',
			'%s',
		)
	);

	if ( false === $result ) {
		wp_send_json_error(
			array(
				'message' => 'Could not save consent log.',
			),
			500
		);
	}

	wp_send_json_success(
		array(
			'status' => 'saved',
		)
	);
}

5 GA4測定IDを変更する

プラグイン本体の次の部分を、自分のGoogle Analytics 4の測定IDへ変更してください。

define( 'WPTCC_GA4_ID', 'G-XXXXXXXXXX' );

たとえば測定IDが G-ABC1234567 なら、次のようにします。

define( 'WPTCC_GA4_ID', 'G-ABC1234567' );

6 ② JavaScriptを作成する

assets/js/cookie-consent.js を作成して、次のコードを保存します。

(() => {
	'use strict';

	const STORAGE_KEY = 'wptccConsentV1';

	document.addEventListener('DOMContentLoaded', () => {

		const banner = document.getElementById('wptcc-banner');

		const settingsButton =
			document.getElementById('wptcc-open-settings');

		const choiceButtons =
			document.querySelectorAll('[data-wptcc-consent]');

		if (
			!banner ||
			!settingsButton ||
			!choiceButtons.length
		) {
			return;
		}

		const config =
			window.WPTCookieConsent || {};


		/* -----------------------------------------
		 * 保存済み設定を取得
		----------------------------------------- */

		function readChoice() {

			try {

				const value =
					localStorage.getItem(STORAGE_KEY);

				return value
					? JSON.parse(value)
					: null;

			} catch (error) {

				return null;
			}
		}


		/* -----------------------------------------
		 * 設定を保存
		----------------------------------------- */

		function writeChoice(choice) {

			try {

				localStorage.setItem(
					STORAGE_KEY,
					JSON.stringify(choice)
				);

			} catch (error) {

				console.warn(
					'Cookie consent preference could not be saved.',
					error
				);
			}
		}


		/* -----------------------------------------
		 * ランダム同意IDを作成
		----------------------------------------- */

		function createConsentId() {

			if (
				window.crypto &&
				typeof window.crypto.randomUUID === 'function'
			) {

				return window.crypto
					.randomUUID()
					.replace(/-/g, '');
			}

			if (
				window.crypto &&
				typeof window.crypto.getRandomValues === 'function'
			) {

				const bytes =
					new Uint8Array(16);

				window.crypto
					.getRandomValues(bytes);

				return Array.from(
					bytes,
					byte =>
						byte
							.toString(16)
							.padStart(2, '0')
				).join('');
			}

			return (
				Date.now().toString(36) +
				Math.random()
					.toString(36)
					.slice(2) +
				Math.random()
					.toString(36)
					.slice(2)
			);
		}


		function showBanner() {
			banner.hidden = false;
		}


		function hideBanner() {
			banner.hidden = true;
		}


		/* -----------------------------------------
		 * GA4を読み込む
		----------------------------------------- */

		function loadGoogleAnalytics() {

			const measurementId =
				String(
					config.measurementId || ''
				).trim();

			if (
				!/^G-[A-Z0-9]+$/i.test(measurementId) ||
				measurementId === 'G-XXXXXXXXXX'
			) {
				return;
			}

			if (window.wptccGaLoaded) {
				return;
			}

			window.wptccGaLoaded = true;

			window.dataLayer =
				window.dataLayer || [];

			window.gtag =
				window.gtag ||
				function () {
					window.dataLayer
						.push(arguments);
				};

			const script =
				document.createElement('script');

			script.async = true;

			script.src =
				'https://www.googletagmanager.com/gtag/js?id=' +
				encodeURIComponent(measurementId);

			document.head.appendChild(script);

			window.gtag(
				'js',
				new Date()
			);

			window.gtag(
				'config',
				measurementId
			);
		}


		/* -----------------------------------------
		 * WordPressへログ送信
		----------------------------------------- */

		async function saveLog(
			status,
			consentId
		) {

			if (
				!config.ajaxUrl ||
				!config.nonce
			) {
				return;
			}

			const body =
				new URLSearchParams({
					action:
						'wptcc_save_consent',

					nonce:
						config.nonce,

					status:
						status,

					consent_id:
						consentId
				});

			const response =
				await fetch(
					config.ajaxUrl,
					{
						method: 'POST',

						headers: {
							'Content-Type':
								'application/x-www-form-urlencoded; charset=UTF-8'
						},

						credentials:
							'same-origin',

						body:
							body.toString()
					}
				);

			if (!response.ok) {
				throw new Error(
					'Consent log request failed.'
				);
			}
		}


		/* -----------------------------------------
		 * 同意・拒否処理
		----------------------------------------- */

		async function applyChoice(status) {

			const previous =
				readChoice();

			const consentId =
				previous && previous.consentId
					? previous.consentId
					: createConsentId();

			writeChoice({
				status:
					status,

				consentId:
					consentId,

				updatedAt:
					new Date().toISOString()
			});

			hideBanner();

			try {

				await saveLog(
					status,
					consentId
				);

			} catch (error) {

				console.warn(error);
			}

			if (
				status === 'accepted'
			) {

				loadGoogleAnalytics();

				return;
			}

			/*
			 * 一度同意した後に拒否へ変更した場合は、
			 * ページを再読み込みして
			 * このプラグインからのGA4読込を停止します。
			 */
			if (
				previous &&
				previous.status === 'accepted'
			) {

				window.location.reload();
			}
		}


		choiceButtons.forEach(
			button => {

				button.addEventListener(
					'click',
					() => {

						applyChoice(
							button.dataset
								.wptccConsent
						);
					}
				);
			}
		);


		/* -----------------------------------------
		 * Cookie設定を再表示
		----------------------------------------- */

		settingsButton.addEventListener(
			'click',
			showBanner
		);


		/* -----------------------------------------
		 * 初期状態
		----------------------------------------- */

		const savedChoice =
			readChoice();

		if (
			!savedChoice ||
			![
				'accepted',
				'rejected'
			].includes(
				savedChoice.status
			)
		) {

			showBanner();

			return;
		}

		if (
			savedChoice.status ===
			'accepted'
		) {

			loadGoogleAnalytics();
		}
	});
})();

7 ③ CSSを作成する

assets/css/cookie-consent.css を作成して、次のCSSを保存します。

/* =========================================================
	Cookie同意バナー
========================================================= */

.wptcc-banner {
	position: fixed;
	left: 50%;
	bottom: 20px;
	transform: translateX(-50%);
	z-index: 99999;
	width: min(92%, 560px);
	padding: 20px;
	border: 1px solid #ddd;
	border-radius: 10px;
	background: #fff;
	box-shadow: 0 8px 28px rgba(0, 0, 0, 0.18);
	color: #222;
	font-size: 14px;
	line-height: 1.7;
}

.wptcc-banner[hidden] {
	display: none;
}

.wptcc-title {
	margin: 0 0 8px;
	font-size: 18px;
	font-weight: 700;
}

.wptcc-description,
.wptcc-policy {
	margin: 0 0 12px;
}

.wptcc-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
}

.wptcc-button,
.wptcc-settings {
	border: 0;
	border-radius: 6px;
	cursor: pointer;
	font: inherit;
}

.wptcc-button {
	padding: 10px 18px;
	font-weight: 700;
}

.wptcc-accept {
	background: #1f6f43;
	color: #fff;
}

.wptcc-reject {
	border: 1px solid #bbb;
	background: #fff;
	color: #222;
}

.wptcc-settings {
	position: fixed;
	right: 16px;
	bottom: 16px;
	z-index: 99998;
	padding: 8px 12px;
	background: #333;
	color: #fff;
	font-size: 13px;
}

.wptcc-button:focus-visible,
.wptcc-settings:focus-visible,
.wptcc-policy a:focus-visible {
	outline: 3px solid #1a73e8;
	outline-offset: 3px;
}


/* =========================================================
	スマートフォン
========================================================= */

@media (max-width: 600px) {

	.wptcc-banner {
		bottom: 12px;
		padding: 16px;
	}

	.wptcc-actions {
		flex-direction: column;
	}

	.wptcc-button {
		width: 100%;
	}
}

8 ④ WordPressでプラグインを有効化する

3つのファイルをサーバーへアップロードしたら、WordPress管理画面の「プラグイン」から「Simple Cookie Consent for GA4」を有効化します。有効化時に同意ログ用のデータベーステーブルが作成され、古いログを削除するWP-Cronも登録されます。

今回はプラグイン側から wp_footer にバナーを出力するため、LightningのウィジェットへHTMLを貼り付ける必要はありません。

9 localStorageには何が保存される?

ブラウザ側には wptccConsentV1 というキーで、同意状態・ランダムな同意ID・変更日時を保存します。たとえば次のような内容です。

{
  "status": "accepted",
  "consentId": "9db0fa80906e46b5a6df781445e74121",
  "updatedAt": "2026-08-14T08:00:00.000Z"
}

localStorage は通常、ブラウザを閉じても保存内容が残りますが、ブラウザのデータ削除、プライベートブラウジング、利用環境などによって削除される場合があります。そのため、「永久に保存される」「再表示防止は完璧」とは考えない方がよいでしょう。

10 「同意する」を押したとき

「同意する」を押すと、ブラウザへ accepted を保存してWordPressへログを送信し、その後GA4を読み込みます。初回ページ表示時にはGA4を読み込んでいないため、ユーザーが選択する前にこのプラグインからGA4へアクセス解析データを送信しない構成です。

11 「拒否する」を押したとき

「拒否する」を選択すると rejected を保存し、このプラグインからGA4を読み込みません。次回アクセス時も拒否状態を確認してGA4を読み込みません。

ただし、この処理で止められるのは今回のプラグインが読み込むGA4だけです。Google Site Kit、Google Tag Manager、テーマ、別のプラグインなどですでにGA4を読み込んでいる場合、それらまで自動的に停止するわけではありません。

12 一度同意した後でも変更できる

画面右下には「Cookie設定」ボタンを常時表示しています。押すと同意バナーが再表示されるため、一度「同意する」を選んだユーザーも後から「拒否する」へ変更できます。

同意済みの状態から拒否へ変更した場合はページを再読み込みし、その後このプラグインからGA4を読み込まない状態にします。ただし、すでにGoogleへ送信済みのデータを削除するものではなく、ブラウザ内にすでに作成されたGoogle Analytics関連Cookieを自動削除する機能でもありません。

13 同意ログにはIPアドレスを保存しない

元の実装例ではIPアドレスやUser-Agentを同意ログへ保存する方法も考えられますが、今回の記事では必要以上の利用者情報を保存しないようにしています。サーバーに保存するのは次の3項目だけです。

  • ランダムな同意ID
  • accepted / rejected
  • 選択日時

ランダムな同意IDはブラウザのlocalStorageにも保存され、同じブラウザで後から設定を変更した場合に同じIDで新しいログを追加します。ただし、このIDだけで「誰が同意したか」という本人確認ができるわけではありません。

14 保存されるテーブル名

標準のWordPressテーブルプレフィックスが wp_ の場合は、次のテーブルが作成されます。

wp_wpt_cookie_consent_log

ただし、WordPressではセキュリティやサイト構成によって wp_ 以外のプレフィックスを使用している場合があります。プラグインでは $wpdb->prefix を使用しているため、実際のテーブル名はサイトによって異なります。

15 phpMyAdminでログを確認する

レンタルサーバーのphpMyAdminを開き、WordPressで使用しているデータベースから ○○_wpt_cookie_consent_log を確認します。SQLから最新100件を確認する場合は、実際のテーブル名に合わせて次のように実行します。

SELECT
	id,
	consent_id,
	status,
	created_at
FROM
	wp_wpt_cookie_consent_log
ORDER BY
	created_at DESC
LIMIT 100;

16 ログは90日を超えると削除される

今回のプラグインでは保存期間を90日にしています。

define( 'WPTCC_RETENTION_DAYS', 90 );

WP-Cronを1日1回実行し、90日より古いログを削除します。保存期間を60日にしたい場合は 9060 に変更できます。

ただし、WP-CronはOSのcronのように指定時刻ぴったりに必ず実行される仕組みではありません。実行予定時刻を過ぎたあとにWordPressへのアクセスが発生したタイミングなどで処理されるため、90日を超えた瞬間に必ず削除されるという意味ではありません。

17 プラグイン停止時にログは削除しない

プラグインを停止すると定期削除用のWP-Cronは解除しますが、データベースのログテーブル自体は削除していません。停止しただけで記録を勝手に消さないためです。完全削除まで自動化したい場合は、別途アンインストール処理を実装します。

18 Ajaxのnonceだけで不正アクセスを完全防止できるわけではない

今回のAjax処理ではWordPressのnonceを確認し、status についても accepted または rejected 以外を保存しないよう検証しています。ただし、WordPressのnonceはユーザー認証やアクセス制限そのものではありません。

このAjaxは未ログイン訪問者が利用する公開処理なので、管理者権限を要求することはできません。大量の不正送信などまで防ぎたい場合は、WAFやレート制限など別の対策も検討します。

今回のコードは、ユーザーが同意するまでGoogle Analyticsタグそのものを読み込まない簡易方式です。Googleが提供するConsent Modeには「Basic」と「Advanced」という考え方があり、Consent Mode v2では analytics_storagead_storage に加えて ad_user_dataad_personalization などの同意状態も扱います。

Google広告やGoogle Tag Managerまで含めてConsent Mode v2を正式に実装する場合は、今回のコードだけで完了とは考えず、GoogleのConsent Mode仕様に合わせて別途設定してください。

20 Google Tag Managerを使っている場合

Google Tag ManagerからGA4を配信している場合は、今回のJavaScriptで直接GA4を読み込む方式と二重にしないでください。GTM側ですでにGA4タグを発火させている状態で今回のコードも使用すると、二重計測になる可能性があります。

GTMを利用するサイトでは、GTMのConsent Modeや同意設定機能を利用してタグの発火条件を管理する方が適している場合があります。

21 Google Site Kitを使っている場合も注意

Google Site Kitや別のGoogle AnalyticsプラグインがGA4タグを出力している場合も、今回の「拒否する」ボタンだけではそのタグを停止できません。今回のコードを使う場合は、GA4をどこから読み込んでいるのかを確認し、二重読み込みにならないようにしてください。

22 プライバシーポリシーに記載する内容

今回の実装では、Google Analyticsの利用に加えて、Cookie設定を保存するためブラウザのlocalStorageを使用し、サーバー側にランダムな同意IDと選択履歴を保存します。そのため、実際のサイト構成に合わせてプライバシーポリシーにも説明を追加します。

一例として、次のような内容が考えられます。

<h3>アクセス解析およびCookie設定について</h3>

<p>
当サイトではアクセス状況を把握するため、
Google Analyticsを利用しています。
当サイトのCookie設定画面で「同意する」を選択した場合に、
Google Analyticsを読み込みます。
</p>

<p>
Cookie設定の選択状態を保持するため、
ブラウザのlocalStorageを使用しています。
また、同意管理の運用確認を目的として、
ランダムに生成した同意ID、同意または拒否の状態、
選択日時を当サイトのデータベースへ記録しています。
IPアドレスおよびUser-Agentは、
当サイトの同意ログとして保存していません。
</p>

<p>
同意ログは一定期間保存した後に削除します。
Cookie設定は、サイト上の「Cookie設定」ボタンから
後で変更できます。
</p>

実際の保存期間や利用サービスが異なる場合は、サイトの実態に合わせて文言を変更してください。

23 日本国内サイトでも法律上の扱いはサイトによって異なる

日本国内のWebサイトだからといって、すべてのサイトに一律で「Cookie同意バナーを必ず表示しなければならない」という単純なルールではありません。一方で、サイトのサービス内容や外部サービスへの情報送信方法によって、個人情報保護法や電気通信事業法の外部送信規律などを確認する必要があります。

特に電気通信事業法の外部送信規律では、対象となるサービスについて、利用者の端末から外部へ送信される情報の内容や送信先などについて確認の機会を設けることが求められています。その方法は必ずしも「同意バナー」だけではなく、通知、公表、同意取得、オプトアウト措置などがあります。

24 Cookieだけを確認すればよいわけではない

外部送信はCookieだけで発生するものではありません。JavaScriptや外部タグによって閲覧ページなどの情報が第三者サービスへ送信される場合もあります。そのため、サイトで利用しているGoogle Analytics、広告、動画埋め込み、SNS、アクセス解析などを確認し、実際にどの情報がどこへ送信されているのか把握することが重要です。

25 AdSenseなどを利用して海外ユーザーへ広告配信する場合

Google AdSense、Google Ad Manager、AdMobなどを利用し、EEA、英国、スイスなどのユーザーへ広告を配信する場合は、Google独自の同意管理要件もあります。この簡易自作バナーだけでGoogle認定CMPの要件を満たせるとは限りません。

広告配信やGDPR対応まで必要なサイトでは、Google認定CMPや専門のConsent Management Platformを利用することも検討してください。

26 この実装で制御できないもの

今回の自作バナーだけでサイト内のすべてのCookieや外部通信を自動制御できるわけではありません。たとえば次のようなものは別途確認が必要です。

  • Google Tag Manager
  • Google Ads
  • Google AdSense
  • Google Site Kit
  • YouTube埋め込み
  • Meta Pixel
  • 外部SNSウィジェット
  • チャットサービス
  • 他のアクセス解析サービス
  • WordPressプラグインが独自に発行するCookie

27 動作確認方法

プラグインを有効化したら、ブラウザのシークレットモードなどでサイトを開きます。最初に同意バナーが表示されることを確認し、「拒否する」を選んだ状態ではGoogle Analyticsのスクリプトが読み込まれていないことをChromeの開発者ツール「Network」などで確認します。

次にlocalStorageを削除して再度アクセスし、「同意する」を選択します。今度は googletagmanager.com/gtag/js が読み込まれることを確認します。さらに「Cookie設定」を押して「拒否する」へ変更し、ページ再読み込み後にGA4が読み込まれなくなることを確認してください。

28 まとめ

WordPressでCookie同意バナーを自作する場合は、単に「同意する」「拒否する」というボタンを表示するだけでは不十分です。実際に解析タグの読み込みを制御し、選択内容を保存し、後から設定を変更できるようにする必要があります。

  • GA4は同意するまで読み込まない
  • 選択はlocalStorageへ保存する
  • Cookie設定を後から変更できるようにする
  • 同意ログは必要最小限の情報だけ保存する
  • IPアドレスやUser-Agentを不用意に保存しない
  • ログの保存期間を決めて削除する
  • Ajaxではnonceだけでなく入力値も検証する
  • GA4以外のタグは別途確認する
  • Consent Mode v2やGoogle広告のCMP要件とは区別する
  • 法律上の対応はサイトのサービス内容や利用地域に応じて確認する

今回のコードは「GA4を同意後に読み込む簡易的な同意管理」の実装例です。すべてのCookie・広告・外部送信・各国のプライバシー法への対応を保証するCMPではありません。 サイトで利用している外部サービスを確認したうえで、必要に応じて専門のCMPや法務確認も利用してください。