← All Articles
Last updated: 2026-03-30

Is Your Website GDPR Compliant? A Practical Checklist

Practical GDPR compliance checklist for websites. Cookie audit, consent banner, privacy policy, Google Analytics.

TL;DR

GDPR compliance for websites requires informed consent before setting non-essential cookies, a transparent privacy policy, properly configured analytics tools, and secure handling of personal data through contact forms. This checklist walks you through auditing your cookies, setting up a legally sound consent banner, configuring Google Analytics 4 with Consent Mode v2, and fixing the most common violations that trigger fines. This article provides technical guidance, not legal advice. Consult a qualified attorney for binding legal assessments.

Prerequisites

Step 1: Understand What GDPR Requires for Websites

The General Data Protection Regulation (GDPR) applies to any website that processes personal data of individuals in the European Economic Area (EEA). For a typical website, personal data processing happens more often than you might think: loading a page already transmits the visitor's IP address.

Core Principles That Affect Websites

The ePrivacy Directive (Cookie Law)

While GDPR covers personal data broadly, the ePrivacy Directive (2002/58/EC, as amended) specifically governs cookies and similar tracking technologies. Together, they require:

Step 2: Perform a Cookie Audit

Before you can manage consent properly, you need to know exactly which cookies and trackers your website uses. Many site owners are surprised by what they find.

Manual Browser Audit

Open your website in an incognito/private window with all extensions disabled. Then open developer tools:

// Chrome / Edge: F12 → Application → Cookies
// Firefox: F12 → Storage → Cookies

// Steps:
// 1. Clear all cookies for your domain
// 2. Load the homepage without interacting with the consent banner
// 3. Document every cookie that appears
// 4. Navigate through key pages (contact, blog, shop)
// 5. Document new cookies set on each page

For each cookie, record:

Automated Scanning

Use a dedicated scanner for a thorough audit. Several free and commercial tools are available:

# Using Cookiebot scanner (free for up to 1 page)
https://www.cookiebot.com/en/cookie-scanner/

# Using the open-source tool "cookie-scanner" via npm
npm install -g @nicedoc/cookie-scanner
cookie-scanner https://yourdomain.com

# Check network requests for third-party calls
# Chrome DevTools → Network tab → filter by "third-party"
# Look for requests to: google-analytics.com, doubleclick.net,
# facebook.com, hotjar.com, fonts.googleapis.com, etc.

Common Cookies You Might Find

CookieSourceCategoryConsent Required
PHPSESSIDYour serverStrictly necessaryNo
wordpress_logged_in_*WordPressStrictly necessaryNo
_ga, _gidGoogle AnalyticsAnalyticsYes
_fbpFacebook PixelMarketingYes
frFacebookMarketingYes
_hjidHotjarAnalyticsYes

Step 3: Implement a Compliant Consent Banner

A GDPR-compliant consent banner is not just a notification. It is a functional interface that collects, records, and enforces user consent choices.

Requirements for a Valid Consent Banner

Implementation Example with a Custom Script

If you prefer not to use a third-party Consent Management Platform (CMP), here is a minimal consent logic pattern:

<!-- Consent Banner HTML -->
<div id="consent-banner" role="dialog" aria-label="Cookie Consent" style="display:none;">
  <p>We use cookies to analyze website traffic and optimize your experience.
     You can choose which categories to allow.</p>
  <div class="consent-options">
    <label>
      <input type="checkbox" checked disabled> Strictly Necessary (always active)
    </label>
    <label>
      <input type="checkbox" id="consent-analytics"> Analytics
    </label>
    <label>
      <input type="checkbox" id="consent-marketing"> Marketing
    </label>
  </div>
  <button id="consent-accept-all">Accept All</button>
  <button id="consent-save">Save Preferences</button>
  <button id="consent-reject-all">Reject All</button>
</div>

<script>
(function() {
  var stored = localStorage.getItem('cookie_consent');
  if (!stored) {
    document.getElementById('consent-banner').style.display = 'block';
  } else {
    applyConsent(JSON.parse(stored));
  }

  document.getElementById('consent-accept-all').addEventListener('click', function() {
    saveConsent({ analytics: true, marketing: true });
  });

  document.getElementById('consent-reject-all').addEventListener('click', function() {
    saveConsent({ analytics: false, marketing: false });
  });

  document.getElementById('consent-save').addEventListener('click', function() {
    saveConsent({
      analytics: document.getElementById('consent-analytics').checked,
      marketing: document.getElementById('consent-marketing').checked
    });
  });

  function saveConsent(choices) {
    choices.timestamp = new Date().toISOString();
    localStorage.setItem('cookie_consent', JSON.stringify(choices));
    document.getElementById('consent-banner').style.display = 'none';
    applyConsent(choices);
  }

  function applyConsent(choices) {
    if (choices.analytics) { loadAnalytics(); }
    if (choices.marketing) { loadMarketing(); }
  }

  function loadAnalytics() {
    // Load GA4 only after consent
    var s = document.createElement('script');
    s.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX';
    document.head.appendChild(s);
  }

  function loadMarketing() {
    // Load marketing scripts only after consent
  }
})();
</script>

Recommended Consent Management Platforms

For production sites, consider a dedicated CMP that handles edge cases, consent logging, and integration with IAB TCF:

Step 4: Create a Proper Privacy Policy

Every website processing personal data must provide a comprehensive privacy policy accessible from every page (typically linked in the footer).

Mandatory Sections

Technical Implementation

<!-- Every page must link to the privacy policy -->
<footer>
  <a href="/privacy-policy">Privacy Policy</a>
  <a href="/imprint">Imprint</a> <!-- Required in DE/AT -->
</footer>

<!-- Privacy policy page must not be blocked by cookie banners -->
<!-- Privacy policy page should be indexable (not noindex) -->
<!-- Privacy policy should load without requiring JavaScript -->

Step 5: Configure Google Analytics for GDPR Compliance

Google Analytics 4 (GA4) requires specific configuration to be used lawfully in the EEA.

Enable Google Consent Mode v2

Consent Mode v2 communicates the user's consent state to Google tags, adjusting their behavior accordingly.

<!-- Set default consent state BEFORE loading gtag.js -->
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}

// Default: deny all until user consents
gtag('consent', 'default', {
  'analytics_storage': 'denied',
  'ad_storage': 'denied',
  'ad_user_data': 'denied',
  'ad_personalization': 'denied',
  'functionality_storage': 'denied',
  'personalization_storage': 'denied',
  'security_storage': 'granted',  // security cookies are strictly necessary
  'wait_for_update': 500          // wait 500ms for CMP to load
});
</script>

<!-- Load gtag.js -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX"></script>
<script>
gtag('js', new Date());
gtag('config', 'G-XXXXXXX', {
  'anonymize_ip': true,           // Redundant in GA4 but explicit
  'send_page_view': true
});
</script>

<!-- When user grants consent, update the state -->
<script>
function updateConsentState(analyticsGranted, marketingGranted) {
  gtag('consent', 'update', {
    'analytics_storage': analyticsGranted ? 'granted' : 'denied',
    'ad_storage': marketingGranted ? 'granted' : 'denied',
    'ad_user_data': marketingGranted ? 'granted' : 'denied',
    'ad_personalization': marketingGranted ? 'granted' : 'denied'
  });
}
</script>

GA4 Settings in the Admin Panel

Server-Side Tagging (Advanced)

For maximum control over data sent to Google, consider server-side Google Tag Manager. This routes analytics data through your own server, allowing you to strip or redact personal data before it reaches Google:

# Server-side GTM deployment overview:
# 1. Deploy a server-side GTM container (App Engine, Cloud Run, or self-hosted)
# 2. Route the GA4 measurement endpoint through your domain
# 3. Configure the server container to strip client IP, user-agent details
# 4. Forward sanitized data to GA4

# Your domain serves as the analytics endpoint:
# https://yourdomain.com/g/collect → your server container → GA4

Step 6: Secure Contact Forms and Data Processing

Contact forms collect personal data directly. They require careful implementation.

Technical Requirements

<!-- Contact form with required GDPR elements -->
<form action="/submit-contact" method="POST">
  <label for="name">Name</label>
  <input type="text" id="name" name="name" required>

  <label for="email">Email</label>
  <input type="email" id="email" name="email" required>

  <label for="message">Message</label>
  <textarea id="message" name="message" required></textarea>

  <!-- GDPR consent checkbox (must not be pre-checked) -->
  <label>
    <input type="checkbox" id="privacy-consent" name="privacy_consent" required>
    I have read the <a href="/privacy-policy" target="_blank">privacy policy</a>
    and agree to the processing of my data for the purpose of responding
    to my inquiry. *
  </label>

  <button type="submit">Send Message</button>
</form>

Backend Requirements

# Example: Server-side consent logging (pseudocode)
def handle_contact_form(request):
    if not request.POST.get('privacy_consent'):
        return error('Consent required')

    submission = {
        'name': request.POST['name'],
        'email': request.POST['email'],
        'message': request.POST['message'],
        'consent_given': True,
        'consent_timestamp': datetime.utcnow().isoformat(),
        'consent_ip': request.META['REMOTE_ADDR'],
        'privacy_policy_version': '2024-01-15',
        'retention_delete_after': datetime.utcnow() + timedelta(days=180)
    }
    db.contact_submissions.insert(submission)
    return success('Message sent')

Step 7: Fix Common Violations

Violation 1: Cookies Set Before Consent

The most frequent violation. Analytics or marketing scripts fire immediately on page load.

Fix: Wrap all non-essential scripts in your consent logic. In Google Tag Manager, use the Consent Overview to ensure tags only fire when the corresponding consent category is granted.

Violation 2: No Reject Option (or Hidden Reject)

Many banners show a prominent "Accept All" button but hide the reject option behind "Manage Preferences" → "Save" with everything unchecked.

Fix: Provide a "Reject All" button at the same level and with equal visual weight as "Accept All."

Violation 3: Third-Party Fonts Loaded Without Consent

Google Fonts loaded from fonts.googleapis.com transmit the visitor's IP to Google servers. A German court ruled this a GDPR violation in January 2022 (LG Munich, Case 3 O 17493/20).

# Fix: Self-host your fonts
# 1. Download fonts from https://gwfh.mranftl.com/fonts or google-webfonts-helper
# 2. Place font files in your /fonts/ directory
# 3. Reference them locally in your CSS:

@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/open-sans-v34-latin-regular.woff2') format('woff2');
}

# 4. Remove any <link> tags pointing to fonts.googleapis.com

Violation 4: Embedded Content Without Consent

YouTube videos, Google Maps, and social media widgets load third-party cookies and transmit visitor data on page load.

Fix: Use a two-click solution. Display a placeholder with a consent notice. Load the actual embed only after the user clicks to activate it.

Violation 5: Missing or Incomplete Privacy Policy

Fix: Audit your privacy policy against the mandatory sections listed in Step 4. Use a generator as a starting point, then customize it to reflect your actual data processing activities.

Violation 6: No Data Processing Agreement (DPA)

You must have a signed Data Processing Agreement (Art. 28 GDPR) with every third-party processor: your hosting provider, email service, analytics provider, CRM, etc.

Fix: Contact each service provider and request their DPA. Most major providers (AWS, Google, Mailchimp, Cloudflare) offer an online DPA you can accept through their admin panel.

Troubleshooting

Consent Banner Does Not Block Scripts

If cookies still appear before consent, check for:

# Verify no cookies are set before consent:
# 1. Open incognito window
# 2. Open DevTools → Application → Cookies → clear all
# 3. Load your site but do NOT interact with the consent banner
# 4. Check cookies — only strictly necessary cookies should appear

# Check for network requests that should be blocked:
# DevTools → Network → reload page without consent
# Filter by: google-analytics | doubleclick | facebook | hotjar
# If you see requests to these domains, your blocking is incomplete

Google Analytics Shows No Data After Implementation

WordPress Plugin Conflicts

Cookie consent plugins may conflict with caching plugins or other scripts. Typical resolution steps:

Prevention

Ongoing Compliance Checklist

Automated Monitoring

# Set up automated cookie monitoring with a cron job
# This script checks for unexpected cookies weekly

#!/bin/bash
# cookie-monitor.sh
DOMAIN="yourdomain.com"
EXPECTED_COOKIES="PHPSESSID,cookie_consent,csrf_token"

# Use a headless browser to detect cookies set without interaction
npx puppeteer-cli screenshot "https://$DOMAIN" \
  --cookie-dump /tmp/cookies.json \
  --no-interaction

# Compare found cookies against expected list
python3 - <<PYEOF
import json
expected = set("$EXPECTED_COOKIES".split(','))
with open('/tmp/cookies.json') as f:
    found = set(c['name'] for c in json.load(f))
unexpected = found - expected
if unexpected:
    print(f"WARNING: Unexpected cookies found: {unexpected}")
    # Send alert email or Slack notification
PYEOF

Before Deploying Any Change

  1. Check if the change introduces new third-party requests or cookies.
  2. If yes, add the new resource to your consent configuration.
  3. Update the cookie declaration in your privacy policy or cookie policy.
  4. Test in an incognito browser: no non-essential cookies before consent.
  5. Document the change in your records of processing activities.

Disclaimer: This article provides technical guidance for implementing GDPR-related website features. It does not constitute legal advice. Data protection requirements vary by jurisdiction and specific use case. Consult a qualified data protection attorney or your Data Protection Officer for legally binding assessments.

Need Expert Help?

Want a professional GDPR/cookie audit with written fixes? €49.

Book Now — €49

100% money-back guarantee

HR

Harald Roessler

Infrastructure Engineer with 20+ years experience. Founder of DSNCON GmbH.