{"id":298118,"date":"2026-04-15T10:00:01","date_gmt":"2026-04-15T10:00:01","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/royalcomply\/"},"modified":"2026-04-30T08:33:08","modified_gmt":"2026-04-30T08:33:08","slug":"royalcomply","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/royalcomply\/","author":23435753,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.0.6","stable_tag":"1.0.6","tested":"6.9.4","requires":"6.2","requires_php":"7.4","requires_plugins":null,"header_name":"RoyalComply \u2013 Cookie Consent, GDPR & CCPA Compliance Banner","header_author":"Royal Plugins","header_description":"Cookie consent and privacy compliance plugin for WordPress. Blocks analytics and marketing scripts until consent is given, supports Google Consent Mode v2, and adapts banner behavior for GDPR, CCPA, and 19 US state privacy laws.","assets_banners_color":"484c58","last_updated":"2026-04-30 08:33:08","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/royalplugins.com\/royal-comply","header_author_uri":"https:\/\/royalplugins.com","rating":0,"author_block_rating":0,"active_installs":0,"downloads":213,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.2":{"tag":"1.0.2","author":"royalpluginsteam","date":"2026-04-15 09:59:37"},"1.0.3":{"tag":"1.0.3","author":"royalpluginsteam","date":"2026-04-18 02:46:40"},"1.0.4":{"tag":"1.0.4","author":"royalpluginsteam","date":"2026-04-30 08:33:08"},"1.0.6":{"tag":"1.0.6","author":"royalpluginsteam","date":"2026-04-30 08:33:08"}},"upgrade_notice":{"1.0.0":"<p>Initial release. Install and configure your cookie consent banner.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3506914,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3506914,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3506914,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3506914,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{"blueprint.json":{"filename":"blueprint.json","revision":3519315,"resolution":false,"location":"assets","locale":"","contents":"{\"$schema\":\"https:\\\/\\\/playground.wordpress.net\\\/blueprint-schema.json\",\"meta\":{\"title\":\"RoyalComply \\u2014 Live Demo\",\"description\":\"Try the GDPR \\\/ CCPA cookie consent banner live in your browser. Script blocking, Google Consent Mode v2, and regional banner behavior all pre-activated.\",\"author\":\"royalplugins\",\"categories\":[\"Privacy & Compliance\"]},\"landingPage\":\"\\\/?p=1\",\"preferredVersions\":{\"php\":\"8.2\",\"wp\":\"latest\"},\"phpExtensionBundles\":[\"kitchen-sink\"],\"features\":{\"networking\":true},\"login\":{\"username\":\"admin\",\"password\":\"password\"},\"steps\":[{\"step\":\"setSiteOptions\",\"options\":{\"blogname\":\"RoyalComply Demo\",\"blogdescription\":\"A sample site showing the cookie consent banner in action.\"}},{\"step\":\"installPlugin\",\"pluginData\":{\"resource\":\"wordpress.org\\\/plugins\",\"slug\":\"royalcomply\"},\"options\":{\"activate\":true}},{\"step\":\"runPHP\",\"code\":\"<?php\\nrequire_once '\\\/wordpress\\\/wp-load.php';\\n\\n$body = <<<HTML\\n<h2>Cookie consent banner &mdash; live<\\\/h2>\\n<p>The banner is visible at the bottom of this page. Click <strong>Accept All<\\\/strong>, <strong>Reject All<\\\/strong>, or <strong>Cookie Settings<\\\/strong> to try the three consent paths.<\\\/p>\\n<p>Behind the scenes, RoyalComply blocks analytics and marketing scripts until you consent by rewriting their <code>type<\\\/code> attribute to <code>text\\\/plain<\\\/code>. After consent, it fires <code>gtag('consent', 'update', {...})<\\\/code> via Google Consent Mode v2 so Google Tag Manager, Google Analytics, and Google Ads respect the choice.<\\\/p>\\n<h3>What to explore in the demo<\\\/h3>\\n<ul>\\n  <li><strong>Banner &mdash; accept \\\/ reject \\\/ settings:<\\\/strong> open the settings modal to toggle Analytics, Marketing, and Preferences categories independently.<\\\/li>\\n  <li><strong>Consent Log:<\\\/strong> every choice is stored with a SHA-256 hashed visitor ID, timestamp, and category selections &mdash; no IPs or PII.<\\\/li>\\n  <li><strong>Banner Design:<\\\/strong> change colors, position (6 options), and layout (bar or box).<\\\/li>\\n  <li><strong>Cookie Scanner:<\\\/strong> scans rendered HTML for known third-party script patterns and matches against 50+ known cookies.<\\\/li>\\n<\\\/ul>\\n<h3>Admin tour<\\\/h3>\\n<p>Log in with <code>admin<\\\/code> \\\/ <code>password<\\\/code> and visit <a href=\\\"\\\/wp-admin\\\/admin.php?page=royalcomply\\\">RoyalComply &rarr; Dashboard<\\\/a>. Submenu: <strong>Cookies<\\\/strong>, <strong>Banner Design<\\\/strong>, <strong>Consent Log<\\\/strong>, <strong>Settings<\\\/strong>.<\\\/p>\\n<h3>Compliance coverage<\\\/h3>\\n<p>GDPR (EU\\\/EEA), CCPA (California), and 19 other US state privacy laws (VCDPA, CPA, CTDPA, UCPA, TIPA, ICDPA, MTCDPA, TDPSA, OCPA, DPDPA, FDBR, NJDPA, NHDPA, KCDPA, NEBDPA, ICDPA, MCDPA, MNDPA). Region detection runs server-side from CDN headers (Cloudflare <code>CF-IPCountry<\\\/code>) with a browser-timezone fallback &mdash; no external IP lookup.<\\\/p>\\nHTML;\\n\\n\\\/\\\/ Overwrite the default 'Hello world!' post (ID 1) so the landing URL is stable.\\nwp_update_post( [\\n  'ID'           => 1,\\n  'post_title'   => 'RoyalComply \\u2014 Live Demo',\\n  'post_content' => $body,\\n  'post_status'  => 'publish',\\n  'post_author'  => 1,\\n] );\\n\"}]}"}},"all_blocks":[],"tagged_versions":["1.0.2","1.0.3","1.0.4","1.0.6"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3506914,"resolution":"1","location":"assets","locale":"","width":1265,"height":993},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3506914,"resolution":"2","location":"assets","locale":"","width":1260,"height":541},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3506914,"resolution":"3","location":"assets","locale":"","width":1257,"height":1069},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3506914,"resolution":"4","location":"assets","locale":"","width":1257,"height":1814},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3506914,"resolution":"5","location":"assets","locale":"","width":1427,"height":84}},"screenshots":{"1":"Dashboard with compliance overview and consent statistics.","2":"Cookie management with category badges and scanner.","3":"Banner design with live preview and color customization.","4":"Consent log with export and filtering.","5":"Settings page with region detection and script blocking options."},"jetpack_post_was_ever_published":false},"plugin_section":[],"plugin_tags":[166295,223629,20272,16626,131785],"plugin_category":[],"plugin_contributors":[253970],"plugin_business_model":[],"class_list":["post-298118","plugin","type-plugin","status-publish","hentry","plugin_tags-ccpa","plugin_tags-consent-mode","plugin_tags-cookie-banner","plugin_tags-cookie-consent","plugin_tags-gdpr","plugin_contributors-royalpluginsteam","plugin_committers-royalpluginsteam","plugin_support_reps-rpteam"],"banners":{"banner":"https:\/\/ps.w.org\/royalcomply\/assets\/banner-772x250.png?rev=3506914","banner_2x":"https:\/\/ps.w.org\/royalcomply\/assets\/banner-1544x500.png?rev=3506914","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/royalcomply\/assets\/icon-128x128.png?rev=3506914","icon_2x":"https:\/\/ps.w.org\/royalcomply\/assets\/icon-256x256.png?rev=3506914","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/royalcomply\/assets\/screenshot-1.png?rev=3506914","caption":"Dashboard with compliance overview and consent statistics."},{"src":"https:\/\/ps.w.org\/royalcomply\/assets\/screenshot-2.png?rev=3506914","caption":"Cookie management with category badges and scanner."},{"src":"https:\/\/ps.w.org\/royalcomply\/assets\/screenshot-3.png?rev=3506914","caption":"Banner design with live preview and color customization."},{"src":"https:\/\/ps.w.org\/royalcomply\/assets\/screenshot-4.png?rev=3506914","caption":"Consent log with export and filtering."},{"src":"https:\/\/ps.w.org\/royalcomply\/assets\/screenshot-5.png?rev=3506914","caption":"Settings page with region detection and script blocking options."}],"raw_content":"<!--section=description-->\n<p>https:\/\/youtu.be\/R7WQkpP4gsU<\/p>\n\n<p>RoyalComply is a cookie consent and privacy compliance plugin for WordPress. It blocks analytics and marketing scripts until consent is given, supports Google Consent Mode v2, and adapts banner behavior based on the visitor's jurisdiction (GDPR, CCPA, and 19 US state privacy laws).<\/p>\n\n<p>The plugin runs entirely on your server. It does not connect to any external services.<\/p>\n\n<h4>Works With Your Stack<\/h4>\n\n<p>RoyalComply integrates with the analytics, advertising, and tag management tools you already use \u2014 no per-vendor configuration required:<\/p>\n\n<ul>\n<li><strong>Analytics<\/strong> \u2014 Google Analytics 4 (GA4), Google Tag Manager, Hotjar, Microsoft Clarity, Matomo, Plausible, Fathom, Mixpanel, Amplitude, Heap.<\/li>\n<li><strong>Advertising pixels<\/strong> \u2014 Meta Pixel (Facebook \/ Instagram), Google Ads, LinkedIn Insight Tag, TikTok Pixel, Pinterest Tag, Twitter (X) Pixel, Snapchat Pixel, Reddit Pixel.<\/li>\n<li><strong>Themes and page builders<\/strong> \u2014 Elementor, Divi, Beaver Builder, Bricks, Gutenberg, Astra, GeneratePress, Kadence, Avada, OceanWP \u2014 works with any theme or builder.<\/li>\n<li><strong>eCommerce and memberships<\/strong> \u2014 WooCommerce, Easy Digital Downloads, MemberPress, Restrict Content Pro, LearnDash, LifterLMS. Cart and session cookies are auto-categorized as \"Necessary\" and never blocked.<\/li>\n<li><strong>Caching plugins<\/strong> \u2014 WP Rocket, W3 Total Cache, LiteSpeed Cache, WP Super Cache, FlyingPress, Cloudflare APO. Banner state is read client-side from localStorage so cached pages render correctly for every visitor.<\/li>\n<li><strong>Multilingual<\/strong> \u2014 Polylang, WPML, qTranslate, TranslatePress. Banner text supports per-language translation; the language-selection cookie is auto-categorized as \"Preferences\".<\/li>\n<li><strong>CDN geo headers<\/strong> \u2014 Cloudflare (<code>CF-IPCountry<\/code>), Fastly, KeyCDN, Sucuri. Geo detection runs server-side from existing CDN headers with a browser-timezone fallback \u2014 no IP geolocation API is contacted.<\/li>\n<\/ul>\n\n<p>RoyalComply is a free alternative to CookieYes, Complianz, Cookiebot, Iubenda, OneTrust, and Termly \u2014 without per-page-view billing, per-domain licensing, or external SaaS dependencies.<\/p>\n\n<p><strong>Features:<\/strong><\/p>\n\n<ul>\n<li><strong>Script blocking<\/strong> \u2014 Uses the WordPress <code>script_loader_tag<\/code> filter to change the <code>type<\/code> attribute of analytics and marketing scripts to <code>text\/plain<\/code> until consent is given, preventing execution.<\/li>\n<li><strong>Google Consent Mode v2<\/strong> \u2014 Outputs the <code>gtag('consent', 'default', {...})<\/code> call with a denied state before Google Tag Manager loads, and sends <code>consent update<\/code> events when the visitor makes a choice.<\/li>\n<li><strong>Regional banner behavior<\/strong> \u2014 Detects the visitor's region from CDN request headers (Cloudflare <code>CF-IPCountry<\/code> etc.) with a browser-timezone fallback. Banner behavior switches between opt-in (GDPR), opt-out (CCPA), and other jurisdictions as configured.<\/li>\n<li><strong>Cookie scanner<\/strong> \u2014 Scans your site's rendered HTML for known third-party script patterns (Google Analytics, Facebook Pixel, Hotjar, etc.) and matches them against a built-in database of 50+ cookie names. The scanner reads HTML your site already produces. No outbound HTTP requests are made.<\/li>\n<li><strong>Consent logging<\/strong> \u2014 Stores each consent choice with a SHA-256 hashed visitor identifier, timestamp, and category selections. Exportable to CSV. No IP addresses or personally identifiable information are stored.<\/li>\n<li><strong>Banner customization<\/strong> \u2014 6 position options, bar or box layout, full color control. Combined CSS and JavaScript on the frontend is under 8KB.<\/li>\n<li><strong>Geo detection<\/strong> \u2014 Reads the <code>CF-IPCountry<\/code> and similar headers already present in the incoming request. Falls back to the browser's timezone via JavaScript. No IP lookup service is called.<\/li>\n<\/ul>\n\n<p><strong>Cookie Categories:<\/strong><\/p>\n\n<ul>\n<li><strong>Necessary<\/strong> \u2014 Always allowed. WordPress sessions, WooCommerce cart, PHP sessions.<\/li>\n<li><strong>Analytics<\/strong> \u2014 Google Analytics, Hotjar, Clarity, Matomo, Plausible.<\/li>\n<li><strong>Marketing<\/strong> \u2014 Facebook Pixel, Google Ads, LinkedIn, TikTok, Pinterest.<\/li>\n<li><strong>Preferences<\/strong> \u2014 Language selection (Polylang, WPML, qTranslate).<\/li>\n<\/ul>\n\n<p><strong>Compliance Coverage:<\/strong><\/p>\n\n<ul>\n<li>GDPR (EU\/EEA) \u2014 Opt-in consent required<\/li>\n<li>CCPA (California) \u2014 Opt-out with \"Do Not Sell\" link<\/li>\n<li>VCDPA (Virginia), CPA (Colorado), CTDPA (Connecticut), UCPA (Utah), TIPA (Tennessee), ICDPA (Indiana), MTCDPA (Montana), TDPSA (Texas), OCPA (Oregon), DPDPA (Delaware), FDBR (Florida), NJDPA (New Jersey), NHDPA (New Hampshire), KCDPA (Kentucky), NEBDPA (Nebraska), ICDPA (Iowa), MCDPA (Maryland), MNDPA (Minnesota)<\/li>\n<\/ul>\n\n<h3>External services<\/h3>\n\n<p>RoyalComply does not connect to any third-party services. The plugin runs entirely on your own WordPress install and does not send data to any external server, API, or CDN.<\/p>\n\n<p>The built-in <strong>cookie scanner<\/strong> makes a single loopback HTTP request to your own site's homepage (<code>home_url( '\/' )<\/code>) using the WordPress <code>wp_remote_get()<\/code> function, with a 10-second timeout. This request goes to the same WordPress install; no third-party service is contacted. The scanner then reads the returned <code>Set-Cookie<\/code> response headers and the HTML response body, and searches the body for known third-party script hostnames (for example <code>google-analytics.com<\/code>, <code>connect.facebook.net<\/code>, <code>js.stripe.com<\/code>, <code>widget.intercom.io<\/code>, <code>cdnjs.cloudflare.com<\/code>). These hostnames are stored as pattern strings inside the plugin and are compared against the response body using PHP's <code>stripos()<\/code> function. The plugin does not make any network requests to the services the pattern strings refer to; it only reads HTML that your own site already generates.<\/p>\n\n<p>The scanner runs only when the site administrator clicks the \"Scan Site\" button in the RoyalComply admin screen. It is not run on a schedule and is not triggered by visitors.<\/p>\n\n<p><strong>Geo detection<\/strong> reads HTTP headers that are already present in the incoming page request (for example Cloudflare's <code>CF-IPCountry<\/code> header) and, as a JavaScript fallback, reads the visitor's timezone from the browser using <code>Intl.DateTimeFormat().resolvedOptions().timeZone<\/code>. No IP geolocation API is contacted.<\/p>\n\n<p><strong>Google Consent Mode v2<\/strong> outputs a <code>gtag('consent', 'default', {...})<\/code> JavaScript call in the page. This call runs in the visitor's browser and is consumed by Google Tag Manager or gtag.js if those are already installed on the site. RoyalComply itself does not load Google's scripts; the integration only configures the consent state that the site's existing Google scripts read.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the <code>royalcomply<\/code> folder to <code>\/wp-content\/plugins\/<\/code>.<\/li>\n<li>Activate the plugin through the Plugins menu.<\/li>\n<li>Go to RoyalComply &gt; Dashboard to configure.<\/li>\n<li>Run the cookie scanner to detect existing cookies.<\/li>\n<li>Customize your banner appearance under Banner Design.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"how%20does%20script%20blocking%20work%3F\"><h3>How does script blocking work?<\/h3><\/dt>\n<dd><p>RoyalComply uses WordPress's <code>script_loader_tag<\/code> filter to change script types from <code>text\/javascript<\/code> to <code>text\/plain<\/code> until consent is given. This prevents scripts from executing. The approach does not require output buffering or DOM manipulation.<\/p><\/dd>\n<dt id=\"does%20it%20work%20with%20google%20tag%20manager%3F\"><h3>Does it work with Google Tag Manager?<\/h3><\/dt>\n<dd><p>Yes. When Google Consent Mode v2 is enabled, RoyalComply outputs the required <code>gtag('consent', 'default', {...})<\/code> call before GTM loads, then updates consent state when the user makes a choice.<\/p><\/dd>\n<dt id=\"is%20this%20plugin%20gdpr%20compliant%3F\"><h3>Is this plugin GDPR compliant?<\/h3><\/dt>\n<dd><p>RoyalComply is designed to help site owners meet GDPR's consent requirements. It blocks analytics and marketing scripts until the visitor gives explicit opt-in consent, stores a record of each consent choice with a SHA-256 hashed identifier, and provides a \"Reject All\" button with the same prominence as \"Accept All\". Whether a given site is fully GDPR compliant also depends on the rest of its privacy practices (privacy policy, data processing agreements, etc.), which are outside the scope of this plugin.<\/p><\/dd>\n<dt id=\"does%20this%20work%20for%20ccpa%3F\"><h3>Does this work for CCPA?<\/h3><\/dt>\n<dd><p>Yes. When the visitor's region is detected as California (or any configured opt-out jurisdiction), RoyalComply switches the banner to opt-out mode and displays a \"Do Not Sell or Share My Personal Information\" link that records the visitor's opt-out choice.<\/p><\/dd>\n<dt id=\"does%20it%20make%20external%20api%20calls%3F\"><h3>Does it make external API calls?<\/h3><\/dt>\n<dd><p>No. Geo detection uses CDN headers (Cloudflare, etc.) that are already present in the request, plus browser timezone as a fallback. No third-party services are contacted.<\/p><\/dd>\n<dt id=\"is%20the%20consent%20log%20gdpr-compliant%3F\"><h3>Is the consent log GDPR-compliant?<\/h3><\/dt>\n<dd><p>Yes. Visitor identifiers are SHA-256 hashed with a site-specific salt. No IP addresses or personally identifiable information is stored.<\/p><\/dd>\n<dt id=\"can%20i%20use%20this%20with%20woocommerce%3F\"><h3>Can I use this with WooCommerce?<\/h3><\/dt>\n<dd><p>Yes. WooCommerce session and cart cookies are automatically categorized as \"necessary\" and are never blocked.<\/p><\/dd>\n<dt id=\"how%20does%20royalcomply%20compare%20to%20cookieyes%2C%20complianz%2C%20cookiebot%2C%20or%20iubenda%3F\"><h3>How does RoyalComply compare to CookieYes, Complianz, Cookiebot, or Iubenda?<\/h3><\/dt>\n<dd><p>CookieYes, Complianz, Cookiebot, Iubenda, OneTrust, and Termly are commercial cookie consent solutions that typically charge $9\u201349\/mo per site or per-page-view (Cookiebot's pricing scales with monthly visitors and can reach hundreds of dollars per month for high-traffic sites). RoyalComply is free with no usage limits, no per-page-view billing, and no external SaaS dependency. Core features \u2014 script blocking, Google Consent Mode v2, GDPR opt-in, CCPA opt-out, regional auto-detection, consent logging, and the cookie scanner \u2014 are included.<\/p><\/dd>\n<dt id=\"does%20royalcomply%20work%20with%20google%20analytics%204%20%28ga4%29%3F\"><h3>Does RoyalComply work with Google Analytics 4 (GA4)?<\/h3><\/dt>\n<dd><p>Yes. Enable Google Consent Mode v2 in RoyalComply settings and the plugin outputs the required <code>gtag('consent', 'default', { analytics_storage: 'denied', ad_storage: 'denied', ... })<\/code> call before GA4 or Google Tag Manager loads. When the visitor accepts, RoyalComply fires a <code>gtag('consent', 'update', ...)<\/code> event with their selections and GA4 begins receiving data. Without consent, GA4 still runs in cookieless mode and reports basic anonymized events (this is Google's intended Consent Mode v2 behavior).<\/p><\/dd>\n<dt id=\"does%20royalcomply%20work%20with%20meta%20pixel%20%28facebook%20pixel%29%20or%20other%20advertising%20pixels%3F\"><h3>Does RoyalComply work with Meta Pixel (Facebook Pixel) or other advertising pixels?<\/h3><\/dt>\n<dd><p>Yes. Meta Pixel, Google Ads, LinkedIn Insight Tag, TikTok Pixel, Pinterest Tag, Twitter (X) Pixel, Snapchat Pixel, and Reddit Pixel are auto-categorized as \"Marketing\" cookies and blocked until the visitor accepts marketing consent. The script-blocking approach uses WordPress's <code>script_loader_tag<\/code> filter to change each script's <code>type<\/code> attribute to <code>text\/plain<\/code> until consent is granted, so pixels never fire prematurely.<\/p><\/dd>\n<dt id=\"will%20royalcomply%20slow%20down%20my%20wordpress%20site%3F\"><h3>Will RoyalComply slow down my WordPress site?<\/h3><\/dt>\n<dd><p>No. The combined frontend CSS and JavaScript is under 8KB. Banner state is checked from localStorage, which is faster than a server round-trip. The cookie scanner runs only when an admin clicks \"Scan Site\" \u2014 never on a visitor request.<\/p><\/dd>\n<dt id=\"does%20royalcomply%20work%20with%20caching%20plugins%20like%20wp%20rocket%2C%20w3%20total%20cache%2C%20or%20litespeed%20cache%3F\"><h3>Does RoyalComply work with caching plugins like WP Rocket, W3 Total Cache, or LiteSpeed Cache?<\/h3><\/dt>\n<dd><p>Yes. The banner is rendered with the same HTML for every visitor and the show\/hide decision is made client-side by reading localStorage. Page caches serve the same HTML to all visitors and the banner correctly hides for returning visitors who already gave consent. No cache exclusion rules required.<\/p><\/dd>\n<dt id=\"how%20do%20i%20show%20a%20%22do%20not%20sell%20my%20personal%20information%22%20link%20for%20california%20%28ccpa%29%20visitors%3F\"><h3>How do I show a \"Do Not Sell My Personal Information\" link for California (CCPA) visitors?<\/h3><\/dt>\n<dd><p>In RoyalComply settings, enable CCPA mode and set California as an opt-out jurisdiction. When a visitor's request arrives with a CDN header indicating California (or matches a California timezone fallback), the banner automatically switches from opt-in to opt-out mode and displays the \"Do Not Sell or Share My Personal Information\" link. Clicking the link records the visitor's opt-out in the consent log.<\/p><\/dd>\n<dt id=\"can%20i%20export%20my%20consent%20log%20for%20dpo%20or%20compliance%20audits%3F\"><h3>Can I export my consent log for DPO or compliance audits?<\/h3><\/dt>\n<dd><p>Yes. Go to RoyalComply &gt; Consent Log and click \"Export CSV\". The export includes hashed visitor IDs, timestamps, region, banner version, and category selections \u2014 sufficient for demonstrating consent under GDPR Article 7 and CCPA record-keeping requirements. No IP addresses are exported.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.0.6<\/h4>\n\n<ul>\n<li>Hardening: All <code>$wpdb<\/code> queries that referenced custom tables now use the <code>%i<\/code> identifier placeholder (WordPress 6.2+) instead of interpolating the table name into the SQL string. The previous interpolations were not exploitable \u2014 the table name was always <code>$wpdb-&gt;prefix . 'rcomply_*'<\/code> (hardcoded literal, no user input) \u2014 but they caused 27 PCP \/ WPCS warnings and made the code harder to audit. <code>Requires at least<\/code> raised from 5.9 to 6.2 because <code>%i<\/code> is a 6.2+ feature.<\/li>\n<\/ul>\n\n<h4>1.0.5<\/h4>\n\n<ul>\n<li>Fix: Per-request data (CSRF nonce + geo-detected region) was inlined as a <code>var rcomplyConfig<\/code> script in the rendered HTML, so when a full-page cache (ForgeCache, WP Rocket, W3TC, or any host CDN) captured the response the FIRST visitor's values were baked in and served to everyone afterwards. Two consequences: the nonce eventually expired past WordPress's nonce TTL (~24h), causing every visitor's consent-log AJAX to return \"Security check failed\" once the cache outlived the token; and the geo result froze on the first visitor's country, so a UK visitor landing on a cache populated by a US visitor would display banner state derived from the wrong region. Both values are now fetched on demand via a new <code>rcomply_init<\/code> AJAX endpoint at admin-ajax.php \u2014 uncached by every cache plugin \u2014 and returned per request. Cached HTML now contains only static config (categories, expiry days, GCM toggle, etc.) which is identical for all visitors.<\/li>\n<li>Internal: Added the <code>rcomply_init<\/code> AJAX action (public, no nonce required since its purpose is to issue one). Returns <code>{nonce, region, country}<\/code> for the current request. banner.js calls it lazily \u2014 only the first time a consent action needs to POST.<\/li>\n<\/ul>\n\n<h4>1.0.4<\/h4>\n\n<ul>\n<li>New: WordPress Playground live preview \u2014 click \"Live Preview\" on the plugin listing to try the cookie consent banner in a browser sandbox with sample content pre-loaded.<\/li>\n<li>New: Video walkthrough embedded on the plugin listing page.<\/li>\n<\/ul>\n\n<h4>1.0.3<\/h4>\n\n<ul>\n<li>Fix: All boolean settings checkboxes (Hide for Admins, Enable Banner, Auto-Detect Location, Script Blocking, Reload on Consent, Google Consent Mode, Consent Logging, CCPA, and the Is Regex flag on cookie rules) now correctly persist when unchecked. Previously, unchecking and saving had no effect because the AJAX handler used <code>isset()<\/code> to detect the checkbox state, but the JavaScript always sends every checkbox as 1 or 0 (so the key was always present, making <code>isset()<\/code> always return true). Switched to <code>! empty()<\/code> which correctly treats \"0\" as false.<\/li>\n<\/ul>\n\n<h4>1.0.2<\/h4>\n\n<ul>\n<li>Compliance: Renamed text domain from <code>royal-comply<\/code> to <code>royalcomply<\/code> so it matches the plugin slug, as required by WordPress.org internationalization guidelines.<\/li>\n<li>Readme: Rewrote the plugin description in a neutral, factual tone, removing promotional and comparative phrasing.<\/li>\n<li>Readme: Added an \"External services\" section that documents how the cookie scanner and geo-detection work, and clarifies that the plugin makes no outbound HTTP requests.<\/li>\n<li>Code: Added file-level documentation to the scanner and script-blocker classes explaining that the hostname strings inside them are local pattern-matching dictionaries, not URLs that the plugin contacts.<\/li>\n<\/ul>\n\n<h4>1.0.1<\/h4>\n\n<ul>\n<li>Security: Removed unnecessary sslverify bypass on cookie scanner<\/li>\n<li>Compatibility: Tested up to WordPress 6.9.3<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release<\/li>\n<li>Cookie consent banner with 6 position options<\/li>\n<li>Script blocking via the script_loader_tag filter<\/li>\n<li>Google Consent Mode v2 support<\/li>\n<li>Built-in database of 50+ known cookies<\/li>\n<li>Cookie scanner for site analysis<\/li>\n<li>GDPR-compliant consent logging with CSV export<\/li>\n<li>Geo detection via CDN request headers and browser timezone (no third-party IP lookup)<\/li>\n<li>CCPA \"Do Not Sell\" support<\/li>\n<li>Customizable banner colors, text, and layout<\/li>\n<li>Privacy and cookie policy text generator<\/li>\n<\/ul>","raw_excerpt":"GDPR and CCPA cookie consent banner with Google Consent Mode v2. Blocks analytics and marketing scripts until the visitor gives consent.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/298118","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=298118"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/royalpluginsteam"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=298118"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=298118"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=298118"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=298118"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=298118"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=298118"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}