Der Aufnahmeantrag der Turnabteilung ging als alte URL in den Einladungen an die neuen Familien raus und lief seit dem Umzug in einen 404, ebenso der Link auf die Turn-Seite. Beide leiten jetzt weiter, dazu alle uebrigen Seiten der alten Navigation unter /08/de/. Die PDF-Regel matcht am Umlaut vorbei: der Dateiname kommt je nach Mailprogramm als NFC, als NFD (so stand er in der alten Datenbank) oder roh als UTF-8 an. Die Sammelregel wirft den alten Query-String weg, sonst haengt mod_rewrite ?group_filter_id=14 ans neue Ziel. Erledigt damit den offenen Punkt F3 aus docs/deploy.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RWoqDYuANFbJDGFQAgaRdT
93 lines
4.7 KiB
ApacheConf
93 lines
4.7 KiB
ApacheConf
RewriteEngine On
|
|
|
|
# --- Kanonische Adresse: HTTPS erzwingen, nackte Domain auf www ---
|
|
# Die Formulare übertragen Name, Adresse und Geburtsdatum — nie über http.
|
|
# Der HTTPS-Redirect ist bewusst host-neutral (funktioniert auch auf einer
|
|
# Test-/Preview-Domain); nur tsv08kulmbach.de wird hart auf www gezogen.
|
|
# Drei unabhängige TLS-Indikatoren, damit kein Redirect-Loop entsteht, falls der
|
|
# Hoster die Verschlüsselung anders durchreicht als erwartet.
|
|
RewriteCond %{HTTPS} !=on
|
|
RewriteCond %{HTTP:X-Forwarded-Proto} !=https
|
|
RewriteCond %{SERVER_PORT} !=443
|
|
RewriteCond %{HTTP_HOST} !^(localhost|127\.0\.0\.1)(:[0-9]+)?$ [NC]
|
|
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
|
|
|
|
RewriteCond %{HTTP_HOST} ^tsv08kulmbach\.de$ [NC]
|
|
RewriteRule ^ https://www.tsv08kulmbach.de%{REQUEST_URI} [R=301,L]
|
|
|
|
# --- Umbenannte Seiten (301) ---
|
|
# Step Aerobic findet nicht mehr statt (31.07.2026), die Seite heißt jetzt
|
|
# „Fitness & Pilates" und liegt unter turnen/fitness-pilates. Der alte Slug war
|
|
# in der Sitemap und ist von Google indexiert, deshalb 301 statt 404 — sonst
|
|
# verliert die Seite ihre Position und alte Links laufen ins Leere.
|
|
# Steht VOR dem Front Controller, sonst würde index.php den Pfad zuerst als
|
|
# unbekannten Slug abfangen und einen 404 rendern.
|
|
RewriteRule ^turnen/step-aerobic-fitness-pilates/?$ /turnen/fitness-pilates [R=301,L]
|
|
|
|
# --- Alte CMS-Adressen (301) ---
|
|
# Die alte Seite lief unter /08/de/<seite>/ und lieferte Dateien aus /userdata/.
|
|
# Beides ist seit dem Umzug tot, die Adressen stehen aber in Einladungen, Aushängen
|
|
# und im Google-Index. Steht wie die Regel darüber VOR dem Front Controller.
|
|
#
|
|
# Der Aufnahmeantrag der Turnabteilung ist der dringendste Fall: er ging so an die
|
|
# neuen Familien raus. Der Dateiname trägt ein „ä" und kommt je nach Mailprogramm in
|
|
# drei Fassungen an: NFC (%C3%A4), NFD (a%CC%88 — genau so stand der Link in der alten
|
|
# Datenbank) oder roh als UTF-8. mod_rewrite prüft den dekodierten Pfad, also matchen
|
|
# wir am Umlaut vorbei, statt eine der drei Fassungen zu raten.
|
|
RewriteRule ^userdata/01_Basis/Beitrittserkl[^/]*\.pdf$ /assets/downloads/beitrittserklaerung-turnen.pdf [R=301,L,NC]
|
|
|
|
# Seiten, die ihren Namen behalten haben. Das abschließende „?" wirft den alten
|
|
# Query-String weg (die Turn-Seite hatte ?group_filter_id=<n> als Disziplin-Filter,
|
|
# 14 war „Allgemeines") — ohne das hängt mod_rewrite ihn ungefragt ans neue Ziel.
|
|
RewriteRule ^08/de/(fussball|jugendfussball|matchcenter|turnen|mitglied-werden|kontakt|historie|impressum|datenschutz)/?$ /$1? [R=301,L]
|
|
|
|
# Seiten, die heute anders heißen
|
|
RewriteRule ^08/de/home/?$ / [R=301,L]
|
|
RewriteRule ^08/de/aktuelles/?$ /news [R=301,L]
|
|
RewriteRule ^08/de/der-verein/?$ /verein [R=301,L]
|
|
RewriteRule ^08/de/der-verein/historie/?$ /historie [R=301,L]
|
|
RewriteRule ^08/de/der-verein/sportheim/?$ /sportheimbuchung [R=301,L]
|
|
RewriteRule ^08/de/personen/?$ /ansprechpartner [R=301,L]
|
|
|
|
# --- Front Controller ---
|
|
RewriteCond %{REQUEST_FILENAME} !-f
|
|
RewriteCond %{REQUEST_FILENAME} !-d
|
|
RewriteRule ^ index.php [L]
|
|
|
|
# --- Security-Header ---
|
|
<IfModule mod_headers.c>
|
|
Header always set X-Content-Type-Options "nosniff"
|
|
Header always set Referrer-Policy "strict-origin-when-cross-origin"
|
|
Header always set X-Frame-Options "SAMEORIGIN"
|
|
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
|
|
# HSTS nur über HTTPS ausliefern (1 Jahr, ohne includeSubDomains — Subdomains
|
|
# wie Webmail liegen nicht in unserer Hand).
|
|
Header always set Strict-Transport-Security "max-age=31536000" env=HTTPS
|
|
# Streng möglich, weil ausnahmslos alles self-hosted ist (CLAUDE.md Regel 1).
|
|
Header always set Content-Security-Policy "default-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'self'"
|
|
</IfModule>
|
|
|
|
# --- Kompression ---
|
|
<IfModule mod_deflate.c>
|
|
AddOutputFilterByType DEFLATE text/html text/css text/javascript application/javascript application/json image/svg+xml
|
|
</IfModule>
|
|
|
|
# --- Caching: Assets 1 Jahr (Busting via ?v=filemtime), HTML nie ---
|
|
<IfModule mod_expires.c>
|
|
ExpiresActive On
|
|
ExpiresByType text/css "access plus 1 year"
|
|
ExpiresByType application/javascript "access plus 1 year"
|
|
ExpiresByType font/woff2 "access plus 1 year"
|
|
ExpiresByType image/jpeg "access plus 1 year"
|
|
ExpiresByType image/png "access plus 1 year"
|
|
ExpiresByType image/webp "access plus 1 year"
|
|
ExpiresByType image/svg+xml "access plus 1 year"
|
|
ExpiresByType image/x-icon "access plus 1 year"
|
|
ExpiresByType video/mp4 "access plus 1 year"
|
|
ExpiresByType text/html "access plus 0 seconds"
|
|
</IfModule>
|
|
|
|
AddType font/woff2 .woff2
|
|
AddDefaultCharset utf-8
|
|
Options -Indexes
|