Security
Content Security Policy
Configured in next.config.ts:
/embed route
Content-Security-Policy: frame-ancestors 'self' https://bttpirineus.org https://www.bttpirineus.orgOnly allows the embed to be loaded in iframes from the BTT Pirineus WordPress site and the app itself.
All other routes
X-Frame-Options: DENYPrevents any other page from being embedded in an iframe.
GPX proxy validation
The /api/gpx proxy only allows GPX files from trusted hosts:
const ALLOWED_HOSTS = ["www.bttpirineus.org", "bttpirineus.org"];Requests for any other hostname return 403. Relative URLs are resolved against https://www.bttpirineus.org.
PostHog reverse proxy
PostHog events are sent through /ingest/* rewrites to https://eu.i.posthog.com/*. This:
- Avoids ad-blocker interference (events go to your own domain)
- Avoids CORS issues
- Keeps PostHog API keys client-side only (public project key, not secret)
Analytics password
The /analytics dashboard and /api/analytics/dashboard endpoint require a password matching the ANALYTICS_PASSWORD environment variable. The password is:
- Stored in
sessionStorage(lost on browser close) - Sent as a query parameter (acceptable since the dashboard is internal-only and served over HTTPS)
Internal user filtering
Internal/admin IPs are detected via /api/analytics/ip-check:
- Reads
x-forwarded-forheader - Checks against a hardcoded IP allowlist
- If internal, PostHog registers
is_internal: trueon the person - Dashboard queries exclude
is_internal = trueevents
Cross-origin iframe safety
When embedded in bttpirineus.org:
- localStorage/cookies may be blocked by the browser — PostHog falls back to
persistence: "memory" - sessionStorage still works for view deduplication within the iframe session
- The
allow="clipboard-write"attribute on iframes enables the share copy-link feature