Profilprodukter: varför vi inte återhämtar oss — och vad som fixas
Vi har i flera veckor jobbat utifrån att raset den 29 juli berodde på tre saker: en komponent som dolde innehåll för Google, långsam server och nio föräldralösa sidor. Alla tre var verkliga och är fixade — men de förklarar inte varför /se, /no och /en föll brett medan startsidan stod stilla. Dagens utredning svarar på det: sajten har haft trasig URL-identitet i nästan varje led (intern länk → redirect → canonical → sitemap → schema), och en scraper-storm sammanföll med Googles omvärdering efter flytten (hypotes om förstärkning — se ruta i avsnitt 2). Inget av det är ett straff. Allt är fixbart.
1. Vad som är uteslutet
Kontrollerat i Google Search Console 23/8: inga manuella åtgärder, inga säkerhetsproblem, inga borttagningar. Host-status grön i 90 dagar (robots.txt-hämtning, DNS, serveranslutning). Googlebot fortsatte crawla ~1 000 sidor/dag genom hela 29 juli. Det var alltså varken straff eller åtkomstproblem.
2. Tidslinjen som data faktiskt visar
Vad vi kan och inte kan bevisa om 29 juli
Serverloggar från juli finns inte (access-loggen var avstängd, Cloudflare Free sparar inga). Kärnkoden bakom URL-problemen är från 11 juni och ingen relevant ändring gjordes 25 juli–2 augusti. Ingen enskild händelse kan därför bindas till dagen. Den mest försvarbara tolkningen: en bred algoritmisk omvärdering träffade en sajt med låg auktoritet (DR 2,1), ~1 s svarstid mot Googlebot under scraper-stormen, och motstridig URL-identitet i alla språkled. Kedjan scraper → överbelastad server → långsam Googlebot → nedjustering är en hypotes (H18) som inte är bevisad: vi ser samtidighet, inte dos–respons, och ingen per-bot-logg finns. Fixarna nedan är rätt oavsett utlösare.
Fas 3-ersättning (Codex, 23/8 kväll): vad de räddade datakällorna faktiskt visar
- Kohortmatris (GSC per URL och dag, veckodagsjusterad): 29/7–4/8 jämfört med juli-baslinjen — SE-produkter 0,34, SE-stad 0,23, /se 0,27, NO-produkter 0,17, EN-produkter 0,04. Motexempel: SE-kategorier 0,95 (föll först 13/8), /no 0,67, root / 1,18 (upp). Raset var alltså inte "allt under /se" — det var produkter, stadssidor och startsidorna, medan kategorisidorna höll i två veckor till.
- Scraper-hypotesen försvagad: Singapore stod för 2 av 5 081 404:or den 29/7 (spiken kom från Frankrike 2 997 och Nederländerna 673). Googlebot-klassen hos Cloudflare fick 4 serverfel 29/7 och 11 den 30/7 (tio timeouts à 125 s) — driftincidenten kom alltså främst efter första rankingfallet.
- Redirect-pulsen var 11–16/7, inte 29/7: 43 % av Googles crawl gick till redirects 11–16/7 (6 760 requests 11/7) när Google upptäckte fel-språk-URL:erna från migrationsbygget; 7 dagar före vs efter 29/7 skiljer bara +2 pp. Ingen ny redirect-regim vid brytpunkten.
- Starkaste tekniska kandidat: commit 454627d8 (20/7, deploy v59) gjorde hela locale-trädet dynamiskt renderat (
await headers()i delad layout) medan root låg utanför — matchar mönstret "root upp, locale-träd ner" men med 9 dagars lagg och utan att förklara SE-kategoriundantaget. Fixad 12/8. Inte kausalt bevisad. - Bekräftat oberoende: keyword.com-panelen 28/7→29/7: 23→10 ord i topp-100, medelrank 45→82, medan 99 serier från nio andra projekt var oförändrade — raset är verkligt, inte mätfel. Ahrefs: referring domains 254 (1/7) → 474 (22/8) utan steg 28→29/7 — juliökningen bör kvalitetsklassas (disavow av 373 domäner gjordes 12/8).
- Det enda direkt testbara som återstår: URL Inspection på 905 stratifierade URL:er (parent/child-par, fel-språk-alias, kategoripar, stadsalias) visar om Google valt fel URL som canonical — körs nu, resultat i nästa revision.
- Går inte att veta längre: Googlebots per-URL-hämtningar i juli, cacheutfall per request, vilket bygge som var aktivt per minut, Googles canonicalval den 29/7.
Codex formulering: "analytiskt fel att sätta en procentsäker rotorsak på någon enskild kandidat." Underlag: /home/admin/scratchpad-fase3-workaround-0823.md + 101 artefaktfiler.
3. Vem scrapern var
Vad vi vet: nätägaren för IP-områdena (whois, APNIC) är Aceville Pte. Ltd., Singapore — Tencent Clouds kontraktsbolag (43.172.x/43.173.x), plus en nod i BytePlus-SG (ByteDance-gruppens enterprise-cloud). Det säger vem som hyr ut servrarna, inte vem som körde trafiken. Det vi såg i augusti-detaljerna (Cloudflare sparar per-IP-data bara 8 dagar, så juli kan inte granskas): ett antal IP:n som var för sig gör 5–190 requests/dag, med Chrome-user-agents i spridda versioner (103–146), mot startsidorna på alla språk, stadssidor och /api/reviews.
Tolkning (hypotes, inte belagd): det är inte en tjänst som identifierar sig (Googlebot, Ahrefs, Semrush gör det) och mönstret liknar inte en konkurrents prisbevakning. Signaturen påminner om det som andra sajter rapporterat 2025–26 från kinesisk molninfrastruktur — datainsamling eller uthyrda proxynätverk — men vi kan inte identifiera aktör eller motiv. Trafik från Singapore fortsätter i låg takt (640–14 267 requests/dag i augusti).
4. Huvudfynden (verifierade live + i kod)
A. 2 007 internt länkade dubblett-URL:er under /no och /en saknar canonical och title
Varje produkt kan nås under /no och /en med sin svenska slug. Den varianten svarar HTTP 200 med fullständig produkt-HTML (251 KB) och schema, men med en <meta refresh=0> till rätt URL, ingen canonical, ingen title — och Product-schemat pekar ut dubblett-URL:en som produktens identitet. Orsak 1: permanentRedirect() anropas inuti en ISR-sida; Next bakar det till meta-refresh i stället för en riktig HTTP 308 (kodkommentaren från juni erkänner det själv). Orsak 2: produktstripen (sedan minst 11 juni) länkade med svensk slug i alla språk. Totalt 5 762 sådana alias är nåbara; 2 007 är internt länkade (987 EN + 1 020 NO, klickdjup 2–3). GSC: 424 NO/EN-sidor med visningar finns inte i sitemapen.
B. 166 produkter har dubbel identitet (parent/child-kategori)
Produkter i 12 underkategorier svarar 200 på både /se/kaffemuggar-med-tryck/… och /se/muggar-med-tryck/…. På SE säger canonical "child" (162 av 166; 4 är self-canonical) men schema och breadcrumbs säger "parent". På NO/EN är båda self-canonical — två fullvärdiga indexerbara identiteter per produkt. 166 produkter × 3 språk = 498 URL-par; 462 av parent-varianterna är internt nåbara.
C. Sitemap, hreflang och statiska sidor motsäger varandra
/en/about-usoch/en/contactligger i sitemap + hreflang men svarar 404./en/bloghar canonical till/en/blogg— som 308:ar tillbaka till/en/blog. Canonical-cirkel på en länk som ligger i footern på varje sida (4 092 inlänkar).- De 12 underkategorierna listas i sitemapen i nästlad form (
/se/muggar-med-tryck/kaffemuggar-med-tryck) som 308:ar — medan de kanoniska URL:erna saknas helt./se/kaffemuggar-med-tryckhar 81 visningar men finns inte i sitemapen. - Sitemapens
lastmod: 94 % av URL:erna "ändrade" senaste två dygnen. Google ignorerar datumet när det är opålitligt. Fixen från 11/8 är inte i produktion. - Sitemapen inkluderar
discontinued- ocharchived-produkter — troligen de 2 948 noindex-sidor Google crawlar förgäves.
D. En tredjedel av Googles crawl går till redirects
GSC Crawl Stats 90 dagar: 31 % av alla Googlebot-requests får 301/308. Google känner till 5 063 "sidor med omdirigering"; i GSC:s exempellista (max 1 000 rader, inte hela populationen) är 85 % NO/EN-produkter med svensk kategori-slug från en äldre build. Därtill 90 redirect-loopar och 451 kedjor med flera hopp i redirect-kartan. JavaScript = 0,1 % av crawlen: robots.txt förbjuder /_next/ uttryckligen för Googlebot (1 376 blockerade URL:er).
E. Google avvisar 5 755 crawlade sidor — i exempellistan är 85 % /en-produkter
Innehållet är inte tunt (≈900 engelska ord, korrekta titlar). Google avvisar dem ändå: /en har EUR-priser men SEK-frakt i schemat, generisk hreflang="en", GB-flagga — sidan motsäger sig själv om vilken marknad den är för. Se avsnitt 7.
F. Övrigt verifierat
- Bara 36 % av produkterna är förrenderade. Supabase-konfigurationen kapar på 1 000 rader och
generateStaticParamspaginerar inte → ~5 500 produktsidor byggs kallt vid första besök (även Googlebots). - Avkortade slugs ger 200: en prefix-matchning (
ilike 'slug%' limit 1) renderar fel produkt för 70 av 100 trunkerade URL:er; 21 slug-kollisioner i databasen, 10 ger fel identitet. - Stadsalias läcker mellan språk:
/se/by/stockholmoch/se/city/stockholmsvarar 200 (by = norsk slug, city = engelsk). - Gamla produkt-URL:er (
/produkter/…,/pf/…) skickas till startsidan i stället för till produkten (soft-404-klass). noindexsätts i dag somnoindex,nofollow— länkkraften stoppas i onödan.
Friskförklarat
Rå HTML innehåller produktlänkar (48–67 per kategorisida, identiskt för Googlebot) — 13/8-fixen är live. Hreflang på produkt- och kategorisidor är komplett och konsekvent. headers()-regressionen från 20/7 är borta ur produktionsbilden. Origin svarar 5–15 ms på varma sidor. Sitemapen är 99,5 % korrekt i form (43 av 9 008 URL:er bryter invarianten) och inga databasprodukter eller kategorier saknas — felen är de 43 (nästlade underkategorier, två 404:or, /produkter) plus lastmod-churnen.
5. Fixplanen — en fix, en commit, ett mätbart acceptansmål
Acceptans mäts genom att köra om invariantcrawlen på den berörda kohorten tills fyndet är 0. Inga "Begär indexering" förrän P0 är live — annars recrawlar Google fel signaler.
| Prio | Fix | Effekt | Status |
|---|---|---|---|
| P0-1 | Produktstripen länkar locale-slug, inte svensk slug (2 rader) | Stoppar de 13 500 länkarna till tomma skal | LIVE 23/8 ~20:50 deploy v266 |
| P0-2 | En enda buildProductPath() för alla produktlänkar (grid, stripes, "relaterade", ItemList, canonical, schema, sitemap) | Sajten slutar skapa fel-språk- och parent-vägar; "relaterade produkter" slutar 308:a | Brief till tillväxt-sessionen |
| P0-3 | Parent→child och fel-språk-slug som riktig HTTP 308 före render (middleware), schema/breadcrumb från canonical | En indexerbar URL per produkt och språk; 5 762 alias blir en-hops-redirect | Brief |
| P0-4 | Ta bort prefix-matchningen; exakt slug, unik ägare, annars 404 | Trunkerade/kolliderande slugs kan inte rendera fel produkt | Brief |
| P0-5 | Ett typat locale-manifest för statiska sidor (sitemap, canonical, hreflang från samma källa) | Inga 404 i sitemap, ingen /en/blog-cirkel, underkategorier i rätt form | Brief |
| P1 | Validera redirect-kartan i build (loop/404/flerhopp = byggfel) · paginera förrendering (36 % → 100 %) · semantisk lastmod + fail-closed sitemap · ta bort /_next/ ur robots + lägg till OAI-SearchBot/Perplexity-policy · alias-normalisering · noindex,follow | Crawl-budget, färskhetssignal, rendering | Brief |
| P1 | Skydd mot scrapers: Cloudflare rate-limit + WAF-regler (verifierade bots undantagna, utländska kunder får challenge — inte block), edge-cache på produktsidor, Telegram-larm från access-loggen | En storm träffar Cloudflare, inte servern; upptäcks snabbt | Codex designar reglerna (förslag, ännu inte applicerat) |
| P2 | /en-strategi (avsnitt 7) | Rätt indexyta för rätt målgrupp | Mikaels beslut, efter P0 |
6. Fas 3-ersättning: vad vi kunde rädda
Planens botlogg-analys var omöjlig (inga loggar). Men: Cloudflares analytics-API har dagsdata tillbaka till 1 juli även på gratisplanen — requests, statuskoder, cache-andel, land. Det gav scraper-fyndet ovan. Per-URL/per-bot-detalj sparas bara 8 dagar, så juli-detaljerna är borta. Sedan 23/8 är Traefiks access-logg påslagen (JSON med user-agent, land, IP) och sparas 31 dagar i Loki — nästa storm syns per user-agent inom minuter. Därtill: GSC-exporten per URL och dag (klar) kan ge kohortvis "vem föll först", och URL Inspection-API:t kan visa om Google valt skal-URL:er som canonical.
7. /en — rätt modell för "utländska kunder som köper i Sverige/Norge"
Målet (Mikael): engelsktalande kunder som ska ha presentreklam till mässor i Sverige/Norge — utan att dränera huvudmarknaderna. Claude och Codex landade i samma rekommendation efter en tydlig skiljelinje:
- Behåll generiskt
hreflang="en"(Codex rättade Claude:en-SEbetyder "engelska för användare i Sverige", medan målgruppen googlar från London, Berlin eller New York före mässan). - Valuta efter marknad, inte språk: SEK/Quizify för Sverige, NOK/Webkompaniet för Norge. I dag motsäger EN-sidan sig själv (GB-flagga, EUR-pris, SEK-frakt).
- Krymp indexytan: indexera /en-startsidan, två landshubbar (promotional products Sweden/Norway), 8–12 EN-kategorier, 5–10 intent-sidor (trade-show giveaways Sweden/Norway, express promotional products, Stockholm/Oslo venue delivery) och en vitlista på 25–100 mässklara produkter + allt som fått klick, leads eller länkar. Övriga EN-produkter
noindex,follow, ur sitemap och hreflang — infört stegvis som ett randomiserat test över 12–16 veckor med beslutsregel. Huvudterm: "promotional products", inte "profile products". - "Drain" är inte bevisat — Google dokumenterar inte "crawled – not indexed" som sajtbred straffsignal. Därför test, inte antagande. Risk att känna till: noindex på produktnivå = tappad Google AI Overviews-citering för de produkterna; intentsidorna och vitlistan bär AI-synligheten i stället.
- Inte
/en-se+/en-nonu — det dubblar EN-inventariet till ~6 000 URL:er. Omprövas vid bevisad efterfrågan.
8. Vad som gjordes i dag utöver utredningen
- claude-seo (tredjepartsskill) installerad och härdad efter Codex-granskning (synk-kanal och oanvända extensions borttagna, beroenden frysta). Egen AI-search-skill rättad (utdaterat FAQ-schema-påstående).
- Disk 92 % → 82 % direkt efter rensning (cacher, gamla binärer, lokala byggträd); dagens crawl-rådata tog sedan några GB.
- Traefik access-logg påslagen (JSON → Loki, 31 dagar).
- Bevisarkiv
/opt/finn/data/evidence/pp-seo-2026-08-23/: Loki-loggar 23/7→ (räddade timmar före radering), GSC-API-export 1/7–21/8, GSC-UI-export (92 CSV i 29 mappar), Cloudflare-dagsserie 20/6→, sitemaps, robots, redirect-kedjor, deployjournal, full crawl-JSONL (627 MB), fas 0/1-rapporter, syntes + fixlista. - P0-1 i produktion (commit 891918ed, deploy v266, blue-green utan nedtid, Cloudflare purgad och värmd).
9. Hur vi vet att det fungerar (sekvens, inte kalender)
- Re-crawl av kohort → P0-acceptanser = 0.
- Access-loggen visar 308 en hop på alias, 200 på canonical, Googlebot får snabba svar.
- GSC: "Page with redirect" och "Alternate canonical" faller, "Indexed" stiger.
- Fasta query-kohorter per språk får visningar igen (mediana, inte snittposition — den är survivor-bias).
- Klick, offertstarter, ordrar.