# Technisches SEO-Audit – Nachprüfung und Arbeitsstand

**Riccardo · Shopware 6.7.14.1 · Stand 23.09.2026, Fassung 2 nach Gegenprüfung**

Grundlage: das technische SEO-Audit vom 21.09.2026
(`2026-09-21-seo-technik-dev-shop.pdf`). Dieses Dokument prüft jeden Befund
daraus gegen den heutigen Stand nach: was umgesetzt wurde, was offen ist
und wo das ursprüngliche Audit sachlich daneben lag.

## Zum Stand dieser Fassung

Fassung 1 dieses Berichts wurde von ChatGPT gegengeprüft. Die Korrekturen
sind eingearbeitet; die Antworten auf meine Rückfragen stehen in
Abschnitt 8.

**Reichweite dieser Gegenprüfung – wichtig für die Einordnung:** Sie war
eine inhaltliche, rechnerische und quellenbasierte Prüfung des *Textes*.
Die Produktionsmessungen, Datenbankzahlen, Serverkonfiguration,
Messskripte und Aussagen des ursprünglichen Audit-PDF wurden dabei **nicht
technisch reproduziert**. Abschnitt 9 listet auf, was damit weiterhin
ausschließlich auf meinen eigenen Messungen beruht.

**Drei Zahlen dieser Fassung sind neu gemessen** (23.09., nach der
Gegenprüfung) und als solche gekennzeichnet: die fehlende
`Vary`-Kopfzeile im Object Storage (3.5), das Verhalten bei
`Accept-Encoding: identity` (3.5) und die Frage, ob ein Admin-Modul die
Storefront rahmt (8.3).

---

## Prüfsystem und Messmethode

| | |
|---|---|
| Gemessenes System | **Produktion**: `shop.riccardo-zigarette.de` |
| Aufbau | Hetzner Load Balancer (TLS) → Varnish 7.1 → nginx 1.24 (127.0.0.1:8080) → PHP-FPM 8.5 → MariaDB-Galera (3 Knoten), Redis, OpenSearch 2.19, Hetzner Object Storage |
| Frontends | 2 Knoten hinter dem Load Balancer, je eigener Varnish |
| Zustand | Wartungsmodus für die Messung abgeschaltet, danach wieder aktiviert |
| **Labormessung** | Chromium (Playwright), Viewport 390×844, Pixeldichte 2, künstlich gedrosselt auf ein Lighthouse-ähnliches Mobilprofil: 1,6 Mbit/s, 150 ms Latenz, CPU 4× langsamer; Varnish vorgewärmt; drei Durchgänge |
| Seitengewicht | Über das Chrome-Protokoll (`Network.loadingFinished`, `encodedDataLength`) – tatsächlich übertragene Bytes, nicht `content-length` |
| Last | `ab -k -t 10 -c 20` gegen die gecachte Startseite |

**Was diese Messung nicht ist:** Es sind **synthetische Labormessungen**,
keine Nutzerwerte. Felddaten (CrUX) gibt es erst nach dem Go-live und nur
bei ausreichendem Verkehr. Der Lasttest betrifft eine gecachte Startseite,
nicht Warenkorb und Bestellabschluss.

**Lesehilfe zur Beweislage:** *Messwert* = an diesem Tag gemessen.
*Schlussfolgerung* = aus Messwerten abgeleitet. *Schätzung* = begründete
Annahme ohne Messung. Die Bereichsnoten in Abschnitt 1 sind ein eigenes
Bewertungsmodell, kein Industriestandard.

---

## 1. Gesamtbild

Das Ursprungsaudit nannte **62/100**. Die ungewichtete Nachrechnung seiner
neun ausgewiesenen Teilwerte ergibt jedoch **63,3/100** (570/9) – die
genannte Note weicht also leicht von der eigenen Tabelle ab. Um
vergleichbar zu bleiben, rechne ich im Folgenden ebenfalls ungewichtet und
nenne als Entwicklung **63 → 78**.

| Bereich | 21.09. | 23.09. | Grundlage der Änderung |
|---|---|---|---|
| Crawlbarkeit | 75 | **75** | unverändert; Varianten blähen die Sitemap weiter auf |
| Indexierbarkeit | 45 | **70** | Canonical auf allen Seiten, Meta-Texte für 20 Kategorien; Varianten offen |
| Sicherheit (HTTP/Anwendung) | 85 | **92** | Kopfzeilen eindeutig aus einer Quelle, IP-Leck geschlossen, Versionen verborgen; CSP fehlt |
| URL-Struktur | 65 | **65** | unverändert, bewusst erst nach dem Go-live |
| Mobil | 85 | **87** | Buy-Box kürzer, ein Kaufknopf, Kopfzeilen-Symbole ausgerichtet |
| **Performance (Labormessung)** | 30 | **90** | siehe 2.1 – **nur LCP und CLS**, INP nicht bewertbar |
| Strukturierte Daten | 50 | **85** | JSON-LD eingeschaltet |
| JS-Rendering | 95 | **95** | unverändert |
| IndexNow | 40 | **40** | unverändert |

**Ungewichtet: 63 → 78.**

Der ungewichtete Mittelwert erzeugt Scheingenauigkeit: für einen Shop, der
gerade die Domain wechselt, wiegt Indexierbarkeit schwerer als IndexNow.
Mit dem in der Gegenprüfung vorgeschlagenen Gewichtungsmodell (Sicherheit
20 %, Indexierbarkeit 20 %, Crawlbarkeit 15 %, Performance 15 %,
URL-Struktur 10 %, Mobil 8 %, Strukturierte Daten 7 %, JS-Rendering 4 %,
IndexNow 1 %) ergibt sich **63 → 81**. Auch das ist ein transparentes
Modell, kein objektiver Maßstab.

### Zwei Einschränkungen, die keine Note abbildet

**Die Bereichsnote „Sicherheit“ misst nur HTTP und Anwendung.** Die
Betriebssicherheit der Server ist bewusst nicht in den Mittelwert
eingerechnet, weil sie sich nicht sinnvoll mit SEO-Bereichen verrechnen
lässt. Sie steht separat:

> **Betriebssicherheit: kritisch.** Auf beiden Frontends ist SSH öffentlich
> erreichbar, mit `PermitRootLogin yes` und `PasswordAuthentication yes`.
> fail2ban begrenzt seit dem 23.09. automatisierte Versuche, behebt das
> Grundrisiko aber nicht. Siehe 2.8 und 3.6.

**Die Performance-Note deckt nur zwei der drei Core Web Vitals ab.** LCP
und CLS sind gute Laborwerte. **INP ist nicht bewertet** – ein
gewöhnlicher Seitenlade-Test ohne reale oder gezielt ausgeführte
Interaktionen kann INP nicht abschließend beurteilen, und TBT als
Laborindikator wurde nicht erhoben. Die Note 90 gilt deshalb ausdrücklich
für LCP und CLS im Labor, nicht als Gesamturteil über die Core Web Vitals.
*(Quellen: developers.google.com/search/docs/appearance/core-web-vitals;
web.dev/articles/vitals-measurement-getting-started)*

### Go-live-Sperrkriterien

Unabhängig von jeder Punktzahl gilt **„nicht go-live-fähig“**, solange
eines davon offen ist:

1. **Zahlungsfähigkeit** – PayPal läuft im Sandbox-Modus (3.2).
2. **Betriebssicherheit** – Root-Passwortanmeldung über SSH offen (3.6).
3. **Redirects, Canonicals, Indexierbarkeit** – Varianten ungeklärt (3.1),
   Redirect-Map noch nicht gegen die finalen Canonicals geprüft.
4. **Temporäres `noindex` entfernt** – steht in der nginx-Konfiguration
   beider Frontends (Checkliste, Schritt 5).
5. **Funktionsfähiger Bestellabschluss** – End-to-End nicht geprüft.

---

## 2. Was seit dem Audit erledigt ist

### 2.1 Bilder und Ladezeit (Audit 2.1 und „Core Web Vitals fail 30“)

**Befund des Audits:** 4.943 PNG mit Ø 1 MB, Kategorieseite lädt 9,3 MB für
sieben Kacheln, LCP im Labor 32 s, kein WebP.

**Messwert heute:** 4.986 Medien sind WebP, insgesamt 339 MB statt
4.939 MB. Die Umstellung erfolgt automatisch beim Upload (eigenes Plugin
`RiccardoImages`); das im Audit empfohlene Fremdplugin wurde nicht
gebraucht.

Danach blieb die Kategorieseite als einzige über vier Sekunden. Zwei
Ursachen, beide gemessen:

* **Die Kachelbreiten in `sizes` waren an jedem Haltepunkt zu groß**, mobil
  um mehr als die Hälfte: angegeben 500 px, gemessen 310 px. Bei doppelter
  Pixeldichte rechnet der Browser daraus 1000 px und greift zur 1600er
  Fassung – 96 bis 138 KB je Kachel. Das ist Punkt 2.1.3 des Audits, der
  nie umgesetzt wurde.
* **Jede Kachel war `loading="lazy"`**, auch die sofort sichtbare.
  Verzögerte Bilder fordert der Browser erst nach Stil und Layout an: die
  erste Bildanfrage begann bei **2.048 ms**. Die ersten vier Kacheln der
  Trefferliste laden jetzt sofort, die erste mit hoher Priorität –
  Anfragebeginn **280 ms**.

| Kategorieseite, mobil | vorher | nachher |
|---|---|---|
| Bilder im ersten Blick | 599 KB | **268 KB** |
| größte Bilddatei | 138 KB | **56 KB** |
| LCP (Labor) | 4,0 s | **1,9 s** |

**Labormessungen auf der Produktion** (mobil, gedrosselt, drei Durchgänge
stabil):

| Seite | LCP | CLS | INP |
|---|---|---|---|
| Startseite | 2,4 s | 0–0,04 | nicht bewertet |
| Kategorie | 1,9 s | 0,034 | nicht bewertet |
| Artikel | 1,7 s | 0,002 | nicht bewertet |

Zum Vergleich das Audit: LCP 12–32 s, CLS 0,10–0,17 – ebenfalls Laborwerte,
dort auf einem Entwicklungsserver ohne Seiten-Cache.

**Seitengewicht, erster Blick, mobil:** Startseite 969 KB, Kategorie
836 KB, Artikel 731 KB. Vollständig gescrollt: Startseite 2,6 MB,
Kategorie 3,3 MB.

**Antwortzeiten:** warm (Varnish HIT) 8–20 ms, kalt (MISS) 0,34–0,84 s.
Trefferquote 97,7 % bzw. 92,9 % je Knoten. Unter Last 1.060 Anfragen/s bei
20 parallelen Verbindungen, 0 Fehler, 95 % unter 22 ms – gecachte
Startseite, nicht Bestellstrecke.

### 2.2 Logo (Audit 2.4)

Das Audit fand 709 KB PNG. Nach der WebP-Umstellung waren es 109 KB, weil
die Theme-Konfiguration weiterhin auf einen Upload mit 1600 px Breite
zeigte – angezeigt wird das Logo mit 58 px Höhe. Jetzt: **8,6 KB**
(534×174 px, auf 32 Farben reduziert, verlustfrei als WebP), die weiße
Fassung für die dunkle Ansicht 9,1 KB.

Bemerkenswert: für dieses dreifarbige Logo ist **verlustfreies WebP nach
Farbreduktion kleiner als verlustbehaftetes** (8,4 gegen 31,7 KB).

### 2.3 Shopname (Audit 2.3, Teilaspekt)

Stand auf der Produktion auf **„Riccardo Dev“**. Das steht nicht nur in
Titel-Rückfällen, sondern in Bestellbestätigungen, Rechnungen und im
strukturierten Datensatz als Verkäufername. Gesetzt auf **„Riccardo
Onlinestore GmbH“** – der Wert, den der alte Shop laut Datenbankabzug
verwendet hat. Das Audit schlug „Riccardo“ vor; die Entscheidung fiel
bewusst anders, weil Kunden den Namen aus Bestellbestätigungen kennen.

### 2.4 PayPal-Ratenbanner (Audit 3.2)

Fußzeile und Anmeldeseite abgeschaltet. Messwert: auf der Startseite lädt
**kein PayPal-Skript mehr**, auf der Artikelseite bleibt der Banner.

### 2.5 Titel und Beschreibungen (Audit 2.3)

Vorher: Startseite „Riccardo Online“, keine Description; Kategorien trugen
nur ihren Namen. **Keine** der 21 Kategorien hatte einen Meta-Titel.

Jetzt: 20 Kategorien mit eigenem Titel (alle unter 60 Zeichen) und
Beschreibung (109–160 Zeichen). Ohne Meta bleibt nur „Katalog #1“, die
interne Katalogwurzel.

Bewusst **kein Muster** wie „{Kategorie} online kaufen“. Jede Beschreibung
nennt etwas Konkretes – Nikotinstärken, Mischverhältnisse, Anschlussmaße
510/810, den Liquidrechner, die Filialen.

> **Rechtlicher Vorbehalt, verschärft nach Gegenprüfung.** Der Ton ist
> bewusst sachlich gehalten, weil § 19 TabakerzG Werbung in Diensten der
> Informationsgesellschaft weit erfasst. Das begründet **keine
> Rechtssicherheit**. Nach dem Urteil des OLG Bamberg vom 21.01.2026 können
> sachliche Angaben zulässig sein, wenn sie der konkreten Vertragsabwicklung
> dienen; attraktive Produktdarstellungen und Verkaufsanreize können
> dagegen verbotene Werbung darstellen. Eine Meta-Beschreibung soll ihrem
> Zweck nach zum Klicken bewegen – das steht in Spannung zu einer rein
> sachlichen Darstellung. **Die konkreten Texte gehören vor dem Go-live
> anwaltlich geprüft**, insbesondere auf Verkaufsanreize, attraktive
> Darstellung, Superlative, wirtschaftliche Vorteile sowie Gesundheits- und
> Jugendschutzbezug. Dieser Bericht ist keine Rechtsberatung.
> *(§ 19 TabakerzG; OLG Bamberg, 21.01.2026, BeckRS 2026, 1588)*

Quelle und Einspielskript: `dev/daten/meta/`.

### 2.6 Canonical (Audit 3.7)

Vorher hatten **15 von 20 geprüften Seiten gar keinen** – alle
Inhaltsseiten und alle Seiten aus eigenen Plugins. Der Rückfall sitzt jetzt
zentral in der Meta-Vorlage des Themes.

Drei Feinheiten, die beim Nachmessen aufgefallen sind:

* `app.request.pathInfo` ist die **interne** Adresse: `/ueber-uns` kommt als
  `/landingPage/<id>` an. Ein Canonical darauf wäre schlimmer als keiner.
* `/liquidrechner` und `/liquidrechner/` antworten beide mit 200 und hätten
  sich gegenseitig für maßgeblich erklärt. Der Schrägstrich am Ende fällt
  weg, die Startseite ausgenommen.
* **Geblätterte Seiten müssen auf sich selbst zeigen.** Der erste Entwurf
  warf alle Abfrageparameter weg, damit zeigte `/magazin?p=2` auf
  `/magazin`. Der Parameter `p` bleibt jetzt erhalten.

**Offen geblieben und durch die Gegenprüfung aufgeworfen:** Der pauschale
Verzicht auf alle übrigen Parameter ist nicht in jedem Fall richtig. Siehe
8.1 und 3.7.

### 2.7 JSON-LD (Audit 3.5)

Das Feature-Flag `JSON_LD_DATA` war aus. Eingeschaltet liefert Shopware 6.7
bereits alles, was das Audit vermisst hat:

```
Artikel: sku=10000067-3202  gtin13=4055387042927  preis=9.99
         availability=InStock  seller="Riccardo Onlinestore GmbH"
```

Dazu `WebSite`, `Organization` (Name, URL, Logo), `BreadcrumbList`,
`ItemList` und `ProductGroup` mit Varianten. `gtin13` erscheint, wo eine EAN
gepflegt ist (3.246 Artikel).

Die alten Microdata verschwinden dabei: auf allen geprüften Seiten steht
genau eine Auszeichnung statt zweier (0 `itemscope`-Elemente).

Nicht gesetzt: `priceValidUntil`, `aggregateRating` (nur bei echten
Bewertungen), Kontaktdaten in `Organization`.

### 2.8 Sicherheit (HTTP und Anwendung)

* **Die Sicherheits-Kopfzeilen fehlten auf der Produktion vollständig.**
  Dabei kam heraus: **Shopware setzt vier davon selbst**
  (`Framework/Routing/CoreSubscriber.php`: HSTS, X-Frame-Options, nosniff,
  Referrer-Policy) – es war nie der Webserver, der sie auf dem
  Entwicklungssystem geliefert hat. Ohne Gegenmaßnahme kamen sie nach der
  nginx-Ergänzung doppelt an, bei `X-Frame-Options` mit **widersprüchlichen
  Werten** (`deny` gegen `SAMEORIGIN`). Jetzt blendet nginx die Kopfzeilen
  der Anwendung aus; einzige Quelle ist eine Snippet-Datei mit Shopwares
  eigenem Wert `deny`.
* **`sw-maintenance-allowlist` ausgeblendet**: Shopware nannte in jeder
  Wartungsantwort öffentlich die freigeschalteten IP-Adressen.
* `server_tokens off`, `expose_php Off`.
* **Permissions-Policy gesetzt** (im Audit unter „niedrig“ gelistet).

Nachgeprüft auf beiden Knoten, dynamisch und statisch und von außen: jede
Kopfzeile genau einmal, gleicher Wert.

Offen: **Content-Security-Policy** (laut Audit sinnvoll erst nach dem
Go-live im Report-Only-Modus).

---

## 3. Was offen ist

### 3.1 Varianten: 3.249 Seiten mit identischem Titel (Audit 2.2, kritisch)

**Unverändert.** Messwert: 1.546 Vaterartikel, **0 mit Canonical-Variante,
0 mit Hauptvariante**.

Verschoben, weil der Zugang zur Search Console des alten Shops derzeit
nicht besteht.

**Nach der Gegenprüfung überarbeiteter Ansatz:** Nicht unbegrenzt auf den
Zugang warten. Verkaufszahlen des alten Shops sind eine vertretbare
Näherung, aber **kein ausreichendes alleiniges Kriterium**. Zusätzlich zu
berücksichtigen:

* dauerhafte Verfügbarkeit der gewählten Variante,
* stabile URL und interne Verlinkung,
* bestehende Redirect-Ziele und Backlinks,
* abweichende Preise, GTIN, Verfügbarkeit oder Inhalte,
* möglicherweise eigenständige Suchintention einzelner Varianten.

**Vorzug, sofern die Shopware-Architektur es sauber zulässt: eine stabile
Produkt- beziehungsweise Basis-URL statt einer Canonical-Variante, die
sich mit den Verkaufszahlen verschieben kann.** Varianten mit tatsächlich
eigenständigem Suchwert dürfen nicht ungeprüft zusammengefasst werden.
Canonical, interne Links, Sitemap und Redirects müssen anschließend
dieselbe Ziel-URL stützen.

*Hinweis zur Umsetzung:* Das Audit nennt `mainVariantId` als Feld. In 6.7
existiert diese Spalte nicht; der Wert steckt im JSON-Feld
`variant_listing_config`. `JSON_EXTRACT(...) IS NOT NULL` meldet für
JSON-`null` fälschlich „gesetzt“ – korrekt ist
`JSON_TYPE(JSON_EXTRACT(...)) = 'STRING'`.

### 3.2 PayPal im Sandbox-Modus (Audit 2.5, Go-live-Sperrkriterium)

**Unverändert, als Entscheidung des Betreibers.** In der
Produktionsdatenbank stehen ausschließlich `clientIdSandbox` und
`clientSecretSandbox`.

Solange das so ist, darf der Shop nicht öffentlich erreichbar sein: 26
aktive Zahlungsarten bedeuten, dass eine echte Bestellung über einen
anderen Weg zustande kommen könnte, während PayPal – rund 90 % der
Bestellungen – ins Leere läuft. Der Wartungsmodus ist deshalb aktiv.

### 3.3 Schriften (Audit 3.4)

**Unverändert: 10 Dateien, 227 KB** auf der Startseite. Nach den Bildern
der größte verbleibende Posten. Empfehlung des Audits: auf fünf Schnitte
reduzieren.

### 3.4 URL-Schreibweisen (Audit 3.6)

**Unverändert und bewusst so.** `/liquids`, `/liquids/`, `/LIQUIDS` und
`/Liquids/` antworten alle mit 200; der Canonical zeigt einheitlich auf
`/Liquids/`. Normalisierung per 301 erst nach dem Go-live, weil die
Redirect-Map auf der kanonischen Schreibweise aufbaut.

### 3.5 Auslieferung vorkomprimierter Dateien – neuer Befund, mittlere Priorität

**Messwert vom 23.09.:** Die Dateien im Object Storage liegen
vorkomprimiert und werden mit `content-encoding: gzip` ausgeliefert –
**auch bei ausdrücklichem `Accept-Encoding: identity`** (geprüft an
`all.css`: HTTP 200 mit gzip-Kodierung). **Eine `Vary`-Kopfzeile fehlt
vollständig.**

Das ist keine bloße theoretische Abweichung:

* Ein Client ohne gzip-Unterstützung erhält Inhalte, die er nicht dekodieren
  kann. Ob es solche Clients im relevanten Verkehr gibt, ist **zu prüfen**,
  nicht anzunehmen.
* Ohne `Vary: Accept-Encoding` dürfen zwischengeschaltete Caches eine
  ungeeignete Repräsentation ausliefern.

**Zu tun:** `Vary: Accept-Encoding` an den betroffenen Objekten setzen und
prüfen, wie Clients ohne gzip bedient werden.

### 3.6 SSH-Zugang – Go-live-Sperrkriterium

**Messwert vom 23.09.:** In 24 Stunden 23.021 fehlgeschlagene
SSH-Anmeldeversuche auf Frontend 1 und 8.938 auf Frontend 2 – bei
`PermitRootLogin yes`, `PasswordAuthentication yes`, ohne Firewall und
ohne fail2ban. Beide Frontends hängen mit öffentlicher IP am Netz.

**Umgesetzt:** fail2ban auf beiden Knoten (5 Versuche in 10 min → 1 h
Sperre); innerhalb von Sekunden waren die ersten Adressen gesperrt.
**apt-cacher-ng**, das auf beiden Knoten öffentlich auf Port 3142 lauschte,
hört jetzt nur noch lokal und im internen Netz.

**Das behebt das Grundrisiko nicht.** fail2ban begrenzt automatisierte
Angriffe; ein offener Root-Zugang mit Passwort bleibt ein
go-live-relevanter Sicherheitsmangel. Vor dem Go-live mindestens:

1. Anmeldung per Schlüssel einrichten,
2. `PermitRootLogin` auf `prohibit-password` oder `no`,
3. `PasswordAuthentication no`,
4. zusätzlich Beschränkung per Firewall, VPN oder festen Wartungsquellen.

Der Betreiber plant, den SSH-Port nach Abschluss der Entwicklung zu
schließen und nur für Wartungsarbeiten zu öffnen. Das deckt Punkt 4 ab;
die Punkte 1 bis 3 bleiben davon unberührt.

### 3.7 Parameterbehandlung beim Canonical – offene Entscheidung

`/filialen?ort=koeln` kanonisiert derzeit auf `/filialen`. Das ist nur dann
richtig, wenn die gefilterte Ansicht **keine eigenständig wertvolle
Suchzielseite** ist. Ist sie es (Suchanfragen nach Filialen mit Ortsbezug
sind plausibel), gehört dorthin ein selbstreferenzieller Canonical – oder
die Filialen brauchen eigene, sprechende URLs. Soll sie nicht indexiert
werden, sind dafür bewusste Indexierungs- und Crawlregeln nötig; Canonical
allein ist keine verlässliche Crawlsteuerung. **Entscheidung steht aus.**

### 3.8 Weiteres

| Punkt | Stand |
|---|---|
| all.css | 100 KB übertragen (gzip), rund 660 KB entpackt |
| DOM-Größe | 1.800–2.100 Elemente, unverändert |
| 51 aktive Produkte ohne Titelbild | unverändert |
| EAN fehlt bei 1.549 Artikeln | unverändert, betrifft `gtin13` |
| Content-Security-Policy | fehlt; sinnvoll nach dem Go-live im Report-Only-Modus |
| IndexNow | nicht vorhanden |
| 404-Seite | 109 KB HTML auf der Produktion (Audit: 361 KB) |
| Überschriftensprung H1→H3 | auf Startseite und Filialen unverändert; in der Abo-Box behoben |

---

## 4. Korrekturen am Audit vom 21.09.

1. **`mainVariantId` ist in 6.7 keine Datenbankspalte** (siehe 3.1). Die
   Aussage selbst stimmt, aber ein Skript gegen die Spalte läuft auf einen
   Fehler.
2. **Die Apache-Empfehlungen (mod_http2, mod_expires) sind gegenstandslos**:
   die Produktion läuft auf nginx mit Varnish, HTTP/2 ist aktiv.
3. **Die Sicherheitsbewertung ordnet die Quelle falsch zu.** Die 85/100
   kamen von Shopwares `CoreSubscriber`, nicht vom Webserver (2.8). Daraus
   folgt die nicht triviale Nebenwirkung, dass ein „Nachrüsten“ im
   Webserver Dubletten und widersprüchliche Werte erzeugt.
4. **Die Empfehlung eines Fremdplugins für WebP-Thumbnails war nicht nötig.**
5. **Die Checkliste nennt `www.riccardo-zigarette.de`**; vereinbart ist
   `shop.riccardo-zigarette.de`.
6. **Der Titel-Suffix „| Riccardo Dev“** entstand nicht auf allen Seiten;
   der eigentliche Schaden des falschen Shopnamens lag in Mails und
   Dokumenten.
7. **Die eigene Gesamtnote weicht von der eigenen Tabelle ab** (62 statt
   63,3, siehe Abschnitt 1).
8. **„Core Web Vitals fail 30“ war weit überwiegend ein Bilderproblem** auf
   einem Server ohne Seiten-Cache. Das Audit sagt das selbst, gewichtet es
   im Gesamtbild aber wie einen strukturellen Mangel.

---

## 5. Fehler, die mir beim Umsetzen unterlaufen sind

Für die Gegenprüfung relevant, weil sie zeigen, welche Art von Fehler hier
vorkommt:

1. **Canonical auf die interne Adresse.** Erster Entwurf schrieb
   `/landingPage/<id>` statt `/ueber-uns`.
2. **Canonical ohne Blätter-Parameter.** `/magazin?p=2` zeigte auf
   `/magazin`.
3. **Sofortiges Laden zu breit angewandt.** Der erste Wurf betraf auch die
   Schieberegler der Startseite: 280 KB mehr, kein Gewinn.
4. **Doppelte Sicherheits-Kopfzeilen** nach der nginx-Ergänzung, bei
   `X-Frame-Options` mit widersprüchlichen Werten.
5. **Theme-Medien lassen sich nicht über `theme.json` aktualisieren.** Sie
   werden nur bei der Erstinstallation importiert; nach dem Löschen des
   Medieneintrags stand die nackte Medien-ID als Bildadresse im Quelltext.
6. **Nach Twig-Änderungen greift auf der Produktion nichts, bevor nicht
   `systemctl reload php8.5-fpm` gelaufen ist.** Die Messung zeigte
   zunächst „keine Verbesserung“, obwohl die Datei auf dem Server lag.
7. **`JSON_EXTRACT(...) IS NOT NULL`** meldete 1.546 gesetzte
   Hauptvarianten, wo keine einzige gesetzt ist.

---

## 6. Checkliste für den Umschalttag (aktualisiert)

Gestrichen, weil erledigt: Thumbnails, Meta-Texte, Logo, PayPal-Banner,
HTTP/2 und Cache-Header.

1. **Vorab, unabhängig vom Umschalttag: Eigentümerverifizierung
   beziehungsweise Zugriff auf die alte *und* neue Search-Console-Property
   sicherstellen.** Ohne das ist die Adressänderung in Schritt 8 nicht
   durchführbar, und die Variantenfrage (3.1) bleibt unentscheidbar.
2. **Tag −1:** Datenbank, Medien, Konfiguration und Redirect-Map gesichert;
   **Canonical-/Hauptvarianten gesetzt** (offen, 3.1); **PayPal live
   abgenommen** (offen, 3.2); **SSH-Zugang gehärtet** (offen, 3.6);
   Redirect-Map automatisiert gegen die finalen Canonicals geprüft (Status,
   Kette, Ziel-Canonical, richtiges Produkt).
3. Verkaufskanal-Domain auf `https://shop.riccardo-zigarette.de`.
4. `.env.local`: `APP_ENV=prod` (gesetzt); Seiten-Cache liegt bei Varnish;
   `cache:clear`, `theme:compile --sync`, `dal:refresh:index`,
   `sitemap:generate`; Worker und Scheduler laufen.
5. **`X-Robots-Tag: noindex, nofollow` aus der nginx-Konfiguration
   entfernen** – eine Zeile in `/etc/nginx/sites-available/riccardo.conf`,
   auf **beiden** Frontends.
6. **Wartungsmodus abschalten** (`sales_channel.maintenance = 0`), danach
   zwingend `cache:pool:clear cache.object` – sonst bleibt er wirksam.
7. Redirect-Map aktiv; Stichprobe 20 alte URLs → genau ein 301 auf 200.
8. Prüfen: `robots.txt`, `/sitemap.xml` auf der neuen Domain,
   Startseite/Kategorie/Artikel mit `index,follow` und korrektem Canonical,
   kein „Riccardo Dev“ im Quelltext.
9. **Search Console: Adressänderung** – setzt die Verifizierung aus Schritt 1
   voraus und wird *nach* der Umstellung und *nach* Aktivierung der
   Weiterleitungen benutzt. Sitemap einreichen, alte Sitemap entfernen.
   *(support.google.com/webmasters/answer/9370220)*
10. **JTL-Connector auf die Produktionsadresse umstellen.** Er zeigt derzeit
    auf `dev.riccardozigarette.com` (letzter Kontakt 23.09., 10:19); die
    Produktion hat seit dem Umzug keine Wawi-Verbindung (letzter Kontakt
    dort 19.09., also noch vom Altsystem). **Ohne diesen Schritt bekommt der
    Live-Shop keine Bestands- und Preisaktualisierung.** Stichprobe zeigt
    identische Entitäts-IDs auf beiden Systemen, die Zuordnungstabelle der
    Wawi bleibt also gültig; nach dem Umstellen einen Testabgleich fahren und
    das Ergebnis prüfen.
11. Monitoring erste 72 h: 404-Log, Crawl-Statistik, TTFB, Bestellungen und
    PayPal-Abschlüsse.

**Reihenfolgefalle:** Nach jeder Änderung an Plugins oder Twig-Vorlagen auf
der Produktion: `cache:clear` **und** `cache:pool:clear cache.object` **und**
`systemctl reload php8.5-fpm` **und** Varnish leeren – auf beiden Knoten.
Fehlt einer der vier Schritte, wirkt die Änderung nicht, ohne dass etwas
fehlschlägt.

---

## 7. Was weiterhin fehlt

* **Felddaten (CrUX)** – erst nach dem Go-live; erst damit ist **INP**
  beurteilbar.
* **Vollständiger Crawl** (interne Verlinkung, verwaiste Seiten,
  Klicktiefe).
* **Abgleich der Redirect-Map** gegen die finalen Canonicals.
* **Lasttest mit echtem Bestellablauf.**
* **Prüfung von Altersprüfung und Cookie-Zustimmung für Crawler** ohne
  Cookies und JavaScript.

---

## 8. Gegenprüfung: Antworten auf meine acht Fragen

Zusammengefasst aus der Gegenprüfung, mit der Folge für dieses Projekt.

### 8.1 Canonical-Rückfall

Ein selbstreferenzieller Canonical ist auf indexierbaren Inhalts- und
Pluginseiten sinnvoll und wird von Google empfohlen; Paginationsseiten
müssen auf sich selbst zeigen. Beides ist umgesetzt.

**Aber:** Andere Parameter dürfen **nicht pauschal** entfernt werden.
`/filialen?ort=…` darf nur dann auf `/filialen` kanonisieren, wenn die
gefilterte Seite keine eigenständig wertvolle Suchzielseite ist. → offener
Punkt 3.7.
*(developers.google.com: canonicalization; pagination-and-incremental-page-loading;
crawling/docs/faceted-navigation)*

### 8.2 Varianten

Nicht unbegrenzt auf den Search-Console-Zugang warten; Verkaufszahlen sind
eine Näherung, kein ausreichendes Alleinkriterium. Stabile Basis-URL
bevorzugen. → eingearbeitet in 3.1.
*(developers.google.com: designing-a-url-structure-for-ecommerce-sites;
structured-data/product-variants)*

### 8.3 `X-Frame-Options: DENY`

`DENY` verhindert, dass **die Shopseite selbst** gerahmt wird – nicht, dass
sie PayPal- oder andere externe Iframes lädt. Betroffen wären Funktionen,
die Storefront-Seiten same-origin rahmen; diese Fälle sind praktisch zu
testen.

**Messwert vom 23.09.:** In dieser Installation rahmt **kein Admin-Modul
die Storefront**. `iframe` kommt in der Administration nur in `sw-cms`
(YouTube-Element), `sw-extension` und `sw-extension-sdk` vor, keines davon
mit Storefront-Adresse. Der praktische Test mit installierten Apps steht
aus. Mittelfristig erlaubt CSP `frame-ancestors` eine genauere Steuerung.
*(developer.mozilla.org: X-Frame-Options)*

### 8.4 HSTS

`includeSubDomains` auf `shop.riccardo-zigarette.de` erfasst nur Hosts
**darunter**, nicht `riccardo-zigarette.de` oder `www.…`. Vertretbar, wenn
HTTPS für diesen Host und alle darunterliegenden dauerhaft garantiert ist.
Empfohlen wird ein gestuftes Vorgehen: zunächst kurze `max-age`, später
erhöhen. Eine Rücknahme per `max-age=0` erreicht nur Browser, die erneut
erfolgreich per HTTPS zugreifen.

**Folge:** Derzeit steht `max-age=31536000`. **Empfehlung: bis zum Go-live
auf einen kurzen Wert senken und erst danach erhöhen.** Noch nicht
umgesetzt – Entscheidung des Betreibers.
*(developer.mozilla.org: Strict-Transport-Security)*

### 8.5 Sofortiges Laden der ersten vier Kacheln

Vier ist nicht erwiesen falsch, mobil aber wahrscheinlich großzügig. Nur
das wahrscheinliche LCP-Bild sollte `fetchpriority="high"` bekommen – das
ist bereits so.

**Folge:** Zwei gegen vier mit kaltem Cache über mehrere Bildschirmhöhen
und Desktop testen, Entscheidung nach LCP, übertragenen Bytes und
Konkurrenz um kritische Ressourcen. **Noch nicht durchgeführt.**
*(web.dev: fetch-priority; browser-level-image-lazy-loading)*

### 8.6 `sizes`-Werte

Verschiedene Komponenten und Layouts können andere Slotbreiten haben. Die
Messung erfolgte an der Trefferliste im Standard-Kachel-Layout.
**Trefferliste, Schieberegler, Wunschliste, Suche und alternative
Kartenlayouts sind separat über alle Haltepunkte und möglichst DPR 1, 2 und
3 zu prüfen.** Zu kleine Angaben erzeugen sichtbar unscharfe Bilder. **Noch
nicht durchgeführt.**

### 8.7 Meta-Beschreibungen

Sachlicher Ton sinnvoll, begründet aber keine Rechtssicherheit; juristische
Freigabe empfohlen. → eingearbeitet in 2.5.

### 8.8 Bewertungsmaßstab

Der ungewichtete Mittelwert erzeugt Scheingenauigkeit; Go-live-Bewertung
mit harten Sperrkriterien ergänzen. → eingearbeitet in Abschnitt 1.

---

## 9. Was die Gegenprüfung nicht reproduziert hat

Die Gegenprüfung war eine inhaltliche, rechnerische und quellenbasierte
Prüfung dieses Textes. **Nicht praktisch nachgemessen** und damit
ausschließlich durch meine eigenen Messungen belegt:

* LCP-, CLS-, Seitengewichts- und Lasttestwerte,
* Varnish-Trefferquoten und Antwortzeiten,
* Zahl und Gesamtgröße der WebP-Medien,
* Produkt-, Varianten-, EAN- und Bildzählungen,
* Kopfzeilen auf beiden Frontend-Knoten,
* die tatsächliche Datenbankstruktur und `variant_listing_config`,
* Canonicals, JSON-LD, Robots-Regeln und Weiterleitungen über alle
  Seitentypen,
* PayPal-Konfiguration und Zahlungsarten,
* die Messskripte unter `dev/qa/`,
* die Abweichungen gegenüber dem ursprünglichen Audit-PDF.

Sollen diese Punkte als abschließend verifiziert gelten, ist eine separate
technische Reproduktion anhand von Rohdaten, Skripten, Serverkonfiguration
und repräsentativen Live-URLs nötig, mit je Befund: Prüfschritt, Sollwert,
Istwert, Beleg.

---

*Fassung 2, erstellt von Claude (Opus 5) am 23.09.2026 nach Gegenprüfung
durch ChatGPT. Alle Messwerte stammen aus eigenen Messungen am
Produktionssystem an diesem Tag. Messskripte: `dev/qa/`, Meta-Texte:
`dev/daten/meta/`, Betriebsdokumentation: `dev/deploy/UMZUG.md`.*
