# Technical SEO — Score 50/100

## Was funktioniert
- HTTP→HTTPS 308 Permanent Redirect (curl-verifiziert: HTTP/1.1 308)
- www→non-www 308 Permanent Redirect (curl-verifiziert: HTTP/2 308, location: https://koeseo.de/)
- HTTP/2 aktiv + HTTP/3 via alt-svc bereit (alt-svc: h3=":443"; ma=2592000)
- Desktop Lighthouse Performance: 100/100, LCP 0.6s, TBT 0ms, CLS 0
- Basis-Security-Header vorhanden: x-content-type-options: nosniff, x-frame-options: SAMEORIGIN, referrer-policy, permissions-policy
- Meta-Tags korrekt: title, description, robots=index,follow, lang=de, viewport, charset UTF-8
- JSON-LD ProfessionalService Block vorhanden (schema.org, name/url/address/vatID/founder)
- robots meta auf /impressum korrekt: noindex,follow (Legal-Seite zu Recht nicht indexiert)
- /impressum und /datenschutz: HTTP 200 (funktionsfähig)
- Statisches HTML — kein JS-Rendering nötig, Googlebot sieht Content direkt
- Fonts mit font-display: swap in CSS (kein FOIT/FOUT-Problem)
- Saubere URL-Struktur, keine Trailing-Slash-Inkonsistenz (https://koeseo.de ohne Slash liefert 200 direkt)
- Mobile CLS: 0, TBT: 0ms (INP-Proxy) — beide Core-Web-Vitals-Metriken im grünen Bereich

## Findings

### [Critical] robots.txt fehlt — HTTP 404
**Befund:** Live-Check via curl bestätigt: GET https://koeseo.de/robots.txt → HTTP 404. Googlebot bekommt keine Crawl-Direktiven, kein Disallow, und es kann kein Sitemap-Hinweis hinterlegt werden. Ohne robots.txt crawlt Google nach eigener Priorität — unkontrolliert und ohne Referenz auf die Sitemap.

**Fix:** robots.txt im nginx-Root erstellen: User-agent: * / Allow: / / Sitemap: https://koeseo.de/sitemap.xml. Bei nginx-Container: Datei in das document root, dann nginx -s reload.

### [High] XML-Sitemap fehlt — HTTP 404 auf /sitemap.xml und /sitemap_index.xml
**Befund:** Live-Check: curl -sI https://koeseo.de/sitemap.xml → HTTP 404. Gleiches gilt für /sitemap_index.xml. Die drei indexierbaren Seiten (/, /datenschutz; /impressum ist noindex) sind für Google Search Console und Bing Webmaster Tools ohne Sitemap nicht explizit bekanntgemacht.

**Fix:** Statische sitemap.xml erstellen mit nur einer URL: https://koeseo.de/ (lastmod, changefreq: monthly, priority: 1.0). /impressum und /datenschutz weglassen (noindex / niedrige Priorität). Pfad: /sitemap.xml, dann in robots.txt referenzieren und in GSC einreichen.

### [High] Canonical-Tag fehlt auf Homepage und /datenschutz
**Befund:** HTML-Parse der Homepage bestätigt: kein <link rel="canonical"> im <head>. Obwohl HTTP→HTTPS und www→non-www via 308 geregelt sind, fehlt der kanonische Signal für Google bei möglichen URL-Varianten (Tracking-Parameter, Fragment-URLs). /impressum hat ebenfalls keinen Canonical, ist aber noindex.

**Fix:** Im <head> der Homepage hinzufügen: <link rel="canonical" href="https://koeseo.de/">. Auf /datenschutz: <link rel="canonical" href="https://koeseo.de/datenschutz">. Da die Site statisch ist, direkt in die HTML-Dateien einpflegen.

### [High] HSTS (Strict-Transport-Security) fehlt
**Befund:** Header-Check bestätigt: kein Strict-Transport-Security-Header in der HTTP-Antwort. Ohne HSTS können Browser beim ersten Besuch noch über HTTP angesprochen werden (vor dem 308-Redirect). HSTS ist auch Voraussetzung für den HSTS-Preload-List-Eintrag.

**Fix:** In nginx.conf im server-Block für HTTPS: add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; — nginx -s reload. Nach 1-2 Wochen Beobachtung: Domain für HSTS-Preload-Liste einreichen (hstspreload.org).

### [High] Favicon fehlt auf allen Pfaden — generiert unnötige 404-Requests
**Befund:** Live-Check: /favicon.ico, /favicon.svg und /apple-touch-icon.png liefern alle HTTP 404. Jeder Browser-Request auf die Seite löst automatisch einen zusätzlichen Favicon-Fetch aus — 404 kostet Crawl-Budget und erzeugt Server-Logs-Rauschen. Google zeigt kein Favicon in den Suchergebnissen.

**Fix:** Minimales favicon.ico (32×32 oder 16×16) und ein favicon.svg im document root ablegen. Im <head> ergänzen: <link rel="icon" href="/favicon.svg" type="image/svg+xml"> und <link rel="icon" href="/favicon.ico" sizes="any">. Optional: apple-touch-icon.png (180×180) für iOS-Bookmark-Icon.

### [Medium] LCP mobil 3.0s — Needs Improvement (Schwelle: gut < 2.5s)
**Befund:** PageSpeed Insights Lab (mobile, verifiziert via API-Call): LCP = 3.0s, Score 0.78. Ursache: style.css (3.6 KB) blockiert den Render mit est. Savings von 140-160ms (render-blocking-insight Score 0.0 in PSI). Das LCP-Element ist wahrscheinlich der <h1>-Text, da keine Bilder vorhanden sind. Desktop: LCP 0.6s (Score 0.99) — kein Problem. CrUX-Felddaten nicht verfügbar (unzureichender Traffic für Google-Messung).

**Fix:** style.css ist render-blocking weil im <head> ohne defer/async geladen. Optionen: (1) Critical CSS inline in <style> im <head>, restliches CSS per <link rel="stylesheet" media="print" onload="this.media='all'"> lazy-laden. (2) Da die CSS-Datei nur 11KB ist, kann auch der komplette CSS-Inhalt direkt inline in den <head> verschoben werden — dann entfällt der HTTP-Request komplett. Option 2 ist bei einer statischen 7KB-Seite die sauberste Lösung.

### [Medium] Thin Content — Homepage ~150 Wörter, 7.4 KB gesamt, keine Bilder
**Befund:** HTML-Analyse: Homepage hat 4 Service-Cards mit je ~10 Wörtern Text, Hero mit ~15 Wörtern, Kontakt-Block. Gesamt-Seitengewicht 7.4 KB. Keine Bilder, keine Fallstudien, keine Referenzen. Für eine B2B-Agentur die AI-Agenten, Infrastruktur, Kassensysteme und CRM-Workflows verkauft, ist das semantisch zu dünn für Longtail-Rankings auf Begriffe wie 'AI-Agenten Mittelstand', 'eigener Server DSGVO' oder 'Kassensystem TSE Bochum'.

**Fix:** Für jede der 4 Service-Cards 2-3 konkrete Ergebnisse oder Use-Cases ergänzen (z.B. Card 01 AI-Agenten: 'Angebots-Erstellung, Rechnungsverarbeitung, Kunden-Routing — automatisiert'). Eine Referenz-Sektion mit 2-3 anonymisierten Kunden-Cases (Branche, Problem, Ergebnis). Trust-Signale: USt-ID und Gründungsjahr sind im JSON-LD, aber im sichtbaren Text fehlen sie. Zielgröße: 400-600 Wörter Homepage-Content.

### [Medium] og:image fehlt + Twitter/X-Cards fehlen komplett
**Befund:** HTML-Parse bestätigt: Die 5 OG-Tags enthalten kein og:image. Alle twitter:*-Tags fehlen. Bei Social-Sharing (LinkedIn, Xing, WhatsApp, X) erscheint kein Vorschaubild — nur Titel und Description. Für eine B2B-Agentur, die über Social-Channels Leads generiert, ist das ein sichtbarer Qualitätsverlust.

**Fix:** og:image (1200×630 PNG/JPG, max 1MB) erstellen und hinzufügen: <meta property="og:image" content="https://koeseo.de/og-image.png"> + og:image:width / og:image:height. Twitter-Cards: <meta name="twitter:card" content="summary_large_image">, twitter:title, twitter:description, twitter:image. Da die Site statisch ist, direkt in den <head> aller Seiten.

### [Medium] JSON-LD unvollständig: kein telephone, kein sameAs, kein priceRange
**Befund:** Live-Check JSON-LD-Block: ProfessionalService enthält name, description, url, email, address, founder, vatID — aber kein telephone, kein sameAs (LinkedIn/Xing-Profil), kein priceRange, kein serviceType. Außerdem ist die email-Adresse eine Gmail-Adresse (goekhan.koese@gmail.com) statt einer Domain-E-Mail, was für B2B-Vertrauenssignale suboptimal ist.

**Fix:** JSON-LD erweitern um: "telephone": "+49-XXX-XXXXXXX", "sameAs": ["https://www.linkedin.com/in/..."], "priceRange": "€€", "serviceType": ["AI-Agenten", "IT-Infrastruktur", "Kassensysteme", "CRM"], "areaServed": "DE". email-Adresse auf @koeseo.de umstellen (Mailcow-Infrastruktur bereits vorhanden auf koehub.de).

### [Medium] Content-Security-Policy fehlt
**Befund:** Header-Check: kein Content-Security-Policy-Header. Das Kontaktformular sendet via fetch() an https://n8n.koehub.de/webhook/koeseo-contact (externe Domain). Ohne CSP gibt es keinen XSS-Schutz und keine Kontrolle über erlaubte Ressourcen-Quellen.

**Fix:** Zunächst im Report-Only-Modus testen: Content-Security-Policy-Report-Only: default-src 'self'; connect-src 'self' https://n8n.koehub.de; font-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; — nach 1-2 Wochen ohne Reports in echte CSP umwandeln. In nginx.conf als add_header.

### [Low] IndexNow nicht implementiert — kein automatischer Push zu Bing/Yandex/Naver
**Befund:** Live-Check: GET /indexnow.txt → HTTP 404. Kein IndexNow-Key im HTML-Source gefunden. Bei Inhaltsänderungen wird Bing nicht automatisch benachrichtigt.

**Fix:** IndexNow-Key generieren (z.B. via Bing Webmaster Tools oder direkt: openssl rand -hex 16), Schlüsseldatei als /<key>.txt auf Server ablegen (Inhalt = der Key selbst), dann nach Deploy-Schritten API-Call: POST https://api.indexnow.org/indexnow mit JSON-Body {host, key, urlList}. Einmalig, ca. 10 Minuten Aufwand.

### [Low] server: nginx — Versionsinformation in HTTP-Response-Header
**Befund:** HTTP-Header zeigt server: nginx. Zwar ohne Versionsnummer (nginx gibt standardmäßig nur 'nginx' aus), aber die Software-Kennung ist sichtbar und gibt Angreifern einen Hinweis auf den Stack.

**Fix:** In nginx.conf unter http {}: server_tokens off; — damit verschwindet der server-Header komplett. nginx -s reload. Minimale Sicherheitsverbesserung, 1 Zeile Aufwand.

---
*Evidenz: Alle Befunde live geprüft via curl (Header-Checks, Status-Codes, HTML-Parse) und PageSpeed Insights API (PSI v5, mobile + desktop, 2026-07-19). CrUX-Felddaten: nicht verfügbar (API: 'chrome ux report data not found' — zu wenig Traffic für Google-Messung). robots.txt/sitemap/favicon via curl -sI direkt auf Produktionsserver verifiziert. HTML-Source vollständig gelesen und geparst (7406 Bytes). JSON-LD manuell aus HTML extrahiert. Redirect-Chain via curl -L mit -sI auf HTTP und www-Varianten geprüft. CSS (11112 Bytes) auf font-display geprüft. PSI-Rohdaten in /scratchpad/psi_mobile.json und psi_desktop.json gespeichert.*
