diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..52ed983
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+.DS_Store
+.playwright-cli/
+node_modules/
+npm-debug.log*
diff --git a/Dockerfile b/Dockerfile
index 8073f20..a25c8a6 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -5,8 +5,7 @@ LABEL org.opencontainers.image.description="Statische Website der Hardtschule Ka
LABEL org.opencontainers.image.source="https://git.servercentral.org/jonas/hardtschule-karlsruhe.de"
COPY nginx.conf /etc/nginx/conf.d/default.conf
-COPY index.html impressum.html datenschutz.html styles.css script.js /usr/share/nginx/html/
-COPY public/ /usr/share/nginx/html/public/
+COPY src/website/ /usr/share/nginx/html/
EXPOSE 80
diff --git a/README.md b/README.md
index 0f9db77..23b548d 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,35 @@
Ein eigenständiger, responsiver Website-Prototyp auf Basis öffentlich auffindbarer Informationen der bisherigen Schulwebsite, des Webarchivs und der Stadt Karlsruhe.
+## Öffentliches Website-Verzeichnis
+
+Alle Dateien, die auf einen Webspace gehören, liegen ausschließlich in:
+
+```text
+src/website/
+├── .htaccess
+├── index.html
+├── impressum.html
+├── datenschutz.html
+├── barrierefreiheit.html
+├── styles.css
+├── script.js
+└── public/
+ └── images/
+```
+
+Für einen Upload oder eine Synchronisation muss deshalb nur der **Inhalt von `src/website/`** in das Zielverzeichnis der Domain übertragen werden. Die versteckte Datei `.htaccess` muss dabei eingeschlossen sein. Dateien aus dem Projektstamm wie `Dockerfile`, `package.json`, `server.mjs` oder `.gitea/` gehören nicht auf den Webspace.
+
+## Auf IONOS/1&1 veröffentlichen
+
+1. Im IONOS-Konto unter **Domains & SSL** prüfen, mit welchem Webspace-Verzeichnis die Domain verbunden ist.
+2. Das SSL-Zertifikat für `hardtschule-karlsruhe.de` aktivieren.
+3. Per SFTP oder Webspace-Explorer den vollständigen Inhalt von `src/website/` in genau dieses Domain-Verzeichnis übertragen.
+4. Darauf achten, dass `.htaccess` mit übertragen wurde; die empfohlenen Dateirechte sind `644`.
+5. Anschließend `https://hardtschule-karlsruhe.de/`, die drei rechtlichen Unterseiten und ein Bild unter `public/images/` testen.
+
+Die `.htaccess` erzwingt HTTPS, deaktiviert Verzeichnislisten, aktiviert gzip-Komprimierung und Browser-Caches und setzt grundlegende Sicherheits-Header. Solange das SSL-Zertifikat noch nicht aktiv ist, muss der entsprechend kommentierte HTTPS-Block vorübergehend auskommentiert werden.
+
## Lokal ansehen
```sh
@@ -59,9 +88,17 @@ Der unveränderliche Commit-Tag erlaubt bei Bedarf ein gezieltes Rollback. `late
- Stadt Karlsruhe: Städtischer Schülerhort an der Hardtschule
- Werkraum Karlsruhe: Theaterprojekte 2023/24
+## Barrierefreiheit
+
+Die Seiten sind auf WCAG 2.2, Konformitätsstufe AA, ausgerichtet. Dazu gehören unter anderem semantische Seitenstrukturen, vollständige Tastaturbedienung, gut sichtbare Fokusmarkierungen, ausreichende Kontraste, Alternativtexte, große Bedienflächen, reduzierte Bewegung auf Wunsch sowie eine eigene Erklärung zur Barrierefreiheit mit Rückmeldemöglichkeit.
+
+Automatisierte Prüfungen ersetzen keine Tests mit assistiven Technologien und betroffenen Nutzer*innen. Nach größeren inhaltlichen oder gestalterischen Änderungen sollten deshalb erneut automatisierte und manuelle Tests durchgeführt werden.
+
## Vor einer Veröffentlichung
- aktuelle Termine, Materiallisten und Pläne ergänzen
- Kontaktangaben und Zuständigkeiten von der Schule bestätigen lassen
- Impressum und Datenschutz an Hosting und tatsächliche Dienste anpassen
+- Erklärung zur Barrierefreiheit fachlich prüfen und mindestens jährlich aktualisieren
+- Informationen in Deutscher Gebärdensprache und eine fachlich geprüfte Fassung in Leichter Sprache ergänzen
- Nutzungsfreigabe für die Gebäudefotos dokumentieren (Dirk Altenkirch / Stadt Karlsruhe); das Hauptmotiv ist ein gekennzeichnetes KI-generiertes Symbolbild
diff --git a/nginx.conf b/nginx.conf
index 3a0dd51..1af180e 100644
--- a/nginx.conf
+++ b/nginx.conf
@@ -24,6 +24,12 @@ server {
return 200 "ok\n";
}
+ location ~ /\.ht {
+ deny all;
+ access_log off;
+ log_not_found off;
+ }
+
location = / {
expires -1;
try_files /index.html =404;
diff --git a/server.mjs b/server.mjs
index 7d7a42f..45d2fe5 100644
--- a/server.mjs
+++ b/server.mjs
@@ -1,8 +1,8 @@
import { createReadStream, existsSync, statSync } from "node:fs";
import { createServer } from "node:http";
-import { extname, join, normalize } from "node:path";
+import { extname, join, relative, resolve, sep } from "node:path";
-const root = process.cwd();
+const root = resolve(process.cwd(), "src", "website");
const port = Number(process.env.PORT || 4173);
const mimeTypes = {
@@ -19,10 +19,16 @@ const mimeTypes = {
createServer((request, response) => {
const pathname = decodeURIComponent(new URL(request.url, `http://${request.headers.host}`).pathname);
const requested = pathname === "/" ? "/index.html" : pathname;
- const safePath = normalize(requested).replace(/^(\.\.(\/|\\|$))+/, "");
- let filePath = join(root, safePath);
+ let filePath = resolve(root, `.${requested}`);
+ const relativePath = relative(root, filePath);
+ const pathSegments = relativePath.split(sep);
- if (!filePath.startsWith(root) || !existsSync(filePath)) {
+ if (
+ relativePath === ".." ||
+ relativePath.startsWith(`..${sep}`) ||
+ pathSegments.some((segment) => segment.startsWith(".")) ||
+ !existsSync(filePath)
+ ) {
response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
response.end("Nicht gefunden");
return;
@@ -32,6 +38,12 @@ createServer((request, response) => {
response.writeHead(200, {
"Content-Type": mimeTypes[extname(filePath).toLowerCase()] || "application/octet-stream",
"Cache-Control": "no-cache",
+ "Content-Security-Policy":
+ "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; form-action 'self'",
+ "Permissions-Policy": "camera=(), microphone=(), geolocation=()",
+ "Referrer-Policy": "strict-origin-when-cross-origin",
+ "X-Content-Type-Options": "nosniff",
+ "X-Frame-Options": "SAMEORIGIN",
});
createReadStream(filePath).pipe(response);
}).listen(port, "127.0.0.1", () => {
diff --git a/src/website/.htaccess b/src/website/.htaccess
new file mode 100644
index 0000000..5fbf244
--- /dev/null
+++ b/src/website/.htaccess
@@ -0,0 +1,60 @@
+# Hardtschule Karlsruhe – Apache/IONOS Webhosting
+
+Options -Indexes -MultiViews
+DirectoryIndex index.html
+AddDefaultCharset UTF-8
+
+# HTTPS erzwingen. Voraussetzung: Das SSL-Zertifikat ist im IONOS-Konto aktiv.
+
+ RewriteEngine On
+ RewriteCond %{SERVER_PORT} !=443
+ RewriteRule ^(.*)$ https://hardtschule-karlsruhe.de/$1 [R=301,L]
+
+
+# Korrekte Auslieferung moderner Bildformate.
+
+ AddType image/webp .webp
+
+
+# Kompression für Textdateien.
+
+ AddOutputFilterByType DEFLATE text/plain
+ AddOutputFilterByType DEFLATE text/html
+ AddOutputFilterByType DEFLATE text/css
+ AddOutputFilterByType DEFLATE application/javascript
+ AddOutputFilterByType DEFLATE application/json
+ AddOutputFilterByType DEFLATE application/xml
+ AddOutputFilterByType DEFLATE image/svg+xml
+
+
+# Sinnvolle Browser-Caches: kurze Laufzeit für Code, lange für Bilder.
+
+ ExpiresActive On
+ ExpiresByType text/html "access plus 0 seconds"
+ ExpiresByType text/css "access plus 1 hour"
+ ExpiresByType application/javascript "access plus 1 hour"
+ ExpiresByType image/jpeg "access plus 30 days"
+ ExpiresByType image/png "access plus 30 days"
+ ExpiresByType image/webp "access plus 30 days"
+ ExpiresByType image/svg+xml "access plus 30 days"
+
+
+
+ Header always set X-Content-Type-Options "nosniff"
+ Header always set X-Frame-Options "SAMEORIGIN"
+ Header always set Referrer-Policy "strict-origin-when-cross-origin"
+ Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
+ Header always set Content-Security-Policy "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; form-action 'self'"
+
+
+ Header set Cache-Control "no-cache, must-revalidate"
+
+
+
+ Header set Cache-Control "public, max-age=3600"
+
+
+
+ Header set Cache-Control "public, max-age=2592000"
+
+
diff --git a/src/website/barrierefreiheit.html b/src/website/barrierefreiheit.html
new file mode 100644
index 0000000..d6d816d
--- /dev/null
+++ b/src/website/barrierefreiheit.html
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+ Barrierefreiheit · Hardtschule Karlsruhe
+
+
+
+ Zum Inhalt springen
+
+
+
+
+
+ Barrierefreiheit
+ Erklärung zur Barrierefreiheit
+
+ Die Hardtschule Karlsruhe ist bemüht, diese Website in Einklang mit § 10 Absatz 1 des Landes-Behindertengleichstellungsgesetzes Baden-Württemberg (L-BGG) barrierefrei zugänglich zu machen.
+
+
+ Geltungsbereich
+ Diese Erklärung gilt für die Website hardtschule-karlsruhe.de einschließlich der Seiten Impressum und Datenschutz.
+
+ Stand der Vereinbarkeit
+
+ Die Website ist nach einer Selbstbewertung teilweise mit den Anforderungen des § 10 Absatz 1 L-BGG und der Barrierefreie-Informationstechnik-Verordnung (BITV 2.0) vereinbar.
+
+
+ Nicht barrierefreie Inhalte
+
+ Erläuterungen zu den wesentlichen Inhalten in Deutscher Gebärdensprache stehen noch nicht zur Verfügung.
+ Eine fachlich geprüfte Fassung der wesentlichen Inhalte in Leichter Sprache steht noch nicht zur Verfügung.
+ Für Inhalte verlinkter externer Websites kann die Hardtschule keine vollständige Barrierefreiheit gewährleisten.
+
+ Wir arbeiten daran, die noch bestehenden Barrieren zu beseitigen. Benötigen Sie Informationen in einer anderen Form, kontaktieren Sie uns bitte.
+
+ Erstellung dieser Erklärung
+ Diese Erklärung wurde am 27. Juli 2026 erstellt und zuletzt am 27. Juli 2026 überprüft.
+
+ Grundlage war eine Selbstbewertung mit automatisierten Prüfungen sowie manuellen Tests von Tastaturbedienung, Fokusführung, Vergrößerung und Umbruch, Kontrasten, Seitenstruktur und zugänglichen Namen.
+
+
+ Rückmeldung und Kontakt
+
+ Sind Ihnen Barrieren aufgefallen? Teilen Sie uns bitte mit, auf welcher Seite und bei welchem Inhalt das Problem auftritt. Wenn möglich, nennen Sie auch den verwendeten Browser oder die eingesetzte assistive Technik.
+
+
+ Hardtschule Karlsruhe
+ Hardtstraße 3
+ 76185 Karlsruhe
+ Telefon: 0721 133-4686
+ E-Mail:
+ poststelle@hardtschule-ka.schule.bwl.de
+
+
+ Schlichtungsverfahren
+
+ Wenn Sie innerhalb von vier Wochen keine oder keine zufriedenstellende Antwort auf Ihre Rückmeldung erhalten, können Sie sich an die Schlichtungsstelle des Landeszentrums Barrierefreiheit wenden. Das Schlichtungsverfahren ist kostenlos.
+
+
+ Landeszentrum Barrierefreiheit – Schlichtungsstelle
+ Else-Josenhans-Straße 6
+ 70173 Stuttgart
+ Telefon: 0711 123 39375
+ E-Mail: schlichtung@barrierefreiheit.bwl.de
+ Website: barrierefreiheit-bw.de
+
+ Auf die Möglichkeit des Verbandsklagerechts nach § 12 Absatz 1 Satz 1 Nummer 4 L-BGG wird hingewiesen.
+
+
+
+ Impressum
+ Datenschutz
+
+
+
+
diff --git a/datenschutz.html b/src/website/datenschutz.html
similarity index 79%
rename from datenschutz.html
rename to src/website/datenschutz.html
index 407afaf..deb31c5 100644
--- a/datenschutz.html
+++ b/src/website/datenschutz.html
@@ -4,25 +4,27 @@
+
Datenschutz · Hardtschule Karlsruhe
+ Zum Inhalt springen
-
+
Rechtliches
Datenschutz
Verantwortliche Stelle
- Hardtschule Karlsruhe Hardtstraße 3 76185 Karlsruhe Telefon: 0721 133-4686 E-Mail: schulleitung@hardtschule-karlsruhe.de
+ Hardtschule Karlsruhe Hardtstraße 3 76185 Karlsruhe Telefon: 0721 133-4686 E-Mail: poststelle@hardtschule-ka.schule.bwl.de
Datenverarbeitung auf dieser Website
Der vorliegende Prototyp verwendet keine Analysewerkzeuge, keine Werbetracker, keine extern geladenen Schriftarten und setzt selbst keine Cookies. Die Kontaktlinks öffnen lediglich die Telefon- oder E-Mail-Anwendung des jeweiligen Geräts.
Server-Protokolle
@@ -33,6 +35,10 @@
Betroffene Personen haben im gesetzlichen Rahmen insbesondere Rechte auf Auskunft, Berichtigung, Löschung, Einschränkung der Verarbeitung und Beschwerde bei der zuständigen Datenschutzaufsicht.
Vor Veröffentlichung erforderlich: Diese Datenschutzhinweise müssen an den tatsächlichen Hostinganbieter, mögliche Formulare, eingebundene Dienste und die Datenschutzorganisation des Schulträgers angepasst und fachlich geprüft werden.
+
+ Impressum
+ Barrierefreiheit
+