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
41 lines
1.6 KiB
Nginx Configuration File
41 lines
1.6 KiB
Nginx Configuration File
# Access log without the client address.
|
|
#
|
|
# The default format writes $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. This format keeps what is useful for operating the site — when, what,
|
|
# which status, how big, from where, with what — and drops the one field that
|
|
# identifies a person. Data minimisation is cheaper than a retention policy.
|
|
log_format noip '[$time_local] "$request" $status $body_bytes_sent '
|
|
'"$http_referer" "$http_user_agent"';
|
|
|
|
server {
|
|
listen 80;
|
|
access_log /var/log/nginx/access.log noip;
|
|
server_name _;
|
|
root /usr/share/nginx/html;
|
|
index index.html;
|
|
|
|
# Hashed asset file names, so they can be cached hard. The HTML is not hashed
|
|
# and must not be, or a redeploy would go unnoticed for a year.
|
|
location /_astro/ {
|
|
add_header Cache-Control "public, max-age=31536000, immutable";
|
|
try_files $uri =404;
|
|
}
|
|
location = /index.html {
|
|
add_header Cache-Control "no-cache";
|
|
}
|
|
|
|
# This page loads nothing from anywhere else, so it can say so.
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
|
add_header X-Frame-Options "DENY" always;
|
|
add_header Content-Security-Policy "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; font-src 'self'; connect-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'" always;
|
|
|
|
gzip on;
|
|
gzip_types text/css application/javascript image/svg+xml application/json;
|
|
gzip_min_length 1024;
|
|
|
|
location / { try_files $uri $uri/ /index.html; }
|
|
error_page 404 /index.html;
|
|
}
|