56 lines
3.1 KiB
ApacheConf
56 lines
3.1 KiB
ApacheConf
Options -MultiViews -Indexes
|
|
DirectoryIndex index.html
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Routing
|
|
# ---------------------------------------------------------------------------
|
|
RewriteEngine On
|
|
|
|
# Serve the prerendered pages produced by prerender.js.
|
|
# /de/technical is a directory, not a file, so the SPA fallback below would
|
|
# otherwise swallow it and ship the empty shell to crawlers.
|
|
RewriteCond %{REQUEST_FILENAME} -d
|
|
RewriteCond %{REQUEST_FILENAME}/index.html -f
|
|
RewriteRule ^(.+?)/?$ $1/index.html [L]
|
|
|
|
# SPA fallback, only for paths that are neither a file nor a directory.
|
|
RewriteCond %{REQUEST_FILENAME} !-f
|
|
RewriteCond %{REQUEST_FILENAME} !-d
|
|
RewriteRule ^ index.html [QSA,L]
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Security headers
|
|
# ---------------------------------------------------------------------------
|
|
<IfModule mod_headers.c>
|
|
# script-src keeps 'unsafe-inline' on purpose: Vite injects an inline
|
|
# modulepreload polyfill, and index.html carries four inline JSON-LD blocks
|
|
# that browsers also evaluate against script-src. Removing it silently breaks
|
|
# both the app and the structured data.
|
|
# style-src likewise: React sets inline style attributes (--reveal-delay).
|
|
Header always set Content-Security-Policy "default-src 'self'; base-uri 'self'; font-src 'self'; img-src 'self' data:; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src 'self' https://api.sascha-bach.de; object-src 'none'; frame-ancestors 'none'; form-action 'self' mailto:; upgrade-insecure-requests"
|
|
|
|
# No 'preload' yet - that directive is practically irreversible and should
|
|
# only be added once every subdomain is permanently HTTPS.
|
|
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains"
|
|
|
|
Header always set Referrer-Policy "strict-origin-when-cross-origin"
|
|
Header always set X-Content-Type-Options "nosniff"
|
|
Header always set X-Frame-Options "DENY"
|
|
Header always set Cross-Origin-Opener-Policy "same-origin"
|
|
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), browsing-topics=()"
|
|
|
|
# Agent discovery documents (ported from the former vercel.json).
|
|
Header always set Link "</llms.txt>; rel=\"alternate\"; type=\"text/markdown\", </llms-full.txt>; rel=\"alternate\"; type=\"text/markdown\", </AGENTS.md>; rel=\"alternate\"; type=\"text/markdown\", </.well-known/agents.json>; rel=\"agents\"; type=\"application/json\", </.well-known/agent-card.json>; rel=\"agent-card\"; type=\"application/json\", </.well-known/webmcp.json>; rel=\"service-desc\"; type=\"application/json\", </.well-known/mcp.json>; rel=\"service-desc\"; type=\"application/json\""
|
|
</IfModule>
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Keep dotfiles unreachable (.env, .git remnants, editor leftovers).
|
|
# This matches the final filename only, so /.well-known/agent-card.json stays
|
|
# reachable - its basename does not start with a dot.
|
|
# ---------------------------------------------------------------------------
|
|
<IfModule mod_authz_core.c>
|
|
<FilesMatch "^\.">
|
|
Require all denied
|
|
</FilesMatch>
|
|
</IfModule>
|