Zum Inhalt springen

Backlink-Daten als JSON: $100 Guthaben, keine Karte erforderlich, Anmeldung mit Google.

SEOMasters.dev
SEOMasters.devRegistrieren

Ein Web-Backlink-Checker in Next.js, Schlüssel serverseitig gespeichert

CORS ist offen, sodass ein Browser die API direkt aufrufen kann. Dies mit einem kostenpflichtigen Schlüssel zu tun, ist ein Fehler. Diese Seite zeigt die dünne Server-Schicht, die das Problem löst.

Panzerkabel verbinden ein Glashaus mit einem unterirdischen Serverraum, das mit schweren Schlössern gesichert ist

Mythos versus Realität

MythosRealität
"Offenes CORS bedeutet, dass ich den Schlüssel in meinem React-Bundle ausliefern kann."Offenes CORS bedeutet, dass der Aufruf funktioniert. Jeder kann den Schlüssel jedoch noch immer über die Netzwerk-Tab auslesen und Ihr Guthaben verbrauchen.
"Eine Linktabelle muss im Client gerendert werden, da es sich um ein Dashboard handelt."Daten auf dem Server abrufen und HTML senden. Ihr Nutzer sieht Zeilen beim ersten Paint, und Ihr Schlüssel verlässt den Server nicht.
"Auch kostenlose Aktionen benötigen Schutz."domain, overview, stats und suggest benötigen keinen Schlüssel, sodass ein Browser diese direkt aufrufen kann.
"Ich sollte bei jeder Seitenansicht neu laden."Der Index wird etwa monatlich neu aufgebaut. Ein 24-Stunden-Cache verliert nichts.
"Der Proxy sollte weiterleiten, was der Client sendet."Listen Sie die Aktionen auf, die Sie verwenden. Ein weitergeleitetes action=search kostet 3 Credits.

Der Route-Handler

App Router, Datei app/api/links/route.ts. Die Umgebungsvariable erreicht das Client-Bundle nicht, da sie kein NEXT_PUBLIC_-Präfix hat.

ts
import { NextRequest, NextResponse } from "next/server";

const ALLOWED = new Set(["refdomains", "anchors", "toppages"]);

export async function GET(req: NextRequest) {
  const p = req.nextUrl.searchParams;
  const action = p.get("action") ?? "";
  const q = (p.get("q") ?? "").toLowerCase();
  if (!ALLOWED.has(action) || !/^[a-z0-9.-]+$/.test(q)) {
    return NextResponse.json({ ok: false, error: "bad request" }, { status: 400 });
  }
  const url = `https://seomasters.dev/api.php?action=${action}&q=${q}&per=100`;
  const upstream = await fetch(url, {
    headers: { Authorization: `Bearer ${process.env.SEO_API_KEY}` },
    next: { revalidate: 86400 },
  });
  if (upstream.status === 402) {
    return NextResponse.json({ ok: false, error: "data temporarily unavailable" }, { status: 503 });
  }
  return new NextResponse(upstream.body, { status: upstream.status, headers: { "content-type": "application/json" } });
}

Warum der Handler so aussieht

Allow-Liste der Aktionen

Ein Bug in Ihrer UI sollte nicht versehentlich einen 3-Credit-search-Aufrout auslösen können.

Regex auf q

Nur ein reiner Hostname kommt durch. Kein Schema, kein Pfad, keine Query-String-Smuggling nach oben.

revalidate: 86400

Ein Upstream-Aufruf pro Domain und Tag. Bei monatlichen Daten ist auch ein längerer Zeitraum in Ordnung.

402 auf 503 gemappt

Ihre Besucher müssen nicht lernen, dass Ihr Guthaben leer ist. Sie sollten es wissen, also loggen Sie es.

Ein Wanderer geht über eine Steinbrücke, während eine Drohne über das neblige Tal schwebt

Rendering: Was ein Crawler und ein Kunde sehen

Wenn sich dieses Dashboard hinter einer Anmeldung befindet, ist das Server- oder Client-Side-Rendering eine UX-Entscheidung. Wenn etwas öffentlich ist, z. B. eine kostenlose Lookup-Seite, die indexiert werden soll, rendern Sie die erste Tabelle auf dem Server.

Eine Seite, die als leere Hülle ankommt und sich nach dem Hydratation füllt, ist sowohl für das Indexing als auch für langsame Geräte ein Risiko. Google kann JavaScript ausführen, aber es stellt diesen Schritt in die Warteschlange und führt ihn nicht immer schnell aus.

Halten Sie die kostenlose Übersicht auf dem Server

Legen Sie den günstigen, kostenlosen overview-Aufruf in eine Server-Komponente und lassen Sie den Besucher die kostenpflichtigen, tieferen Abrufe über eine Schaltfläche auslösen. Diese Trennung macht auch Ihren Credit-Verbrauch vorhersehbar.

Kostenlose Aufrufe kosten nichts, sodass ein serverseitig gerendertes Overview keinen Credit-Verbrauch verursacht. Nur die kostenpflichtigen Abrufe benötigen eine Schaltfläche und eine Session.

Denken Sie zuerst über den leeren Zustand nach. Ein Besucher, der auf einer leeren Tabelle landet, geht davon aus, dass das Tool defekt ist, und ein schnelles zweites Paint macht diesen ersten Eindruck nicht rückgängig.

Wählen Sie die kostenlosen Aktionen für den ersten Bildschirm und die kostenpflichtigen für Drill-Downs. Ein Besucher, der eine Seite öffnet und eine kostenlose Zusammenfassung sieht, hat keinen Grund, versehentlich eine kostenpflichtige Schaltfläche zu drücken.

Verlinken Sie die Erklärung, nicht nur die Zahl

Die Zahlen zu verdrahten, ist einfach. Sie zu erklären, ist schwieriger. Verlinken Sie daher die DR-Formel aus dem Tooltip und erwähnen Sie, dass sich der Index monatlich aktualisiert.

Ein Besucher, der eine Score ohne ihre Formel sieht, wird raten, daher gehört der Link neben der Zahl auf dem Bildschirm.

Ein Handwerker hält einen Messingschlüssel in der Nähe einer verschlossenen Schublade hinter einer Holzabtrennung

Server- und Client-Teile eines Dashboards

Die meisten Dashboards benötigen beide Code-Arten. Die Ownership entscheidet über die Aufteilung. Der Schlüssel, die Daten und die Zugriffsregeln haben jeweils einen festen Platz. Eine öffentliche Web-Backlink-Checker-Seite benötigt dieselbe Aufteilung wie eine hinter einer Anmeldung.

Auf dem Server abrufen, auf dem Client sortieren

Eine Backlink-Tabelle ist eine sinnvolle Client-Komponente, wenn Nutzer darin sortieren, filtern und paginieren. Die Zeilen sollten von einer Server-Komponente stammen, die Ihren Route bereits aufgerufen hat, damit der Browser den Schlüssel oder die Upstream-URL nicht hält.

Das Sortieren von zehn Zeilen ist auf dem Client sofort erledigt. Das Sortieren von fünftausend Zeilen bei jeder neuen Anfrage ist langsam, daher halten Sie den vollständigen Satz in der Komponente, die Sie bereits geladen haben.

Halten Sie es einfach. Eine Tabelle, die sofort sortiert, ist besser als eine, die bei jedem Klick neu lädt.

Halten Sie die Client-Grenze niedrig

Schieben Sie die Grenze so weit wie möglich nach unten im Baum. Ein dynamisch importiertes, nur auf dem Client gerendertes Widget wird im Browser ausgeführt, während die Komponente, die die Zeilen abgerufen hat, auf dem Server bleibt. Übergeben Sie Zeilen als Props und halten Sie den Fetch oberhalb der Grenze.

Wenn Sie den Fetch unter die Grenze verschieben, führt der Browser den Aufruf selbst aus. Das ist der Fehler, vor dem im Abschnitt zum Schlüssel-Leck gewarnt wird. Überprüfen Sie daher die Netzwerk-Tab nach jeder Änderung an dieser Grenze.

Wo genau fällt die Linie? Zeichnen Sie sie zuerst auf Papier, mit einer Box für den Fetch und einer für jede interaktive Steuerung, und zählen Sie die Pfeile, die sie kreuzen.

Shell- und Session-Prüfungen bleiben auf dem Server

Layout- und Session-Prüfungen entscheiden darüber, was ein Besucher überhaupt sehen darf, daher gehören sie auf den Server. Interaktive Tabellen, Formulare und Diagramme können danach hydratisiert werden, ohne diese Entscheidung zu ändern.

Ein Client-seitiger Guard kann eine Schaltfläche ausblenden, aber er kann eine Anfrage nicht stoppen. Der Route-Handler und das Server-Layout sind die Orte, an denen der Zugriff tatsächlich durchgesetzt wird.

Senden Sie dann dieselbe Anfrage ohne Cookie und bestätigen Sie, dass der Server sie ablehnt, nicht nur die Seite.

Dieselbe Regel gilt für den Proxy selbst. Eine Session-Prüfung, die nur im Browser stattfindet, kann die Route nicht schützen, daher prüft die Route die Session ebenfalls, bevor sie einen Credit verbraucht.

Cache pro Domain, nicht pro Besucher

Der Route baut seine Upstream-URL aus der Domain auf, daher folgt der Cache-Schlüssel ebenfalls der Domain. Ein Besucher, der eine Site nachschaut, wärmt die Seite für den nächsten Besucher, der dieselbe Site abfragt.

Das ist der Punkt der täglichen Revalidate im Handler. Zwei Besucher auf derselben Domain kosten einen Upstream-Aufruf, und tausend Besucher auf tausend Domains kosten tausend Aufrufe, daher ist der Cache das Budget.

Beachten Sie auch den umgekehrten Fall. Eine Seite, die ihren Query-String durch einen Zeitstempel variiert, zerstört den Cache vollständig, und die Rechnung zeigt dies, bevor die Logs es tun.

Revalidieren Sie in einem Zeitplan, der zum Index passt. Ein tägliches Fenster passt zu monatlichen Daten, und ein kürzeres fügt nur Aufrufe hinzu, ohne neue Fakten zu liefern.

Härtung, in der Reihenfolge, in der wir sie durchführen würden

Der Handler ist der einfache Teil. Die Härtung ist dort, wo ein Schlüssel leckt oder ein Guthaben schmilzt, daher arbeiten Sie diese Punkte nacheinander ab.

Scannen Sie Ihre eigene Netzwerk-Tab

Öffnen Sie Chrome DevTools in Ihrem eigenen Dashboard und lesen Sie die Netzwerk-Tab. Schauen Sie genau hin. Wenn eine Anfrage dort irgendwo sk_live_ zeigt, rotieren Sie den Schlüssel noch heute. Diese Prüfung dauert eine Minute und findet das häufigste Leck.

Durchsuchen Sie auch die Antwortkörper sowie die Anfrage-Header. Ein Schlüssel, der in einer Fehlermeldung wiederholt wird, ist ebenfalls ein Leck.

Begrenzen Sie Aufrufe pro Benutzer

Fügen Sie als Nächstes ein pro-Benutzer-Limit hinzu. Platzieren Sie es vor der Route. Der obige Handler verbraucht Ihren Credit für jeden, der ihn erreichen kann, also koppeln Sie ihn an eine Session und begrenzen Sie Aufrufe pro Stunde. Wählen Sie das Limit basierend auf der Budget-Mathematik auf der Credit-Seite.

Starten Sie großzügig und schärfen Sie später nach. Ein Limit, das echte Benutzer am ersten Tag blockiert, bringt sie dazu, ein anderes Tool zu finden.

Nennen Sie, wer nachlädt

Auch einen Satz im Runbook zu erwähnen, ist sinnvoll: Wer wird alarmiert, wenn das Guthaben unter ein bestimmtes Niveau fällt, und wer lädt es nach? Nennen Sie die Person.

Schreiben Sie den Nachlade-Schritt als Checkliste mit dem Link zur Kontoseite und der erwarteten Bearbeitungszeit nieder. Ein Runbook, das nur im Kopf einer Person existiert, scheitert beim ersten Mal, wenn diese Person im Urlaub ist. Der Schritt selbst dauert Minuten, daher ist das Niederschreiben die größere Aufgabe.

Loggen Sie das Guthaben, bevor es aufgebraucht ist

Loggen Sie dann _credits aus jeder Upstream-Antwort in Ihre eigenen Metriken. Ein Graph des verbleibenden Guthabens, täglich abgetastet, warnt Sie eine Woche bevor HTTP 402 eintrifft.

Wählen Sie den Alert-Level basierend auf Ihrer wöchentlichen Nutzung. Eine Zahl, die aus einer Vorlage gewählt wurde, passt selten zu Ihren tatsächlichen Aufrufen.

Halten Sie den letzten gesehenen Wert sowohl im Speicher als auch im Log, damit die Route bezahlte Abrufe verweigern kann, sobald die Zahl unter den Alert-Level fällt. Diese Prüfung ist eine Zeile und verhindert, dass ein Traffic-Burst den Rest verbraucht.

Halten Sie die Allow-Liste an einem Ort

Eine weitere Gewohnheit zahlt sich aus. Halten Sie die Allow-Liste in einer gemeinsamen Konstante, importiert vom Handler und von den Tests, sodass eine neue Aktion nicht ohne Bearbeitung einer sichtbaren Liste und ohne Aufmerksamkeit eines Reviewers in die Produktion gelangen kann.

Eine Datei, ein Import. Niemand sollte die Liste in drei Komponenten suchen müssen, um zu wissen, was der Proxy akzeptiert.

Testen Sie die Laufzeitumgebung, zu der Sie bereitstellen

Testen Sie den Handler abschließend unter Node 20 oder der Edge-Laufzeit Ihrer Wahl und testen Sie beide. Das Streamen des Upstream-Körpers, wie es der Code tut, funktioniert in beiden, aber die Header-Behandlung unterscheidet sich und ist einen Test wert.

Fügen Sie einen Test hinzu, der die Route mit einer ungültigen Aktion und einen mit einer gültigen Aktion aufruft und auf die Statuscodes prüft. Diese beiden Tests fangen die meisten Regressionen in der Allow-Liste und der Header-Behandlung ab.

Den Proxy in der richtigen Reihenfolge verdrahten

  1. 1

    Fügen Sie den Schlüssel auf dem Server hinzu

    Speichern Sie den Schlüssel in Ihrer Server-Umgebung unter einem Namen ohne öffentliches Präfix und starten Sie den Dev-Server neu, damit er geladen wird.

  2. 2

    Schreiben Sie zuerst die Allow-Liste

    Listen Sie die Aktionen auf, die Ihre Bildschirme wirklich aufrufen. Alles andere erhält von Ihrer eigenen Route eine 400, bevor es die Upstream-API erreicht.

  3. 3

    Rufen Sie Ihre Route von einer Server-Komponente ab

    Rendern Sie die Zeilen dort, wo die Daten eintreffen. Der Browser ruft nur Ihre eigene Domain auf, daher bleibt die Upstream-Adresse außerhalb der Seite.

  4. 4

    Behalten Sie das Guthaben im Auge

    Loggen Sie _credits bei jeder Upstream-Antwort und legen Sie einen Alert-Level fest, auf den Sie reagieren können, bevor ein 402 als 503 einen Besucher erreicht.