Scan time: 2026-07-06 21:31:04
Overall Score
⚠ This website has serious GDPR deficiencies. Immediate action is required.
GDPR Issues Detected (4):
❌ 1 tracking service(s) detected — without prior consent (opt-in) this violates Art. 6(1) GDPR.
Detected trackers:
⚠ No Content Security Policy — increased risk of cross-site scripting (XSS) and data theft.
⚠ Missing or unsafe Referrer-Policy — URLs containing personal data may be leaked to third parties.
❌ No cookie consent banner detected despite trackers or third-party cookies — violation of the ePrivacy Directive.
Note: This automated analysis does not replace legal advice. For a complete GDPR assessment, consult a data protection officer.
↓ See detailed results for each category below.
The website uses an encrypted connection (HTTPS).
Latest encryption active (TLS 1.3 — TLSv1.3).
The security certificate is valid (expires 2026-09-01).
Adequate encryption method (TLS_AES_128_GCM_SHA256, 128 bit).
HSTS is enabled — the browser is instructed to always use the encrypted connection.
HSTS duration: 31536000 seconds (at least 1 year) — very good.
No Content Security Policy (CSP) found. The website has no protection against injected malicious code.
A Content Security Policy (CSP) is a doorkeeper rule for the browser: "Scripts and styles may only be loaded from these allowed sources." Without CSP, injected malicious code (XSS) can freely fetch anything. Start with a simple, secure baseline.
File: .htaccess in the web root
<IfModule mod_headers.c>
Header always set Content-Security-Policy "default-src 'self'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; font-src 'self' https: data:; object-src 'none'; frame-ancestors 'self'; base-uri 'self'"
</IfModule>⚠ This policy is intentionally pragmatic (allows inline styles since many themes/plugins rely on them). If something breaks after enabling: F12 → Console shows "Refused to load…" — add the affected domain after script-src / img-src.
File: .htaccess in the WordPress root
# BEGIN WebForensik CSP
<IfModule mod_headers.c>
Header always set Content-Security-Policy "default-src 'self'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; font-src 'self' https: data:; object-src 'none'; frame-ancestors 'self'; base-uri 'self'"
</IfModule>
# END WebForensik CSP⚠ WordPress often loads external scripts (Google Fonts, jQuery CDN, analytics pixel) — if CSP blocks them: open the console, see which domain is blocked, append that domain to "script-src 'self'" separated by a space.
File: functions.php of your CHILD theme
add_action('send_headers', function () {
header("Content-Security-Policy: default-src 'self'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; font-src 'self' https: data:; object-src 'none'; frame-ancestors 'self'; base-uri 'self'");
});⚠ If unsure: start with "Content-Security-Policy-Report-Only" (only monitor, don’t block), watch violations in the console, then switch to enforced mode.
✓ How to verify it works: Open page, F12 → Console — no red "Refused to load…" messages. Network tab → first request → Response Header "content-security-policy" visible.
No Referrer-Policy set. When clicking external links, the full page URL is shared with other websites.
Without a Referrer-Policy the browser sends the full URL of your current page (incl. search terms, usernames in URL params) on every click to the destination site. Privacy-relevant per Art. 5 GDPR. Recommended safe setting: strict-origin-when-cross-origin.
File: .htaccess in the web root
<IfModule mod_headers.c>
Header always set Referrer-Policy "strict-origin-when-cross-origin"
</IfModule>⚠ "strict-origin-when-cross-origin" is the modern standard: on cross-domain clicks only your origin (no path/params) is sent — internal clicks include the full URL. Stricter is "no-referrer" (send nothing) but it breaks some analytics tools.
File: .htaccess in the WordPress root
<IfModule mod_headers.c>
Header always set Referrer-Policy "strict-origin-when-cross-origin"
</IfModule>⚠ WordPress 4.9+ already emits a meta-tag with this policy, but the HTTP header above applies to all resources (images, scripts) — not only the HTML document.
File: functions.php of your CHILD theme
add_action('send_headers', function () {
header('Referrer-Policy: strict-origin-when-cross-origin');
});⚠ Always back up functions.php before edits.
✓ How to verify it works: F12 → Network → first request → Response Headers — "referrer-policy: strict-origin-when-cross-origin" must be present.
No MIME type protection (X-Content-Type-Options missing). Browsers may misinterpret files.
Without the "X-Content-Type-Options: nosniff" header the browser guesses file types from content — which attackers can exploit (e.g. a HTML file disguised as .jpg is executed as HTML). The fix is one single line.
File: .htaccess in the web root
<IfModule mod_headers.c>
Header always set X-Content-Type-Options "nosniff"
</IfModule>⚠ No side effects expected — considered a safe standard and best practice for years.
File: .htaccess in the WordPress root
<IfModule mod_headers.c>
Header always set X-Content-Type-Options "nosniff"
</IfModule>⚠ Safe to add alongside other Header set entries.
File: functions.php of your CHILD theme
add_action('send_headers', function () {
header('X-Content-Type-Options: nosniff');
});⚠ Back up functions.php before edits.
✓ How to verify it works: F12 → Network → Response Header: "x-content-type-options: nosniff".
No clickjacking protection. The website could be embedded in other pages to trick users.
Without clickjacking protection your site can be invisibly embedded into a malicious page ("enter your password here" — the click actually lands on your overlayed login form). Fix: set SAMEORIGIN (only your own domain may embed).
File: .htaccess in the web root
<IfModule mod_headers.c>
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Content-Security-Policy "frame-ancestors 'self'"
</IfModule>⚠ Use both headers: X-Frame-Options for older browsers, frame-ancestors for modern ones. If you already have a CSP, add "frame-ancestors 'self'" there — don’t duplicate.
File: .htaccess in the WordPress root
<IfModule mod_headers.c>
Header always set X-Frame-Options "SAMEORIGIN"
</IfModule>⚠ If your site is intentionally embedded elsewhere (e.g. booking widget on partner sites): instead of SAMEORIGIN, list allowed domains via CSP: Header always set Content-Security-Policy "frame-ancestors 'self' https://partner.example.com"
File: functions.php of your CHILD theme
add_action('send_headers', function () {
header('X-Frame-Options: SAMEORIGIN');
});⚠ WordPress already tries to set X-Frame-Options — this hook deliberately overrides it.
✓ How to verify it works: F12 → Network → Response Header: "x-frame-options: SAMEORIGIN".
No Permissions-Policy set. Third-party scripts could access camera, microphone, or location.
Permissions-Policy controls whether scripts (including third-party) may access camera, microphone, location, motion sensors etc. GDPR-relevant because sensitive device APIs can otherwise be reached unnoticed.
File: .htaccess in the web root
<IfModule mod_headers.c>
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), accelerometer=(), gyroscope=(), magnetometer=(), interest-cohort=()"
</IfModule>⚠ "()" at the end means: no caller (not even your own page) may use this API. If you need geolocation (e.g. a map feature): use geolocation=(self) instead of geolocation=(). "interest-cohort=()" disables Google’s FLoC tracking.
File: .htaccess in the WordPress root
<IfModule mod_headers.c>
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), interest-cohort=()"
</IfModule>⚠ Standard WordPress needs none of these APIs. If you use a plugin that needs the camera (QR scanner, video upload), set that API to "(self)".
File: functions.php of your CHILD theme
add_action('send_headers', function () {
header('Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=(), interest-cohort=()');
});⚠ Back up functions.php before edits.
✓ How to verify it works: F12 → Network → Response Header: "permissions-policy" visible.
No cookies set — exemplary for privacy.
No local storage (Web Storage) used — no tracking risk.
1 request(s) to 1 different third-party servers.
1 third-party server(s) within the EU/EEA.
Requested URLs:
https://plausible.io/js/pa-Om2DrfQCk6niVSCTA5r-g.js
1 known tracker(s) detected! These track visitors across different websites.
Trackers (Google Analytics, Facebook Pixel, …) capture visitors and follow them across multiple sites. Under GDPR Art. 6 and ePrivacy / national implementations, explicit consent is required BEFORE loading the tracker. "Continued scrolling = consent" is NOT acceptable.
WordPress plugin: Consent plugins that properly block trackers until consent: "Complianz" (free, very good), "Real Cookie Banner", "Borlabs Cookie" (paid, most thorough). Principle after setup: do NOT embed the tracker snippet (e.g. GA script) directly in your theme — register it with the consent plugin, which only releases it after "Accept". Privacy-friendly tracker alternatives: Matomo (cookieless mode → may need no consent), Plausible (EU, anonymous, vendor claims no consent needed — legal advice recommended).
✓ How to verify it works: Incognito, load page — BEFORE "Accept": F12 → Network → no requests to google-analytics.com, facebook.com/tr etc. AFTER "Accept", yes.
Plausible (Analytics): plausible.io
0 of 1 external resource(s) use integrity verification (SRI).
Only some of your external resources (0 of 1) are protected by SRI. Add integrity attributes to the remaining ones too.
WordPress plugin: Approach: view page source → all <script src="https://…"> and <link href="https://…"> without integrity attribute → generate hash at https://www.srihash.org/ → add integrity="sha384-…" crossorigin="anonymous". Plugin "WP-SRI" automates many cases.
✓ How to verify it works: F12 → Console on page load: no "Failed to find a valid digest" messages. Source: all external <script>/<link> have an integrity attribute.
No external resources use integrity verification. Tampered files would not be detected.
SRI (Subresource Integrity) is a checksum in HTML that defines what an externally loaded file MUST look like. If someone tampers with the external file (e.g. a CDN gets compromised), the browser refuses to load it. You add the "integrity" attribute on the script/link tag.
WordPress plugin: In WordPress you can rarely add SRI hashes manually (scripts are queued via wp_enqueue_script()). Plugin "WP-SRI" (in the plugin directory) adds integrity hashes automatically for external scripts/styles. For statically embedded resources in your theme: generate the hash at https://www.srihash.org/, add integrity="sha384-…" and crossorigin="anonymous" on the <script>/<link> tag.
✓ How to verify it works: F12 → Network → requests with status 200 from CDN domains (cdn.jsdelivr.net, cdnjs.cloudflare.com etc.) → in HTML source the tag must contain "integrity=\"sha384-…\" crossorigin=\"anonymous\"".
No CAA records. Any certificate authority could issue a certificate for this domain.
CAA records (Certification Authority Authorization) define in DNS which Certificate Authorities are allowed to issue certificates for your domain. Without a CAA record an attacker could request a fraudulent certificate for your domain at any CA. CAA is pure DNS configuration — set in your registrar/DNS-panel, NOT in WordPress.
Find your host in the table, copy the values to your DNS panel. For multi-CA hosts: one separate CAA record per CA (all with tag issue, flag 0, name @). Additionally recommended: an iodef record with a contact email for abuse reports.
| # | Host | CA(s) used | CAA value(s) — tag issue |
|---|---|---|---|
| 1 | Hetzner Webhosting (basic certificate, free in package) | DigiCert (programme „Encryption Everywhere") | digicert.com |
| 1 | Hetzner Webhosting (Let’s Encrypt, free) | Let’s Encrypt (ISRG) | letsencrypt.org |
| 2 | All-Inkl | Let’s Encrypt + Sectigo (Pro) | letsencrypt.orgsectigo.com |
| 3 | IONOS (1&1) | DigiCert (GeoTrust) + Let’s Encrypt | digicert.comletsencrypt.org |
| 4 | STRATO | Sectigo + Let’s Encrypt | sectigo.comletsencrypt.org |
| 5 | Cloudflare (Universal SSL) | Google Trust Services + DigiCert + Let’s Encrypt | pki.googdigicert.comletsencrypt.org |
| 6 | AWS (ACM / CloudFront) | Amazon Trust Services | amazon.comamazontrust.comawstrust.comamazonaws.com |
| 7 | Mittwald | Let’s Encrypt + Sectigo | letsencrypt.orgsectigo.com |
| 8 | Webgo | Let’s Encrypt + Sectigo | letsencrypt.orgsectigo.com |
| 9 | raidboxes (Managed WordPress) | Let’s Encrypt | letsencrypt.org |
| 10 | Host Europe / DomainFactory | Sectigo + Let’s Encrypt | sectigo.comletsencrypt.org |
Name Type Flag Tag Value
@ CAA 0 issue "digicert.com"
@ CAA 0 issue "letsencrypt.org"
@ CAA 0 iodef "mailto:security@your-domain.com"
The iodef line (last line) is optional but recommended: CAs report abuse attempts to that address. For subdomains (e.g. shop.your-domain.com) create separate records with the subdomain name instead of @ — modern CAs check parent CAA automatically though.
If your host is not on the list: open your current certificate in the browser (padlock → certificate → issuer). The CA name is shown there (e.g. "Sectigo RSA Domain Validation Secure Server CA" → value sectigo.com). Add that as a CAA record, done.
WordPress plugin: CAA records are NOT created in WordPress but in your domain registrar / DNS provider panel (e.g. Hetzner-Robot, IONOS Domains, Cloudflare Dashboard, INWX, etc.). Common label: "CAA record" or under "TXT records" with type selector "CAA". One separate record per CA.
✓ How to verify it works: On https://www.ssllabs.com/ssltest/analyze.html?d=your-domain.com → "DNS CAA" section → all your CAs should be listed. Or via dig: dig CAA your-domain.com.
2 nameservers present — good redundancy.
No IPv6 support (no AAAA record).
Your domain has no IPv6 address (AAAA record). Over 40% of users (especially mobile) reach the internet via IPv6 — they must take the slower IPv4 gateway detour.
WordPress plugin: Pure DNS + server matter. Step 1: check if your host has an IPv6 address for you (hosting panel or support ticket). Step 2: in the DNS panel create an AAAA record pointing to that IPv6. Step 3: test.
✓ How to verify it works: dig AAAA your-domain.com — or online https://ipv6-test.com/validate.php?url=your-domain.com.
SPF record present: v=spf1 include:secure-mailgate.com ?all — protects against email spoofing.
DMARC record present: v=DMARC1; p=none; rua=mailto:dmarc@smtp-staging.mailtrap.net; ruf=mailto:dmarc@smtp-staging.mailtrap.net; rf=afrf; pct=1 — email authentication active.
No security.txt file found (RFC 9116). Security researchers don't know how to report vulnerabilities.
A security.txt (RFC 9116) tells security researchers how to responsibly report vulnerabilities to you. Without it, reports may go to spam or never be sent. A plain text file at the correct path is enough.
File: /.well-known/security.txt (create the folder if it doesn’t exist)
Contact: mailto:security@your-domain.com
Expires: 2027-12-31T23:59:59.000Z
Preferred-Languages: en, de
Canonical: https://your-domain.com/.well-known/security.txt⚠ Replace "security@your-domain.com" with your actual security contact (or a generic info@). "Expires" must be a future date and should be renewed regularly. The file is plain .txt, not PHP.
File: security.txt file in /.well-known/ under your WordPress root
Contact: mailto:security@your-domain.com
Expires: 2027-12-31T23:59:59.000Z
Preferred-Languages: en, de
Canonical: https://your-domain.com/.well-known/security.txt⚠ Via FTP/SFTP create a folder ".well-known" in the WordPress root (the leading dot matters — some FTP tools need "show hidden files" enabled), inside save the file security.txt with the content above. If WordPress redirects the URL: add to .htaccess: RewriteRule ^\.well-known/ - [L]
WordPress plugin: Plugin "security.txt" (search the plugin directory) lets you configure this in the WordPress backend without FTP.
✓ How to verify it works: Open https://your-domain.com/.well-known/security.txt in a browser — content must be visible (no 404).
No external reporting endpoints detected.
No cookie consent banner detected — even though trackers or third-party cookies are present! This is a GDPR violation without prior consent.
GDPR/ePrivacy violation: your site loads trackers or third-party cookies but shows NO consent banner. Consent must be explicit, informed, granular and given BEFORE the first tracker fires. A "this site uses cookies — OK" notice is not sufficient.
WordPress plugin: Recommendations: "Complianz" (free, GDPR-focused, thorough wizard) OR "Real Cookie Banner" (free, knows many services automatically) OR "Borlabs Cookie" (paid, most thorough). Critical setup: 1) run the wizard and configure each service you use, 2) enable "do NOT load scripts before consent", 3) banner must contain: equally weighted "Reject" button next to "Accept", granular per category (statistics/marketing), link to the privacy policy.
✓ How to verify it works: Incognito → load site → banner appears immediately (before any tracker activity). F12 → Network → BEFORE clicking "Accept": no requests to google-analytics.com, facebook.com etc. → AFTER clicking "Accept": then yes.
Privacy policy linked: "Datenschutzerklärung" (/datenschutz).
Legal notice linked: "Impressum" (/impressum).
Privacy policy page is accessible (HTTP 200).
All missing security headers combined into one block. Append this block to the end of your .htaccess — done. 5 headers will be set.
The Content-Security-Policy above deliberately includes 'unsafe-inline' for both style-src and script-src. This does NOT provide full XSS protection — it's a pragmatic trade-off, not a bug.
Why? A typical WordPress setup (theme + 5-15 plugins) emits 10-50 different inline <script> blocks into the HTML: jQuery init, slider init, cookie banner, tracking, GTM, web vitals, lazy-load, speculation rules and so on. A strict script-src 'self' blocks them all — the site becomes visually and functionally broken (blank slider, broken cookie banner, dead plugins).
Consequence for scoring: Sites running WordPress with plugins can score at most ~75-85 points in the CSP category in this app — the full 100% rating is only achievable when inline code is signed via nonce or hash (technically demanding, breaks on every theme/plugin update).
Paths to full XSS protection (in increasing complexity):
Anyone who doesn't take one of these paths lives with 'unsafe-inline' — like about 95% of all production WordPress sites on the web. The other CSP directives still protect: default-src 'self' blocks external resources, object-src 'none' bans Flash/Java, frame-ancestors 'self' prevents clickjacking, base-uri 'self' prevents base-tag hijacking. Not maximum protection, but realistic protection for WP reality.
Append this block to the end of your .htaccess in the web root — done.
<IfModule mod_headers.c>
Header always set Content-Security-Policy "default-src 'self'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; font-src 'self' https: data:; object-src 'none'; frame-ancestors 'self'; base-uri 'self'; upgrade-insecure-requests"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), accelerometer=(), gyroscope=(), magnetometer=(), interest-cohort=(), browsing-topics=()"
</IfModule>
Insert this block ABOVE the "# BEGIN WordPress" line, otherwise WP overwrites it on permalink changes.
# BEGIN WebForensik Security
<IfModule mod_headers.c>
Header always set Content-Security-Policy "default-src 'self'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; font-src 'self' https: data:; object-src 'none'; frame-ancestors 'self'; base-uri 'self'; upgrade-insecure-requests"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), accelerometer=(), gyroscope=(), magnetometer=(), interest-cohort=(), browsing-topics=()"
</IfModule>
# END WebForensik Security
If your host disallows .htaccess changes: append this PHP snippet to the end of your CHILD theme's functions.php. Back up first — NEVER edit the parent theme, it gets overwritten on updates.
add_action('send_headers', function () {
header("Content-Security-Policy: default-src 'self'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; font-src 'self' https: data:; object-src 'none'; frame-ancestors 'self'; base-uri 'self'; upgrade-insecure-requests");
header("Referrer-Policy: strict-origin-when-cross-origin");
header("X-Content-Type-Options: nosniff");
header("X-Frame-Options: SAMEORIGIN");
header("Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=(), accelerometer=(), gyroscope=(), magnetometer=(), interest-cohort=(), browsing-topics=()");
});
| Header | Value |
|---|---|
| accept-ranges | bytes |
| age | 0 |
| cache-control | public,max-age=0,must-revalidate |
| cache-status | "Netlify Edge"; fwd=miss |
| content-encoding | br |
| content-length | 833 |
| content-type | text/html; charset=UTF-8 |
| date | Mon, 06 Jul 2026 19:30:59 GMT |
| etag | "3ba39c78955f014d3e703c85ec6aa1de-ssl-df" |
| server | Netlify |
| strict-transport-security | max-age=31536000 |
| vary | Accept-Encoding |
| x-nf-request-id | 01KWWEK52XQSG5ZAH17Z4H16NC |