Impressum and Datenschutz, in both languages — and stop logging IPs

Four pages: /imprint/ and /privacy/ in English, /de/impressum/ and
/de/datenschutz/ in German, linked from the footer of the matching language.
The German version is the authoritative one; the English one says so.

Every fact in them was checked rather than assumed. The hosting section comes
from the Art. 28 processing agreement with Hetzner (EU/EEA only, DPO appointed,
breach notification, ISO 27001, BSI C5 Type 2); the server location comes from
the provider's API and is Nuremberg; the "no cookies, no third-party requests,
nothing in your browser" claim comes from loading the live page in a browser
that records every outgoing request — all of them went to this domain, with
zero cookies and an empty localStorage.

nginx now logs without the client address. The default format was writing
$remote_addr and X-Forwarded-For, so a static page that needs no personal data
at all was storing the IP of everyone who looked at it. The log keeps time,
request, status, size, referrer and user agent — enough to operate the site,
minus the field that identifies a person. Data minimisation beats a retention
policy you then have to honour.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nh4A79bbqT9dcd2inzYUdg
This commit is contained in:
tillandClaude Opus 5 committed 2026-09-15 16:02:50 +00:00
1 parent 71ffedfaa4
commit bfcfd14625
10 files changed
+299 -3

No files matched your search

+77
View File
@@ -0,0 +1,77 @@
---
import '@fontsource/space-grotesk/400.css';
import '@fontsource/space-grotesk/500.css';
import '@fontsource/space-grotesk/700.css';
import '@fontsource/space-mono/400.css';
import '@fontsource/space-mono/700.css';
import '../styles/tokens.css';
import '../styles/site.css';
import type { LegalPage } from '../lib/legal';
interface Props {
page: LegalPage;
lang: 'en' | 'de';
canonical: string;
otherHref: string;
otherLabel: string;
backHref: string;
backLabel: string;
siblingHref: string;
siblingLabel: string;
}
const { page, lang, canonical, otherHref, otherLabel, backHref, backLabel, siblingHref, siblingLabel } = Astro.props;
const home = lang === 'de' ? '/de/' : '/';
---
<!doctype html>
<html lang={lang}>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta name="theme-color" content="#EAEAE3" />
<title>{page.title} · Klarbild</title>
<meta name="robots" content="noindex, follow" />
<link rel="canonical" href={canonical} />
</head>
<body>
<header class="top">
<div class="shell top-in">
<a class="brand" href={home}><span class="regmark"></span>klarbild</a>
<nav>
<a href={backHref}>{backLabel}</a>
<a class="langswitch" href={otherHref} hreflang={otherLabel.toLowerCase()}>{otherLabel}</a>
</nav>
</div>
</header>
<main class="legal shell">
<span class="kicker">{page.updated}</span>
<h1>{page.title}</h1>
{page.intro && <p class="lead">{page.intro}</p>}
{page.blocks.map((b) => (
<section class="lblock">
{b.h && <h2>{b.h}</h2>}
{b.rows && (
<dl class="lrows">
{b.rows.map(([k, v]) => (<><dt>{k}</dt><dd>{v}</dd></>))}
</dl>
)}
{b.p && <p>{b.p}</p>}
{b.html && <p set:html={b.html} />}
</section>
))}
<p class="lsibling"><a href={siblingHref}>{siblingLabel}</a></p>
</main>
<footer>
<div class="shell foot-in">
<a class="brand" href={home}><span class="regmark"></span>klarbild</a>
<a href={backHref}>{backLabel}</a>
<a href={siblingHref}>{siblingLabel}</a>
<a href="https://github.com/tillheidrich/klarbild" rel="noopener">GitHub</a>
</div>
</footer>
</body>
</html>