Menu Luk

Maskinrummet – hvordan jeg sikrede de kørende domæner midt i Googles manuelle kontroller

📖 Historisk kontekst

Denne artikel er en del af serien SEO’s glemte historie (1999-2010) og skal læses som historisk dokumentation.

Artiklen beskriver en periode, hvor søgemaskiner, websites og SEO udviklede sig meget hurtigt. Fokus er på de softwarearkitektoniske overvejelser, den tekniske udvikling og de erfaringer, som prægede perioden.

De beskrevne systemer blev udviklet og anvendt i en helt anden teknologisk virkelighed end den, vi kender i dag. Formålet er at dokumentere et stykke dansk SEO-historie og de softwarearkitektoniske erfaringer, som perioden gav. De beskrevne teknologier har derfor først og fremmest historisk interesse.

Alle beskrivelser bygger på mine egne erfaringer, observationer og dokumentation fra perioden og skal læses i deres historiske kontekst.

SEO’s glemte historie (1999-2010) – Del 3

I de to foregående artikler fortalte jeg, hvordan IP-Delivery fungerede, og hvordan Google omkring 2007 begyndte en langt mere målrettet manuel kontrol af danske IP-cloakede websites.

Denne artikel handler om, hvad der skete bagefter.

Den handler ikke om cloaking som teknik og heller ikke om, hvordan sådanne systemer kunne bygges. Den handler om, hvad der skete i maskinrummet, da jeg midt i en presset periode måtte gentænke software, serverarkitektur og arbejdsgange, mens systemerne fortsat skulle holde kundernes trafik i gang.

Den oprindelige version af mit IP-Delivery-system arbejdede med to logiske spor: ét til den almindelige besøgende og ét til søgemaskinens crawler.

Da mine analyser af serverlogfilerne viste, at de manuelle kontroller opførte sig anderledes end den normale crawling, stod det klart, at den eksisterende arkitektur ikke længere var tilstrækkelig.

Jeg udviklede derfor et tredje logisk spor, som udelukkende håndterede de manuelle kontroller.

Det var ikke en løsning, jeg havde hentet andre steder.

Det var et eksperiment.

Jeg havde ingen facitliste.

Jeg vidste ikke, om det ville virke.

Jeg vidste heller ikke, hvor længe det ville holde.

En ting var jeg dog ikke i tvivl om. Hvis jeg ikke fandt en løsning, risikerede en betydelig del af den organiske trafik til de websites, jeg arbejdede med, at forsvinde. Der var tale om flere hundrede tusinde organiske besøg om måneden fordelt på danske og internationale domæner. Derfor blev opgaven øjeblikkeligt min højeste prioritet.

Det eneste, jeg vidste, var, at hvis de websites, der fortsat var i drift, skulle kunne fortsætte med at generere trafik, måtte systemet tænkes på en helt ny måde.

Det var samtidig første gang, jeg for alvor oplevede, at arbejdet ikke længere kun handlede om SEO. Det var blevet et egentligt softwareprojekt, hvor logik, databaser, serverrespons og løbende kontrol skulle fungere som ét samlet system. Jeg vidste ikke, om løsningen ville holde i måneder eller år, men jeg vidste, at den skulle bygges robust nok til at fungere fra dag ét.

Faktaboks: Maskinrummet – den tekniske arkitektur

Figuren nedenfor viser den overordnede arkitektur, som systemet arbejdede efter i perioden. Selvom diagrammet er forenklet, illustrerer det de centrale beslutningspunkter og gør det lettere at forstå resten af kapitlet.

                         BESØGENDE
                              │
                              ▼
                 CGI SCRIPT #1 – GROVSORTERING
                              │
        ┌─────────────────────┼─────────────────────┐
        │                     │                     │
        ▼                     ▼                     ▼
 MANUEL KONTROL       ALMINDELIG BRUGER      KENDT SØGEMASKINE
        │                     │                     │
        ▼                     ▼                     ▼
 Fejlside / visitkort      HTTP 302         CGI SCRIPT #2
 "Her flytter..."          Redirect               │
                                                  ▼
                                         IP-adresse kendt?
                                          (IP-database)
                                                  │
                           ┌──────────────────────┴─────────────────────┐
                           │                                            │
                           ▼                                            ▼
                    JA – VERIFICERET                            NEJ – UKENDT
                           │                                            │
                           ▼                                            ▼
                    HTTP 200 OK                                  HTTP 302
                 Indekserbart indhold                       Registreres i log
                           │                                            │
                           │                                            ▼
                           │                                  Daglig loganalyse
                           │                                            │
                           │                                  Reverse DNS
                           │                                  Hostname
                           │                                  Søgemaskine
                           │                                            │
                           └──────────────────────┬─────────────────────┘
                                                  ▼
                                      IP tilføjes databasen
                                                  │
                                                  ▼
                               Fremtidige besøg → HTTP 200 OK

 

CGI Script 1 – Grovsorteringen

Det første CGI-script fungerede som systemets portner. Her blev alle besøgende vurderet, allerede inden de fik adgang til websitet.

Scriptet fordelte trafikken i tre hovedgrupper:

  • Manuelle kontroller – IP-adresser identificeret gennem serverloganalyse og knyttet til de manuelle kontroller.
  • Almindelige besøgende
  • Verificerede søgemaskiner

Allerede her blev den videre behandling af besøget bestemt.


Spor 1 – Manuelle kontroller

Besøgende, som blev vurderet til at være manuelle kontroller, blev ikke sendt videre til websitet.

De blev i stedet mødt af en simpel informationsside eller en fejlside, eksempelvis:

“Her flytter en ny kunde ind.”

Disse sider indeholdt ingen navigation og gav ingen mulighed for at udforske resten af websitet.


Spor 2 – Almindelige besøgende

Almindelige brugere blev altid sendt videre via en HTTP 302 Redirect, før de nåede frem til det rigtige website.

Det var et bevidst designvalg.

Hvis en ny crawler fra eksempelvis Google besøgte websitet fra en endnu ukendt IP-adresse, ville den opleve præcis den samme behandling som en almindelig bruger. Set fra serverens side var der derfor ingen forskel mellem en ny crawler og enhver anden ukendt besøgende.


Spor 3 – Verificerede søgemaskiner

Kendte crawlere fra blandt andet Google, Yahoo og MSN blev identificeret på baggrund af deres registrerede IP-adresser.

Disse blev sendt videre til systemets andet CGI-script, som returnerede en HTTP 200 OK og leverede det indhold, som søgemaskinen skulle indeksere.


Hvorfor blev HTTP 302 anvendt?

Valget af HTTP 302 (Found) var ikke tilfældigt.

En 302-statuskode fortæller, at en side er flyttet midlertidigt, hvilket var en helt legitim serverrespons.

Nye crawlere, som endnu ikke var registreret i databasen, modtog derfor samme behandling som almindelige brugere. Først når deres IP-adresser var verificeret, blev de flyttet over til den gruppe, der modtog HTTP 200 OK.

Dermed opførte systemet sig ens over for alle ukendte besøgende.


Daglig loganalyse

Alle ukendte IP-adresser blev registreret i serverens logfiler.

Logfilerne blev gennemgået dagligt, hvor nye adresser blev kontrolleret ved hjælp af blandt andet hostnavne, reverse DNS og oplysninger om, hvilken søgemaskine de tilhørte.

Når en IP-adresse var verificeret, blev den tilføjet databasen og indgik fremover blandt de godkendte crawler-IP-adresser.


Kort fortalt

Arkitekturen arbejdede med tre forskellige behandlingsforløb:

Besøgstype Serverrespons
Manuel kontrol Fejlside eller informationsside
Almindelig besøgende HTTP 302 Redirect
Verificeret søgemaskine HTTP 200 OK og indekserbart indhold

Systemets styrke lå ikke i ét enkelt script, men i samspillet mellem CGI-programmer, IP-databaser, serverlogfiler og den løbende analyse af nye crawler-IP-adresser.

Arkitekturen voksede videre

Samtidig med arbejdet på IP-Delivery udviklede og testede jeg gennem flere år en anden teknik, som dengang blev omtalt som DNS Cloaking.

Grundideen var forholdsvis enkel. Et domæne kan tilgås på flere måder, eksempelvis som example.com og www.example.com. Teknisk er www-versionen et subdomæne, og det samme gælder andre navne foran domænet, eksempelvis blog.example.com, test.example.com eller dev.example.com.

Fra et serverperspektiv kan hvert subdomæne pege på sin egen dokumentmappe eller sit eget website. Netop denne fleksibilitet gjorde det muligt at opbygge forskellige tekniske løsninger omkring det samme domæne.

For mig blev DNS Cloaking ikke en erstatning for IP-Delivery, men endnu et udviklingsspor. Erfaringerne fra de to teknologier blev efterhånden samlet i én fælles softwareplatform, hvor Perl-programmer, CGI-scripts, databaser og loganalyse arbejdede sammen.

Historisk eksempel: DNS Cloaking i praksis

I modsætning til klassisk IP Delivery blev indholdet ikke gemt som en lokal kopi på det DNS-cloakede website. I stedet hentede et PHP-script den aktuelle side direkte fra kundens almindelige website, hver gang siden blev kaldt.

Scriptet udtog indholdet mellem sidens <body>-tags og indsatte det i en ny HTML-ramme. Samtidig blev kundens egne stylesheets indlæst direkte fra det oprindelige website. Relative links og billedadresser blev omskrevet, så de fortsat pegede på kundens egne filer. Resultatet var derfor ikke en side med et efterlignet design, men en visning baseret på kundens egen template og dens aktuelle indhold.

Det DNS-cloakede domæne kunne derefter tilføje sin egen interne navigation uden at ændre kundens typografi, farver, billeder eller øvrige visuelle udtryk.

Det betød også, at indholdet fulgte kundens aktuelle hjemmeside. Når kunden ændrede en tekst, en pris eller en produktside, slog ændringen igennem, næste gang siden blev hentet. Der skulle derfor ikke vedligeholdes separate kopier.

Nedenstående kode er et anonymiseret historisk eksempel. Kundenavn, domæner, produkttekster og interne filnavne er ændret. De forskellige adresser er erstattet med blandt andet example.dk og example.com, mens kodestrukturen og de mange konkrete URL-omskrivninger er bevaret.

<?php
    $url = $_GET['url']
        ? $_GET['url']
        : 'http://www.example.dk/default.asp?pageid=499';

    function fetch_body($url, $replace = 1) {
        function check_for_http_and_replace($str1, $str2, $str3, $url) {
            if (preg_match("#^([a-z]+)://#i", stripslashes($str2))) {
                return stripslashes($str1 . $str2 . $str3);
            }

            return stripslashes($str1 . $url . $str2 . $str3);
        }

        $contents = implode("", file($url));

        if ($replace) {
            $dir = dirname($url) . '/';

            $contents = preg_replace(
                "/.*<body[^>]*>(.*)<\/body>.*/sim",
                "$1",
                $contents
            );

            $contents = preg_replace(
                "/(<img[^>]+src=\")([^\"]+)(\")/ei",
                "check_for_http_and_replace('$1', '$2', '$3', '$dir')",
                $contents
            );

            $contents = preg_replace(
                "/(<a[^>]+href=\")([^\"]+)(\")/ei",
                "check_for_http_and_replace('$1', '$2', '$3', '$dir')",
                $contents
            );

            $contents = str_replace(
                "example.dk//",
                "example.dk/",
                $contents
            );

            $contents = str_replace(
                "/global/pre/iframe/",
                "http://www.example.dk/global/pre/iframe/",
                $contents
            );

            $contents = str_replace("  ", "", $contents);
            $contents = str_replace("&#xA; ", "", $contents);

            $contents = str_replace(
                "/goto.asp",
                "http://www.example.dk/goto.asp",
                $contents
            );

            $contents = str_replace(
                "/default.asp",
                "http://www.example.dk/default.asp",
                $contents
            );

            $contents = str_replace(
                "http://www.example.dkhttp://",
                "http://",
                $contents
            );

            $contents = str_replace(
                "http://www.example.dkhttps://www.example.com/",
                "https://www.example.com/",
                $contents
            );
        }

        return $contents;
    }
?>
<html>
<head>
<meta http-equiv="Content-Type"
      content="text/html; charset=iso-8859-1" />

<title>Lorem ipsum dolor sit amet</title>

<meta name="description"
      content="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." />

<meta name="keywords"
      content="lorem ipsum, dolor sit amet" />

<link rel="stylesheet"
      href="http://www.example.dk/example_internet.css"
      type="text/css" />

<link rel="stylesheet"
      href="http://www.example.dk/example_colors.asp"
      type="text/css" />

<link rel="stylesheet"
      href="http://www.example.dk/global/example.css"
      type="text/css" />

<link rel="stylesheet"
      href="https://www.example.com/assets/layout.css"
      type="text/css" />
</head>

<body onLoad="drawTopMenu();">

<?php print fetch_body($url, 1); ?>

<br>

<p align="center">
    <font face="Arial" color="#C0C0C0" size="1">
        <?php include("nav/_crosslinks-example.txt"); ?>
    </font>
    <br>
    <br>
</p>

<div class="historical-content">
    <h1>Lorem ipsum dolor sit amet</h1>

    <p>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit.
        Integer vitae justo eget magna fermentum iaculis.
    </p>

    <p>
        Sed ut perspiciatis unde omnis iste natus error sit
        voluptatem accusantium doloremque laudantium.
    </p>
</div>

</body>
</html>

Funktionsdiagram: Sådan arbejdede PHP-wrapperen

                 BESØGENDE KALDER DEN LOKALE SIDE
                                  │
                                  ▼
                      URL-PARAMETER KONTROLLERES
                                  │
                 ┌────────────────┴────────────────┐
                 │                                 │
                 ▼                                 ▼
          URL ER ANGIVET                    INGEN URL ANGIVET
                 │                                 │
                 ▼                                 ▼
       Den valgte side bruges             Standardsiden bruges
                 │                                 │
                 └────────────────┬────────────────┘
                                  ▼
                         fetch_body($url, 1)
                                  │
                                  ▼
               KUNDENS AKTUELLE HTML-SIDE HENTES
                          med file($url)
                                  │
                                  ▼
                    KUN INDHOLDET I <body>
                            BEVARES
                                  │
                                  ▼
            RELATIVE BILLEDER OG LINKS OMSKRIVES
                                  │
                 ┌────────────────┴────────────────┐
                 │                                 │
                 ▼                                 ▼
          Relative billedstier              Relative links
          gøres absolutte                   gøres absolutte
                 │                                 │
                 └────────────────┬────────────────┘
                                  ▼
                  KONKRETE URL-FEJL KORRIGERES
              dobbelte skråstreger, ASP-stier m.m.
                                  │
                                  ▼
                    DET BEARBEJDEDE INDHOLD
                          RETURNERES
                                  │
                                  ▼
              INDSÆTTES I DEN LOKALE HTML-RAMME
                                  │
         ┌────────────────────────┼────────────────────────┐
         │                        │                        │
         ▼                        ▼                        ▼
  Lokal titel og           Kundens egne             Lokal menu,
  metadata                 stylesheets              crosslinks og tekst
         │                        │                        │
         └────────────────────────┴────────────────────────┘
                                  │
                                  ▼
                   ÉN SAMLET HTML-SIDE SENDES
                          TIL BROWSEREN

Sådan fungerede koden i praksis

Koden fungerede som et serverbaseret integrationslag mellem det DNS-cloakede domæne og kundens almindelige website. Den var ikke i sig selv den del af systemet, der identificerede søgemaskiner eller manuelle kontroller. Den havde i stedet til opgave at hente, bearbejde og præsentere kundens aktuelle indhold inde i en lokalt styret HTML-ramme.

Først blev den side, der skulle hentes, valgt. Hvis der blev sendt en URL med i kaldet, anvendte scriptet denne. Ellers blev en fast standardside hentet. Dermed kunne den samme PHP-fil bruges som beholder for flere forskellige undersider.

Funktionen fetch_body() hentede derefter hele den eksterne HTML-side direkte fra kundens website. Indholdet blev ikke gemt som en lokal kopi. Hver gang siden blev kaldt, blev den aktuelle version hentet igen. Når kunden ændrede tekst, billeder, priser eller produktinformation på sit eget website, slog ændringen derfor igennem ved næste visning.

Efter hentningen blev alt uden for sidens <body>-område fjernet. Kundens oprindelige titel, metabeskrivelse, øvrige metadata og ydre HTML-struktur blev dermed ikke anvendt. Kun selve indholdet fra body-sektionen blev bevaret og indsat i den nye lokale ramme.

Relative billed- og linkadresser blev derefter omskrevet til fulde adresser. Et billede som eksempelvis images/foto.jpg skulle fortsat hentes fra kundens server, selv om HTML-indholdet nu blev vist på et andet domæne. Det samme gjaldt interne links til ASP-sider og andre ressourcer.

De mange efterfølgende str_replace()-linjer var konkrete korrektioner til den HTML, som kundens system leverede. De rettede blandt andet dobbelte skråstreger, rodrelative adresser, links til bestemte ASP-filer og tilfælde, hvor et domæne ved en fejl var blevet sat foran en adresse, der allerede var absolut.

Den lokale HTML-side omkring det hentede indhold styrede selv sidens titel, metabeskrivelse, keywords og stylesheets. Kundens egne stylesheets blev fortsat indlæst, så det hentede indhold beholdt sit oprindelige visuelle udtryk. Samtidig kunne det DNS-cloakede domæne tilføje sin egen menu, sine egne metadata og et separat lag af interne crosslinks.

Crosslinks blev inkluderet fra en fælles ekstern tekstfil. Det gjorde det muligt at ændre det interne linklag centralt og lade de samme links indgå på mange forskellige sider uden at redigere hver PHP-fil enkeltvis.

Den samlede side bestod derfor af tre lag: en lokalt styret HTML-ramme, kundens dynamisk hentede body-indhold og et separat lag med navigation, crosslinks og eventuelt supplerende tekst.

Teknisk var der altså tale om en specialbygget server-side HTML-wrapper eller content-proxy. Browseren modtog én samlet HTML-side, men hovedindholdet var blevet hentet fra kundens website under PHP-eksekveringen.

Det er samtidig vigtigt at skelne mellem dette præsentationslag og resten af systemet. Den viste PHP-kode udførte ikke selv den IP-baserede sortering. Identifikation af søgemaskiner, manuelle kontroller, IP-databaser, serverrespons og loganalyse lå i andre dele af den samlede platform.

Det afgørende i eksemplet er derfor samspillet mellem HTML, billeder, links, stylesheets og den lokale sidearkitektur: Scriptet hentede kundens eksisterende side, udtog dens centrale indhold og omskrev de relative adresser, så ressourcerne fortsat blev leveret fra kundens eget website.

Koden anvender ældre PHP-syntaks, herunder preg_replace()-modifieren e, som ikke understøttes i moderne PHP. Den er gengivet som historisk dokumentation af arkitekturen og ikke som vejledning til nutidig anvendelse.

SEO Historie Danmark 2007

Historisk illustration af arkitekturen i mit IP Delivery-system (ca. 2007-2012)

Arkitekturen skulle virke naturlig

Da jeg byggede systemet om, handlede det ikke kun om at tilføje et tredje logisk spor. Lige så vigtigt var det at overveje, hvordan et almindeligt website normalt ville reagere.

Den mest oplagte løsning kunne have været blot at blokere bestemte IP-adresser. Det er en metode, der også i dag anvendes mod hackerforsøg, spam og anden uønsket trafik.

Men den løsning valgte jeg bevidst ikke.

En ting stod klart for mig. Hvis en manuel kontrol kun kunne se det indhold eller de fejlmeddelelser, som blev vist til netop den IP-adresse eller den pågældende IP-klasse, så var det også det eneste billede, kontrolløren havde af websitet.

Filosofien var derfor ikke blot at afvise besøgende. Systemet skulle reagere på en måde, som virkede naturlig. En hjemmeside kan være under ombygning, en side kan være slettet, en ny løsning kan være på vej, eller serveren kan ganske enkelt have problemer. Fejlkoder, midlertidige omdirigeringer og korte beskeder er en helt naturlig del af internettets hverdag.

Hvis jeg blot havde blokeret bestemte IP-adresser, ville det sende et tydeligt signal om, at de var blevet opdaget. Derefter kunne kontrollen i princippet blot fortsætte fra en anden IP-adresse, en dynamisk forbindelse eller via en VPN.

Min vurdering var derfor, at et website, som reagerede markant anderledes over for enkelte besøgende, i sig selv kunne komme til at virke unormalt.

Målet var ikke at afvise bestemte besøgende. Målet var, at systemet reagerede på en måde, som et almindeligt website også kunne have gjort. Om det var en fejl, en flytning, en side under ombygning eller en midlertidig ændring, kunne ingen udefra vide med sikkerhed. Netop den usikkerhed var en bevidst del af arkitekturen.

Set i bakspejlet virker det måske indlysende. Omkring 2007 var denne type arkitektur imidlertid langt fra standard. For mig var det endnu en detalje, der skulle få systemet til at opføre sig så naturligt som muligt.

Allerede i den oprindelige arkitektur var hver underside matchet med en konkret landingsside på kundens eget domæne. Den struktur ændrede jeg ikke på, da systemet blev bygget om.

Systemet sendte ikke alle besøgende videre til forsiden. Hver underside fortsatte i stedet naturligt videre til den mest relevante landingsside på kundens eget website.

Det var en bevidst designbeslutning. Brugerrejsen skulle hænge logisk sammen, uanset hvilken side en besøgende landede på.

Set i bakspejlet virker det måske indlysende. Omkring 2007 var det langt fra standard, og det var endnu en af de detaljer, som adskilte mine systemer fra mange af de løsninger, der fandtes på markedet.


Serverlogfilerne var mit vigtigste udviklingsværktøj

Mange forbinder datidens SEO med søgeord, metatags og placeringer.

For mig handlede en stor del af arbejdet om noget helt andet.

Serverlogfiler.

Det var her, historien gemte sig.

Mine systemer var bygget op med to forskellige logniveauer.

Kunderne havde deres egne besøgslogfiler, hvor de kunne følge trafikken på deres websites. Samtidig havde jeg en samlet log over alle de websites, der indgik i systemet. Det var den fælles log, der gjorde forskellen. Når den samme crawler eller den samme ukendte besøgende dukkede op på flere websites, begyndte mønstrene langsomt at tegne sig.

Det første spørgsmål var altid det samme:

Var det en rigtig søgemaskine – eller nogen, der blot udgav sig for at være det?

En user-agent kunne eksempelvis præsentere sig som Googlebot, AltaVistas Scooter eller en anden kendt crawler.

Men en user-agent er blot en tekststreng.

Den kan relativt let ændres.

Derfor stolede mine systemer aldrig alene på user-agenten.

Det næste skridt var altid at kontrollere IP-adressen.

Ved hjælp af et WHOIS-opslag kunne jeg se, hvem der ejede adressen. Hvis user-agenten fortalte, at den kom fra en søgemaskine, men IP-adressen tilhørte en tilfældig internetudbyder, vidste jeg, at noget ikke stemte. Først når user-agent og ejerskabet af IP-adressen stemte overens, blev crawleren betragtet som legitim og tilføjet IP-databasen.

Den fælles log gjorde forskellen

Mine systemer registrerede alle besøg på kundernes websites. Hver kunde havde adgang til sin egen besøgslog og kunne følge med i, hvilke søgemaskiner og besøgende der havde været forbi.

Men den log var kun interessant for den enkelte kunde.

For mig var det langt vigtigere at kunne se, hvad der skete på tværs af alle websites.

Derfor byggede jeg en central logfunktion, hvor oplysninger fra samtlige websites blev samlet ét sted. Formålet var enkelt: Jeg ville undgå at skulle logge ind på adskillige websites hver eneste dag for at lede efter nye crawlere eller usædvanlige besøgende.

Den fælles log gjorde det muligt hurtigt at opdage nye mønstre.

Når en ukendt crawler dukkede op, blev den registreret centralt, og jeg kunne straks begynde at undersøge, om der var tale om en legitim søgemaskine eller blot en browser, der udgav sig for at være en crawler.

I den oprindelige arkitektur blev kendte crawlere behandlet anderledes end ukendte besøgende. De crawlere, der allerede var verificeret i systemets database, fortsatte som normalt. Ukendte crawlere blev derimod markeret i loggen og kunne efterfølgende analyseres. Det var netop disse registreringer, der gjorde det muligt løbende at identificere nye søgemaskiner, kontrollere deres IP-adresser og opdatere databasen, når det var nødvendigt.

Set i bakspejlet var den centrale log sandsynligvis den vigtigste del af hele platformen. Den gjorde det muligt at vedligeholde systemet effektivt, selv om antallet af websites voksede, og den gav et samlet overblik, som jeg aldrig ville have fået ved kun at analysere ét website ad gangen.

Et godt historisk eksempel er AltaVista

Deres crawler Scooter fandtes i mange forskellige versioner, hostnavne og IP-adresser. Mine gamle oversigter indeholder blandt andet registreringer som disse:

User-Agent : Scooter-W3.1.2
Host       : trek11.sv.av.com
IP         : 64.152.75.1

User-Agent : Scooter-3.2
Host       : scooter9.sv.av.com
IP         : 64.152.75.4

User-Agent : Scooter/3.3
Host       : buildrack90.sv.av.com
IP         : 64.152.75.182

Den komplette oversigt fyldte flere tusinde registreringer med forskellige hostnavne, user-agents og IP-adresser. Den var ikke interessant i sig selv – den var interessant, fordi den dokumenterede, hvilke servere der faktisk tilhørte søgemaskinen.

Programmerne var i virkeligheden den lette del.

Den virkelige udfordring var vedligeholdelsen.

Nye user-agents dukkede op.

IP-adresser blev ændret.

Nye crawlere kom til.

Andre forsvandt igen.

Alt skulle kontrolleres, dokumenteres og løbende vedligeholdes. Hvis kontrollen blev forsømt, begyndte systemet før eller siden at træffe forkerte beslutninger.

Det var systemets akilleshæl.

Det var ikke programmeringen, der kostede mest tid.

Det var kvalitetssikringen.

Og det var netop under dette arbejde med logfiler, WHOIS-opslag og kontrol af crawlernes identitet, at jeg begyndte at se de mønstre, som førte til den største ændring af hele arkitekturen.

Systemet gik fra to logiske spor til tre.

(Her følger illustrationen af de tre spor og den forenklede Perl-model.)

===============================================================================
HISTORISK SIMULATION: IP DELIVERY MED TRE LOGISKE SPOR
===============================================================================

Denne illustration viser den forenklede Perl-model bag det beslutningslag,
som fordelte besøgende mellem tre forskellige forløb.

Programmet udfører ingen virkelig IP-kontrol, ingen redirects og sender
ingen HTTP-statuskoder. Det er alene en historisk simulation af den
softwarearkitektur, der blev anvendt i perioden.

-------------------------------------------------------------------------------

#!/usr/bin/perl
#
# Historisk simulation af beslutningslaget i et IP Delivery-system.
#
# Det oprindelige system anvendte IP-databaser, serverlogfiler,
# crawlerkontrol og flere CGI-programmer.
#
# Denne model viser kun den logiske opdeling i tre spor.
#

use strict;
use warnings;
use utf8;
use open qw(:std :encoding(UTF-8));

my %routes = (

    visitor => {
        title  => 'Almindelig besøgende',
        status => 'HTTP 302 Redirect',
        action => 'Besøgende sendes til den relevante landingsside på kundens website.',
    },

    crawler => {
        title  => 'Verificeret søgemaskine',
        status => 'HTTP 200 OK',
        action => 'Crawleren får adgang til den relevante cloakede underside.',
    },

    reviewer => {
        title  => 'Manuel kontrol',
        status => 'Neutral side eller fejlsvar',
        action => 'Kontrolløren mødes af en enkel informationsside uden navigation.',
    },
);

print <<"HEADER";

===============================================================================
SIMULERET BESLUTNINGSLAG
===============================================================================

Den samlede platform arbejdede logisk med tre spor:

  1. Almindelig besøgende
  2. Verificeret søgemaskine
  3. Manuel kontrol

HEADER

for my $type (qw(visitor crawler reviewer)) {

    print "---------------------------------------------------------------\n";
    print "$routes{$type}{title}\n";
    print "---------------------------------------------------------------\n";
    print "Serverrespons: $routes{$type}{status}\n";
    print "$routes{$type}{action}\n\n";
}

print <<"FLOW";

-------------------------------------------------------------------------------
FORENKLET FUNKTIONSFORLØB
-------------------------------------------------------------------------------

                         INDGÅENDE FORESPØRGSEL
                                  |
                                  v
                    +---------------------------+
                    |   CGI SCRIPT #1           |
                    |   GROVSORTERING            |
                    +---------------------------+
                                  |
          +-----------------------+-----------------------+
          |                       |                       |
          v                       v                       v

   MANUEL KONTROL        ALMINDELIG BRUGER       KENDT SØGEMASKINE
          |                       |                       |
          v                       v                       v

 Neutral informationsside     HTTP 302              CGI SCRIPT #2
 eller fejlside               Redirect                    |
          |                       |                        v
          |                       |               IP-adresse kendt?
          |                       |                (IP-database)
          |                       |                        |
          |                       |          +-------------+-------------+
          |                       |          |                           |
          |                       |          v                           v
          |                       |     JA – VERIFICERET            NEJ – UKENDT
          |                       |          |                           |
          |                       |          v                           v
          |                       |     HTTP 200 OK                  HTTP 302
          |                       |          |                    Registreres i log
          |                       |          v                           |
          |                       |   CLOAKET UNDERSIDE                  v
          |                       |   MED INDEKSERBART            Daglig loganalyse
          |                       |   INDHOLD                            |
          |                       |                                     v
          |                       |                               Reverse DNS
          |                       |                               Hostname
          |                       |                               Søgemaskine
          |                       |                                     |
          |                       |                                     v
          |                       |                            IP tilføjes databasen
          |                       |                                     |
          |                       |                                     v
          |                       |                         Fremtidige besøg → HTTP 200
          |                       |
          |                       v
          |             RELEVANT LANDINGSSIDE
          |              PÅ KUNDENS WEBSITE
          |
          v
 Ingen adgang til de
 cloakede undersider


Kort fortalt:

 - Almindelige besøgende blev sendt videre til den relevante side
   på kundens almindelige website.

 - Verificerede søgemaskiner modtog HTTP 200 og fik adgang til
   de relevante cloakede undersider.

 - Ukendte crawlere blev behandlet som almindelige besøgende,
   registreret i loggen og først senere verificeret.

 - Manuelle kontroller blev mødt af en neutral side eller et
   fejlsvar uden adgang til systemets øvrige sider.


Bemærk:

Denne illustration viser alene det forenklede beslutningslag.

De faktiske systemer omfattede desuden IP-databaser, CGI-programmer,
serverlogfiler, WHOIS-opslag, reverse DNS, automatiserede processer
og løbende kvalitetssikring.

===============================================================================
FLOW

Mere end IP Delivery

Perl illustrationen ovenfor er naturligvis ikke mit originale program. Den er en forenklet illustration af den arkitektur, jeg udviklede i perioden, og har alene til formål at vise tankegangen bag systemet.

Det interessante var efterhånden ikke længere den enkelte SEO teknik.

Det interessante var arkitekturen

Samtidig med, at jeg byggede IP-Delivery-systemet om, arbejdede jeg videre med DNS-Cloaking, som jeg gennem flere år havde udviklet og testet parallelt. Oprindeligt betragtede jeg de to teknikker som selvstændige løsninger, men efterhånden begyndte de at smelte sammen.

Erfaringerne fra de forskellige systemer blev gradvist samlet i én fælles softwareplatform. Perl-programmer, CGI-scripts, databaser, central loganalyse og automatiserede processer begyndte at arbejde sammen, og fokus flyttede sig fra den enkelte teknik til udviklingen af en egentlig arkitektur.

De IP-adresser fra Googles irske netværk, som tidligere havde fået alarmklokkerne til at ringe, dukkede fortsat op i logfilerne. De forsvandt ikke. Jeg kunne stadig se dem besøge nogle af de websites, der indgik i systemet.

Forskellen var, at der efterfølgende ikke længere skete noget

Hvor de tidligere havde været et tydeligt faresignal, blev de med tiden blot endnu en linje i logfilen. Efter en periode holdt jeg helt op med at interessere mig for dem, fordi de ikke længere havde nogen praktisk betydning i den daglige drift.

Det er naturligvis alene en beskrivelse af mine egne observationer. Jeg ved ikke, hvilke interne procedurer Google anvendte, eller hvordan de vurderede de enkelte websites. Jeg kan blot konstatere, at de IP-adresser, som tidligere havde fået min fulde opmærksomhed, efter ændringerne ikke længere førte til de konsekvenser, jeg tidligere havde oplevet. Løsningen blev herefter anvendt i yderligere omkring fem år, og jeg oplevede i den periode ingen nævneværdige problemer eller tilfælde, hvor de pågældende domæner blev fjernet fra Googles indeks.

Business as usual

Mens platformen blev bygget om og videreudviklet, fortsatte den daglige drift.

For kunderne var der ingen dramatik. De købte fortsat abonnementer, og systemerne leverede den organiske trafik, de forventede. De så ikke arbejdet med Perl-programmer, CGI-scripts, databaser eller serverlogfiler. De oplevede blot, at trafikken fortsatte.

Det var netop et af målene med ombygningen. Arkitekturen skulle ændres i baggrunden, uden at kunderne blev berørt af det arbejde, der foregik i maskinrummet.

Platformen blev anvendt af virksomheder inden for en lang række brancher, blandt andet rejser, sommerhusudlejning, finans, gaming, underholdning og andre kommercielle områder, hvor organisk synlighed var en vigtig del af forretningen.

Den anonymiserede faktura nedenfor viser én måneds levering til én kunde i februar 2008. Den er ikke medtaget for at fremhæve kunden, men som dokumentation for, at platformen fortsat var i normal kommerciel drift, efter at systemerne var blevet bygget om.

Anonymiseret faktura fra februar 2008 for cloaking-abonnement
Anonymiseret faktura fra februar 2008.
Kunden er anonymiseret, men dokumentet viser, at platformen fortsat blev anvendt kommercielt som en abonnementsløsning.

Det er måske værd at nævne, at hele platformen blev udviklet, drevet og vedligeholdt af mig alene. Der var ingen udviklingsafdeling, ingen driftsteam og ingen supportfunktion. Programmering, loganalyse, kundekontakt og den daglige vedligeholdelse var samlet hos én person. Det var også en af årsagerne til, at løsningen med tiden blev stadig mere ressourcekrævende at holde kørende.

Sådan fortsatte forretningen i flere år. Først omkring 2012 begyndte det for alvor at stå klart, at tiden var ved at løbe fra denne type systemer. Stadig flere virksomheder ønskede langsigtede white hat-løsninger, og kundegrundlaget for denne type systemer blev gradvist mindre. Samtidig voksede vedligeholdelsesarbejdet. Logfiler skulle gennemgås, databaser opdateres, nye IP-adresser verificeres og systemerne vedligeholdes næsten dagligt.

På et tidspunkt stod det klart, at arbejdsindsatsen ikke længere stod mål med behovet.

I 2012 slukkede jeg den sidste trafikserver.

De havde været i drift i mere end ti år og leveret millioner af organiske besøg til kunder i både Danmark og udlandet.

Det blev afslutningen på et teknologisk kapitel.

Men ikke afslutningen på de erfaringer, perioden havde givet mig.

Tværtimod.

Arbejdet med Perl, CGI-programmering, databaser, serverarkitektur, loganalyse og automatisering blev fundamentet for den måde, jeg senere kom til at arbejde med teknisk SEO, softwareudvikling og – mange år senere – AI.

Når jeg ser tilbage i dag, var IP Delivery derfor ikke det vigtigste, jeg tog med mig fra perioden.

Det var maskinrummet.

Det var måden at tænke på: at observere, analysere mønstre, bygge robuste systemer og forbedre dem på baggrund af data. Den tankegang har fulgt mig lige siden – fra teknisk SEO og softwareudvikling til de AI-løsninger, jeg arbejder med i dag.

Næste artikel: Serverlogfilerne blev mit vigtigste værktøj gennem hele perioden. Det var her, jeg kunne se, hvem der besøgte et website, hvornår de gjorde det, og hvilke mønstre der gentog sig på tværs af hundredvis af domæner. Historien handler ikke om avancerede SEO værktøjer. Den handler om data, loganalyse og de observationer, der ofte fortalte mere end noget andet værktøj på markedet.

Kilder og historisk dokumentation

Denne artikel bygger på mine egne erfaringer, observationer og dokumentation fra perioden 1999-2012.

For at bevare artiklens fokus på historien har jeg valgt at samle den bagvedliggende dokumentation på en separat side. Her findes blandt andet historiske ordrebekræftelser, priseksempler, anonymiserede fakturaer, arkiverede websites og andet materiale, der dokumenterer mit arbejde med SEO siden 1999.

Læs den historiske dokumentation her:

SEO siden 1999 – historisk dokumentation

Denne artikel er en del af serien SEO’s glemte historie (1999-2010)

✓ Del 1: IP Delivery – hvordan en af datidens mest avancerede SEO-teknikker fungerede

✓ Del 2: Da Google begyndte den manuelle jagt på danske IP-cloakede websites

✓ Del 3: Maskinrummet – hvordan jeg sikrede de kørende domæner midt i Googles manuelle kontroller (du læser den nu)

◻ Del 4: Serverlogfilerne fortalte sandheden

◻ Del 5: Google Cache – den oversete kampplads

◻ Del 6: Det internationale SEO miljø omkring år 2000

◻ Del 7: Fra IP Delivery til moderne SEO og AI

◻ Del 8: SEO’s vilde år 1999-2006 – erfaringerne bagefter


Posted in Historiske artikler

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *