JExcellence
JExcellence
Full-stack · Oldenburg
OPEN SOURCE · 7 MIN READ

Why I built a free website privacy check

The privacy check loads your site in a real Chromium and reports trackers, Google Fonts and cookie banner issues. Nothing to install, nothing stored.
Justin EiletzOctober 30, 2026 · Updated November 7, 2026

Most site owners I talk to have a cookie banner and consider the topic closed. Yet on the first visit the page often loads fonts from Google, a map or an analytics script before anyone has clicked anything. The privacy check shows exactly that, in the browser, with nothing to install.

Why a web scanner next to the CLI

The checks come from PrivaLint, my open-source tool for privacy checks in a real browser. PrivaLint itself has its own article. It runs as a CLI, a GitHub Action and a Node.js API. That suits developers, not the owner of a beauty studio or the managing director of a joinery. If checking your own website means installing Node.js and Chromium first, you skip it.

So I put PrivaLint behind a form: enter the address, confirm it is your own site, pick a region profile. The scan takes three minutes at most, then the report is on the page. The findings are the same the CLI produces, with evidence and a suggested fix.

What happens during a scan

The scanner opens the home page in a fresh Chromium with no cookies and no stored data. It waits for the network to settle and records every request, cookie and link. If the banner shows up late, it waits one more settle period. If it finds a cookie banner, it first presses the Tab key up to 60 times to see whether the reject button gets focus. Then it clicks “Reject”, reloads the page and records a second time, and follows up to two more pages of the same site in link order. It loads the Impressum and privacy policy the home page links once more and reads their text, plus a cookie policy linked there, also as a PDF on the same site.

28 rules of PrivaLint 0.10.0 evaluate what was recorded, summarised here:

Check
What it reports
Trackers before consent
Analytics, advertising and session-replay services that send requests, set cookies or write to localStorage before a choice. Also Google Analytics, Google Tag Manager and Meta Pixel routed through your own domain.
Fingerprinting
Third-party scripts that read canvas, WebGL or audio characteristics of the browser before consent.
Data in requests
Email addresses or phone numbers in requests to other sites, services outside the EU before consent, tracking cookies lasting more than 13 months and full page addresses sent to other sites.
Trackers without a banner
Trackers run and there is no banner at all.
Trackers after rejection
Known trackers that keep loading after “Reject” and a reload.
Reject on the first layer
Banners that offer Accept but hide Reject in the settings.
Reject as easy as accept
Reject shown as plain text, much smaller or lower in contrast than accept, not reachable with the Tab key, or in another language than the page.
Cookie settings can be reopened
No visible link to withdraw consent later.
Legal links in the banner
A banner that blocks the page doesn't link the privacy policy or the Impressum.
Google Fonts and Adobe Fonts
Font files loaded from Google or Adobe servers.
Embedded content
YouTube, Google Maps, reCAPTCHA, chat and review widgets such as Intercom or Trustpilot, and similar embeds before consent.
Public CDNs
Scripts and styles from jsDelivr, unpkg or cdnjs.
Impressum and privacy policy
Missing links to either page.
Content of Impressum and privacy policy
An Impressum without a postal address or email, a privacy policy without the controller's contact or with template text, and trackers or embeds of the home page it doesn't name.
Forms
Contact or login forms without a privacy policy link next to them, pre-ticked marketing consent and forms that send over plain HTTP.
Withdrawal button and accessibility
Online shops only: the "withdraw from contract" button and accessibility information.

The region profile (Germany, Austria, Switzerland, Hungary or EU) sets the legal references in each finding and, for Switzerland, the severity of some rules. For Hungary, PrivaLint also recognises Hungarian banner buttons such as “Elutasítom” and links such as “Impresszum”. The profile does not move the browser: that stays where the server is.

An empty report is not a seal of approval
No findings means the automated rules found nothing in what loaded. It does not certify GDPR compliance and is not legal advice. Server-side tracking and unknown vendors stay invisible to the scanner.

Preview, full report and access code

Without a code, the page shows the five most important entries, sorted by severity and with at least one error and one warning where there are any. The full report arrives by email: every finding as a designed mail, plus a PDF report and PrivaLint's JSON export.

  • Without a code: the home page plus two pages, three in total.
  • With an access code: up to ten pages and the full report right on the page. I hand out codes on request.
  • More pages: for larger sites I run PrivaLint myself or set it up in your pipeline.

What data the check keeps

None. A result stays in memory for an hour at most and can only be reached through a random link. A restart of the service clears everything. There are no accounts, no database and no stored reports.

  • To limit abuse, the service counts scans per visitor. It keeps only an HMAC of the IP address under a key that changes daily, never the address itself.
  • The email address for the report goes to the mail server (IONOS) only and is not stored afterwards.
  • If you want me to get in touch, you tick a separate box and confirm through a link in the mail (double opt-in). Only then do I learn the address.

How the scanner protects itself

A service that opens arbitrary addresses is a target: someone could try to reach internal systems through it. So the scanner runs in its own container without credentials. Every browser request passes a network adapter that resolves the address and refuses internal ranges. A firewall lets only DNS, HTTP and HTTPS leave the container, plus mail delivery.

The browser sends read-only requests (GET and HEAD). It deliberately does not send beacons, the pings that carry usage data. Their addresses still end up in the report, because a tracker beacon is a finding in itself.

What my own scan turned up

The first full 50-page scan of jexcellence.de showed yellow on every rule of the home page: “not all content loaded”. One request caused it. Next.js prefetches linked pages in the background and cancels those prefetches when the network is slow. PrivaLint took the cancelled request for a loading failure and marked the whole home page incomplete.

Since PrivaLint 0.6.1 the tool notices when the page cancels a request itself and lists it in the loading details as “cancelled by the page”. Real failures such as timeouts or DNS errors still limit the result. Without the web scanner running under real load I would not have caught it.

Limits of the check

  • “Reject” on the home page is tested. PrivaLint can also switch everything off in the banner settings with --check-settings, but the web scanner does not run that check, because the second browser run does not fit the time limit. Whether the choice is stored permanently is not checked either.
  • Only the CLI checks HTTPS and HSTS. The web scanner makes no request of its own to http://.
  • Pages behind a login are out of scope. Forms are only checked for a privacy link next to them; nothing is submitted.
  • Some sites hide their banner from automated browsers. The report then shows no banner although visitors see one.
  • PrivaLint recognises trackers from a maintained vendor list and, for cookies, about 1,000 entries from the Open Cookie Database. New or very small vendors can still be missing.

What comes after the report

Two ways. Set PrivaLint up yourself: it is MIT-licensed, and the commands are below the report and in the repository. Or hand the findings to me. I self-host fonts, load maps and videos only after consent, set up the banner with Reject on the first layer and send a re-test afterwards. I quote a price once I have seen the report.

Check your website now or talk the findings through with me. If you hit a false positive, please open an issue on GitHub. privalint gets better with every such report.

CONTENTS
privacy check
GDPR
cookie banner
Google Fonts
privalint
website audit
Did this article surface a question in your own project? Let's talk it through for 30 minutes — complimentary and non-binding.