En webbsökning efter backlänkar i Next.js, där nyckeln behålls på servern
CORS är öppet, så en webbläsare kan anropa API:et direkt. Att göra detta med en betalnyckel är misstaget. Denna sida visar det tunna serverlagret som åtgärdar problemet.

Myt mot verklighet
| Myt | Verklighet |
|---|---|
| "Öppen CORS innebär att jag kan skicka med nyckeln i mitt React-paket." | Öppen CORS innebär att anropet fungerar. Alla kan fortfarande läsa nyckeln från nätverksfönstret och förbruka din saldo. |
| "En tabell med länkar måste renderas på klienten eftersom det är en dashboard." | Hämta data på servern och skicka HTML. Din användare ser rader vid första ritningen och din nyckel lämnar aldrig servern. |
| "Även gratisåtgärder behöver skydd." | domain, overview, stats och suggest behöver ingen nyckel, så en webbläsare kan anropa dem direkt. |
| "J bör uppdatera data vid varje sidvisning." | Indexet byggs om ungefär en gång i månaden. En 24-timmars-cache förlorar ingen information. |
| "Proxyn bör vidarebefordra allt klienten skickar." | Tillåt endast de åtgärder du använder. En vidarebefordrad action=search kostar 3 krediter. |
Route handlern
App Router, fil app/api/links/route.ts. Miljövariabeln når aldrig klientpaketet eftersom den inte har prefixet NEXT_PUBLIC_.
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" } });
}Varför handlern ser ut som den gör
Tillåtna åtgärder
Ett fel i ditt gränssnitt bör inte kunna utlösa ett 3-krediters search-anrop av misstag.
Regex på q
Endast en ren värdnamn släpps igenom. Ingen schematyp, ingen sökväg, ingen frågestring som smygs uppströms.
revalidate: 86400
Ett anrop till upstream per domän per dag. Med månatliga data är en längre period också okej.
402 mappat till 503
Dina besökare behöver inte lära sig att ditt saldo är tomt. Det behöver du, så logga det.

Renderning: vad en crawler och en kund ser
Om denna dashboard ligger bakom en inloggning är server- eller klientrendering ett UX-val. Om något av det är offentligt, till exempel en gratis uppslags sida som du vill att Google ska indexera, rendera den första tabellen på servern.
En sida som anländer som ett tomt skal och fylls i efter hydration är en risk för indexering och för långsamma telefoner. Google kan köra JavaScript, men det köar det steget och når det inte alltid snabbt.
Behåll den fria översikten på servern
Placera det billiga, fria overview-anropet i en Server Component och låt besökaren trigga de betalda, djupare anropen från en knapp. Denna uppdelning gör också att dina kreditutgifter blir förutsägbara.
Gratisanrop kostar inget, så en serverrenderad översikt tillför inga kreditutgifter. Endast de betalda anropen behöver en knapp och en session.
Tänk på det tomma tillståndet först. En besökare som landar på en tom tabell kommer att anta att verktyget är trasigt, och en snabb andra ritning tar inte bort det första intrycket.
Välj de gratisåtgärderna för den första skärmen och de betalda för fördjupningar. En besökare som öppnar en sida och ser en gratis sammanfattning har ingen anledning att trycka på en betald knapp av misstag.
Länka förklaringen, inte bara numret
Att koppla ihop siffrorna är den lätta delen. Att förklara dem är svårare, så länka DR-formeln från verktygtipset och nämn att indexet uppdateras månatligen.
En besökare som ser en poäng utan dess formel kommer att spekulera i den, så länken hör hemma bredvid numret på skärmen.

Server- och klientdelar av en dashboard
De flesta dashboards behöver båda typerna av kod. Ägarskap bestämmer uppdelningen. Nyckeln, datan och åtkomstreglerna har var sitt ställe. En offentlig sida för webbsökning efter backlänkar behöver samma uppdelning som en bakom en inloggning.
Hämta data på servern, sortera på klienten
En backlink-tabell är en rimlig klientkomponent när människor sorterar, filtrerar och paginerar igenom den. Raderna bör komma från en serverkomponent som redan har anropat din route, så webbläsaren håller aldrig nyckeln eller upstream-URL:en.
Att sortera tio rader är omedelbart på klienten. Att sortera fem tusen rader i ett nytt anrop varje gång är långsamt, så behåll hela uppsättningen i komponenten du redan har laddat.
Håll det enkelt. En tabell som sorterar omedelbart slår en som hämtar data på nytt vid varje klick.
Håll klientgränsen låg
Flytta gränsen så långt ner i trädet som du kan. En dynamiskt importerad, klientendast widget renderas i webbläsaren, medan komponenten som hämtade raderna stannar på servern. Skicka raderna som props och håll hämtningen ovanför gränsen.
Om du flyttar hämtningen under gränsen gör webbläsaren anropet själv. Det är misstaget som avsnittet om nyckelläckage varnar för, så kontrollera Nätverksfönstret efter varje ändring av denna gräns.
Var exakt faller linjen? Rita den på papper först, med en ruta för hämtningen och en för varje interaktiv kontroll, och räkna pilarna som korsar den.
Skal- och sessionskontroller stannar på servern
Layout- och sessionskontroller bestämmer vad en besökare överhuvudtaget får se, så de hör hemma på servern. Interaktiva tabeller, formulär och diagram kan hydreras efteråt utan att ändra det beslutet.
En klient-sida-guard kan dölja en knapp, men den kan inte stoppa ett anrop. Route handlern och serverlayouten är där åtkomst faktiskt upprätthålls.
Skicka sedan samma anrop utan cookie och bekräfta att servern vägrar det, inte bara sidan.
Samma regel gäller proxyn själv. En sessionskontroll som bara sker i webbläsaren kan inte skydda routen, så routen kontrollerar sessionen också innan den förbrukar en kredit.
Cachning per domän, inte per besökare
Routen bygger sin upstream-URL från domänen, så cachnyckeln följer också domänen. En besökare som slår upp en webbplats värmer sidan för nästa besökare som frågar om samma webbplats.
Det är poängen med den dagliga revalidations i handlern. Två besökare på samma domän kostar ett upstream-anrop, och tusen besökare på tusen domäner kostar tusen anrop, så cachen är budgeten.
Var uppmärksam på motsatsen också. En sida som varierar sin frågestring med en tidsstämpel slår sönder cachen helt, och fakturan visar det innan loggarna gör det.
Revalidatera i ett schema som matchar index. En daglig fönster passar månatliga data, och en kortare tillför bara anrop utan att tillföra nya fakta.
Hårdning, i den ordning vi skulle göra det
Handlern är den lätta delen. Hårdningen är där en nyckel läcker eller ett saldo töms, så gå igenom dessa i ordning.
Skanna ditt eget Nätverksfönster
Öppna Chrome DevTools på din egen dashboard och läs Nätverksfönstret. Titta noga. Om ett anrop där visar sk_live_ någonstans, rotera nyckeln idag. Den här kontrollen tar en minut och hittar det vanligaste läckaget.
Sök i svarskropparna såväl som i begäranderubrikerna. En nyckel som ekas i ett felmeddelande är fortfarande ett läckage.
Begränsa anrop per användare
Nästa, lägg till en gräns per användare. Placera den framför routen. Handlern ovanför förbrukar din kredit för alla som kan nå den, så koppla den till en session och begränsa anrop per timme. Välj gränsen från budgetmatematiken på kreditsidan.
Börja generös och strama åt senare. En gräns som blockerar riktiga användare från dag ett lär dem att hitta ett annat verktyg.
Namnge vem som fyller på
Även värt en rad i driftmanualen: vem som kallas när saldet sjunker under en viss nivå, och vem som fyller på det. Namnge personen.
Skriv ner fyllningssteget som en checklista med länken till kontosidan och förväntad handläggningstid. En driftmanual som bara finns i en persons huvud sviktar första gången den personen är ledig. Steget i sig tar minuter, så skrivuppgiften är det större jobbet.
Logga saldet innan det tar slut
Logga sedan _credits från varje upstream-svar till dina egna mätvärden. En graf över återstående saldo, provtagad dagligen, varnar dig en vecka innan HTTP 402 gör det.
Välj varningsnivån från din egen veckovisa användning. Ett nummer valt från en mall matchar sällan dina verkliga anrop.
Behåll det sista värdet du såg i minnet såväl som i loggen, så att routen kan vägra betalda anrop när numret sjunker under varningsnivån. Den här kontrollen är en rad, och den stoppar en trafikstorm från att förbruka resten.
Håll tillåtna åtgärder på ett ställe
En vana till som lönar sig. Håll tillåtna åtgärder i en delad konstant, importerad av handlern och av testerna, så att en ny åtgärd inte kan nå produktionen utan att någon redigerar en synlig lista och en granskare märker det.
En fil, en import. Ingen bör behöva leta efter listan i tre komponenter för att veta vad proxyn accepterar.
Testa runtime:n du distribuerar till
Slutligen, kör handlern under Node 20 eller edge runtime av ditt val och testa båda. Att streama uppströmskroppen, som koden gör, fungerar i båda, men hanteringen av rubriker skiljer sig och är värd en testning.
Lägg till ett test som anropar routen med en dålig åtgärd och ett med en giltig åtgärd, och kontrollerar statuskoderna. Dessa två tester fångar de flesta regressioner i tillåtna åtgärder och rubrikhanteringen.
Koppla ihop proxyn i ordning
- 1
Lägg till nyckeln på servern
Lagra nyckeln i din servermiljö under ett namn utan ett offentligt prefix, starta sedan om dev-servern så att den laddas.
- 2
Skriv tillåtna åtgärder först
Lista de åtgärder som dina skärmar verkligen anropar. Allt annat får en 400 från din egen route, innan det når uppströms-API:et.
- 3
Hämta din route från en serverkomponent
Rendera raderna där datan anländer. Webbläsaren anropar bara din egen domän, så uppströmsadressen förblir utanför sidan.
- 4
Håll koll på saldot
Logga
_creditspå varje upstream-svar och sätt en varningsnivå du kan agera på, innan en 402 når en besökare som en 503.