> ./exec Web_dev.sh — GUIDE

GDPR-Compliant Website: Checklist for Mid-Market Companies

Léa — Frontend Engineer LéaFrance · Frontend Engineer 09-07-2026 7 min read WEB-DEV

A GDPR fine starts at 2% of global annual revenue. For a company with EUR 5 million in annual revenue, that is a minimum exposure of EUR 100,000. The surprisingly common root cause is not a sophisticated data breach but a standard website component: an externally loaded font, a cookie banner without genuine opt-in logic, a contact form missing an Art. 13 notice.

This guide contains eight checkpoints that can be verified directly in the code or server configuration. Each section states precisely what to measure and which tool makes the status visible.


Checkpoint 1: HTTPS and TLS 1.3

Art. 32 GDPR requires "appropriate technical measures" to protect personal data. An unencrypted HTTP connection does not meet this criterion.[1] TLS 1.0 and 1.1 have been considered outdated since 2021 and must no longer be used. The BSI recommends TLS 1.3 with modern ECDHE cipher suites and explicitly excludes SHA-1 certificates.[4]

Minimal Nginx configuration:

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384';
ssl_prefer_server_ciphers off;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

Validation: The SSL Labs Server Test returns an A+ grade when HSTS and TLS 1.3 are configured correctly. Alternatively, running curl -I https://your-domain.com and checking the Strict-Transport-Security header in the response is sufficient.


The Conference of Independent German Data Protection Authorities (DSK) has clarified in its Telemedia guidance: cookies that are not technically necessary may only be set after an active, informed consent. Pre-checked checkboxes, an "OK" button without an equivalent "Decline" option, or hidden opt-out links do not meet the requirements.[2]

What this means technically: third-party scripts must not be injected into the DOM until after the consent event.

// Load scripts only AFTER consent
function loadAnalytics() {
  const script = document.createElement('script');
  script.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-XXXXXXX';
  script.async = true;
  document.head.appendChild(script);
}

document.addEventListener('consent:analytics', loadAnalytics);

A consent manager (Usercentrics, Cookiebot, Borlabs Cookie) implements this event pattern automatically. Critical point: the consent manager itself must not set any tracking cookies before the user has made a decision. This sounds trivial but is a frequent configuration error in low-cost implementations.

Lighthouse check: Under "Third-party resources" in the Lighthouse report and in the Network tab of Chrome DevTools, you can see how many requests fire on the very first page load. Every external script before the consent event is a potential violation.


Checkpoint 3: Serving Google Fonts Locally

The Munich Regional Court I ruled in January 2022: automatically transmitting a visitor's IP address to Google when loading fonts from the Google CDN violates the GDPR.[3] The technical fix is straightforward. The google-webfonts-helper tool provides WOFF2 files ready for local delivery.

/* styles/fonts.css */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/inter-v13-latin-regular.woff2') format('woff2');
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/fonts/inter-v13-latin-700.woff2') format('woff2');
}

font-display: swap is not an optional detail here. It prevents FOIT (Flash of Invisible Text) and measurably improves the LCP score. In our own tests: LCP reduction of 0.3 to 0.8 seconds after switching from Google CDN to local font delivery.[5]


Checkpoint 4: Contact Forms and Disclosure Requirements

Every form that collects personal data must reference the privacy policy immediately before submission. The information must be visible before the user submits, not only in a linked document.[1]

<p class="form-privacy-hint">
  By submitting this form, you consent to the processing of your data
  to handle your enquiry.
  Further information: <a href="/privacy">Privacy Policy</a>.
</p>

The principle of data minimization under Art. 5(1)(c) GDPR applies to the field structure: a contact form requires a name and email address. Phone number and company name must be marked as optional fields.[1] Mandatory fields that are not strictly necessary for the form's purpose increase compliance risk.


Checkpoint 5: Data Processing Agreements (DPA)

Every external service provider that processes personal data on your behalf requires a Data Processing Agreement (DPA). This applies not only to the hosting provider but to all tools that touch visitor or customer data:

  • Hosting providers (Hetzner, IONOS, AWS, Azure)
  • Email service providers (Mailchimp, Brevo, Microsoft 365)
  • Analytics services (Google Analytics 4, Matomo Cloud, Hotjar)
  • Form services (Formspree, HubSpot, Typeform)
  • CDN providers (Cloudflare, Fastly, Akamai)

Most major providers offer standardized DPA templates in their customer portal. Missing or expired DPAs are among the most common findings in GDPR audits. A simple table listing provider, processing purpose, DPA status, and last review date is sufficient as minimum documentation.


Checkpoint 6: Content Security Policy Header

A CSP header limits which resources the browser may load and where it may send data. It is not an optional security hardening measure but a technical control within the meaning of Art. 32 GDPR that prevents unauthorized data transfers to third parties.

Strict base header for Nginx:

add_header Content-Security-Policy "
  default-src 'self';
  script-src  'self';
  style-src   'self' 'unsafe-inline';
  font-src    'self';
  img-src     'self' data:;
  connect-src 'self';
  frame-ancestors 'none';
" always;

After switching to local fonts and self-hosted asset delivery, font-src can be restricted to 'self'. Every additional domain in the directives is a potential external data point. The Chrome DevTools Security tab displays CSP violations directly in the console.

Benchmark: A complete CSP header with default-src 'self' blocks an average of 8 to 14 third-party requests on a typical marketing website at first load.


Checkpoint 7: Server Logs and IP Anonymization

Web server logs contain full IPv4 and IPv6 addresses by default. A complete IP address is personal data. Without a legal basis or anonymization, storing it violates Art. 5 GDPR.[1]

Nginx configuration with IP anonymization via map:

map $remote_addr $remote_addr_anon {
  ~(?P<ip>\d+\.\d+\.\d+)\.\d+$  $ip.0;
  ~(?P<ip>[^:]+:[^:]+):          $ip::;
  default                        0.0.0.0;
}

log_format anonymized '$remote_addr_anon - $remote_user [$time_local] '
                       '"$request" $status $body_bytes_sent';

access_log /var/log/nginx/access.log anonymized;

In addition, log rotation to a maximum of 7 days is recommended (logrotate with rotate 1 and daily). For debugging purposes, a 48-hour window is sufficient in practice. Longer retention requires an explicit legal basis and an entry in the records of processing activities.


Checkpoint 8: Lighthouse as an Early GDPR Warning System

Lighthouse does not measure GDPR compliance in the legal sense, but third-party requests in the Lighthouse report correlate directly with a website's risk profile. Every external script, every CDN font, every tracking pixel is a data transfer to a third party.

Target values for a GDPR-compliant website with solid Core Web Vitals:[5]

Metric Target Measurement tool
LCP (Largest Contentful Paint) below 2.5 s Lighthouse, CrUX
INP (Interaction to Next Paint) below 200 ms Chrome DevTools Performance
CLS (Cumulative Layout Shift) below 0.1 Lighthouse
Third-party requests (first load) 0 Network tab
Third-party bytes (first load) 0 KB Network tab

On the very first page load, before any user interaction, third-party requests should be reduced to zero. This is the technical equivalent of the consent requirement.[5]

CLI call for automated checks in the CI/CD pipeline:

npx lighthouse https://your-domain.com \
  --only-categories=performance \
  --output=json \
  --output-path=./lighthouse-report.json

Integrate this step into every deployment run. New third-party requests will surface before they reach production.


Audit Cadence

A one-off GDPR review is not enough. Every dependency update, every new plugin, every additional tracking integration can change compliance status. Recommended cadence:

  • Monthly: Lighthouse scan of the homepage and key landing pages, comparison of third-party requests before and after consent
  • Quarterly: Full review of all third-party providers against the DPA list with review date
  • At every release: CSP header test in the staging environment, Network tab check at first load
  • Annually: Full GDPR audit of all processing activities including the records of processing activities

The effort for monthly scans is under 30 minutes once the Lighthouse CI integration is set up. A fine notice costs considerably more, in time and in euros.


Sources

[1] European Parliament and Council of the European Union, Regulation (EU) 2016/679 on the protection of natural persons with regard to the processing of personal data (General Data Protection Regulation), Official Journal of the European Union L 119, 4 May 2016, pp. 1-88. https://eur-lex.europa.eu/legal-content/DE/TXT/?uri=CELEX%3A32016R0679

[2] Conference of Independent German Federal and State Data Protection Authorities (DSK), Guidance for Telemedia Providers from 1 December 2021, Version 1.1, March 2022. https://www.datenschutzkonferenz-online.de/media/oh/20220405_oh_telemedien.pdf

[3] Munich Regional Court I, judgment of 20 January 2022, case no. 3 O 17493/20, on the transmission of IP addresses to Google LLC through the integration of Google Fonts via CDN.

[4] Federal Office for Information Security (BSI), Technical Guideline BSI TR-02102-2: Cryptographic Mechanisms, Recommendations and Key Lengths, Part 2: Use of Transport Layer Security (TLS), Version 2024-01. https://www.bsi.bund.de/SharedDocs/Downloads/DE/BSI/Publikationen/TechnischeRichtlinien/TR02102/BSI-TR-02102-2.html

[5] Google LLC, Core Web Vitals, Reference documentation for LCP, INP, and CLS, web.dev/vitals, accessed 9 July 2026. https://web.dev/vitals/

Léa — Frontend Engineer

LéaFrance

Frontend Engineer

Frontend architecture, performance, reusable components, animations.

Need help with Web & Dev?

Free initial consultation, fixed price after audit.

INIT_CONSULTATION() →