Kontrola zpětných odkazů na webu v Next.js, klíč zůstává na serveru
CORS je otevřený, takže prohlížeč může volat API přímo. Použití placeného klíče tímto způsobem je chyba. Tato stránka ukazuje tenkou vrstvu serveru, která to řeší.

Mýtus versus realita
| Mýtus | Realita |
|---|---|
| "Otevřené CORS znamená, že mohu vložit klíč do svého React balíčku." | Otevřené CORS znamená, že volání funguje. Klíč si může kdokoli přečíst v síťové kartě a utratit váš zůstatek. |
| "Tabulka odkazů musí být vykreslena na klientovi, protože jde o dashboard." | Načtěte data na serveru a pošlete HTML. Uživatel vidí řádky při prvním vykreslení a váš klíč nikdy neopustí server. |
| "I bezplatné akce potřebují ochranu." | domain, overview, stats a suggest nepotřebují klíč, takže je může prohlížeč volat přímo. |
| "Měl bych znovu načítat data při každém zobrazení stránky." | Index se rebuilduje přibližně měsíčně. Cachování na 24 hodin nic neznamená. |
| "Proxy by měla forwardovat všechno, co klient pošle." | Povolte pouze akce, které používáte. Forwardovaný action=search stojí 3 kredity. |
Route handler
App Router, soubor app/api/links/route.ts. Proměnná prostředí nikdy nedostane se do klientova balíčku, protože nemá prefix 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" } });
}Proč handler vypadá takto
Seznam povolených akcí
Chyba ve vašem UI by neměla náhodou spustit 3-kreditní volání search.
Regex na q
Projde pouze samotná doména. Žádný protokol, žádná cesta, žádný query string, který by se propracoval nahoru.
revalidate: 86400
Jedno volání na serveru pro doménu za den. S měsíčními daty je delší doba také v pořádku.
402 mapováno na 503
Vaši návštěvníci nemusí vědět, že je zůstatek prázdný. Vy ano, takže to logujte.

Renderování: co uvidí crawler i zákazník
Pokud je tento dashboard za přihlášením, je renderování na serveru nebo klientovi volbou UX. Pokud je něco veřejné, například stránka pro volné vyhledávání, kterou chcete indexovat, vykreslete první tabulku na serveru.
Stránka, která přijde jako prázdná skořápka a po hydrataci se zaplní, je riziková pro indexování i pro pomalá zařízení. Google sice umí spustit JavaScript, ale tento krok zařadí do fronty a nemusí ho provést rychle.
Ponechte volný přehled na serveru
Umístěte levé, bezplatné volání overview do Server Componentu a nechte návštěvníka spustit placená, hlubší volání tlačítkem. Toto rozdělení také udržuje vaše náklady na kredity předvídatelné.
Bezplatná volání nic nestojí, takže serverově vykreslený přehled nezvyšuje spotřebu kreditů. Pouze placená volání potřebují tlačítko a session.
Nejdříve myslete na prázdný stav. Návštěvník, který narazí na prázdnou tabulku, předpokládá, že nástroj nefunguje, a rychlé druhé vykreslení tento první dojem nezmění.
Vyberte bezplatné akce pro první obrazovku a placené pro podrobnosti. Návštěvník, který otevře stránku a uvidí volný souhrn, nemá důvod omylem stisknout placené tlačítko.
Odkazujte na vysvětlení, ne jen na číslo
Propojení čísel je snadná část. Vysvětlení je těžší, proto odkazujte na vzorec DR z tooltipu a zmíněte, že index se obnovuje měsíčně.
Návštěvník, který uvidí skóre bez vzorce, bude hádat, proto by měl být odkaz vedle čísla na obrazovce.

Serverová a klientová část jednoho dashboardu
Většina dashboardů potřebuje oba typy kódu. Vlastnictví rozhoduje o rozdělení. Klíč, data a pravidla přístupu mají každé své místo. Stránka pro kontrolu zpětných odkazů, která je veřejná, potřebuje stejné rozdělení jako ta za přihlášením.
Načtěte na serveru, řaďte na klientovi
Tabulka zpětných odkazů je rozumný klientový komponent, když ji lidé třídí, filtrují a stránkují. Řádky by měly pocházet ze serverového komponentu, který již volal váš route handler, aby prohlížeč nikdy neměl klíč ani URL serveru.
Třídění deseti řádků je na klientovi okamžité. Třídění pět tisíc řádků při každém novém požadavku je pomalé, takže si ponechte celou sadu v komponentu, který jste již načítali.
Ponechte to jednoduché. Tabulka, která se třídí okamžitě, je lepší než ta, která znovu načítá data při každém kliknutí.
Ponechte klientovou hranici nízkou
Posuňte hranici co nejníže ve stromu. Dynamicky importovaný, pouze klientový widget se vykreslí v prohlížeči, zatímco komponent, který načítal data, zůstane na serveru. Předávejte řádky jako props a ponechte fetch nad hranicí.
Pokud přesunete fetch pod hranici, provede volání přímo prohlížeč. To je chyba, na kterou varuje sekce o úniku klíče, takže po každé změně této hranice zkontrolujte síťovou kartu.
Kde přesně čára prochází? Nakreslete si ji nejprve na papír, s jednou krabicí pro fetch a jednou pro každý interaktivní ovládací prvek, a spočítejte šipky, které ji překračují.
Kontrola skořápky a session zůstává na serveru
Layout a kontrola session rozhodují o tom, co návštěvník vůbec uvidí, takže patří na server. Interaktivní tabulky, formuláře a grafy mohou být hydratovány později bez změny tohoto rozhodnutí.
Klientská ochrana může skrýt tlačítko, ale nemůže zastavit požadavek. Route handler a serverový layout jsou místy, kde je přístup skutečně vynucen.
Pak odešlete stejný požadavek bez cookie a potvrďte, že ho server odmítne, nejen stránka.
Stejné pravidlo platí pro samotnou proxy. Kontrola session, která probíhá pouze v prohlížeči, nemůže chránit route, takže route také zkontroluje session před tím, než utratí kredit.
Cachujte podle domény, ne podle návštěvníka
Route handler sestavuje URL serveru z domény, takže klíč cache také následuje doménu. Jeden návštěvník, který zkontroluje web, nahřeje stránku pro dalšího návštěvníka, který se ptá na stejný web.
To je smyslem denní revalidace v handleru. Dva návštěvníci na stejné doméně stojí jedno volání nahoru, a tisíc návštěvníků na tisíci doménách stojí tisíc volání, takže cache je rozpočet.
Sledujte také opačný případ. Stránka, která mění query string podle časového razítka, cache zcela zruší a účet to ukáže dříve než logy.
Revalidujte podle rozvrhu, který odpovídá indexu. Denní okno vyhovuje měsíčním datům a kratší pouze přidává volání bez přidání nových faktů.
Zpevnění v pořadí, v jakém bychom to dělali
Handler je ta snadná část. Zpevnění je tam, kde klíč unikne nebo zůstatek vyteče, takže projděte tyto kroky v pořadí.
Skenujte svou vlastní síťovou kartu
Otevřete Chrome DevTools na svém dashboardu a přečtěte si síťovou kartu. Podívejte se pozorně. Pokud tam požadavek ukazuje sk_live_ kdekoli, otočte klíč dnes. Tato kontrola trvá minutu a najde nejčastější únik.
Hledejte také v tělech odpovědí a nejen v hlavičkách požadavků. Klíč vrácený v chybové zprávě je také únik.
Omezte volání na uživatele
Dalším krokem je přidání limitu na uživatele. Umístěte ho před route handler. Handler výše utratí váš kredit pro kohokoli, kdo se k němu dostane, takže ho váže na session a omezte volání za hodinu. Vyberte limit z výpočtů rozpočtu na stránce kreditů.
Začněte štědře a zpřísněte později. Limit, který zablokuje skutečné uživatele první den, je naučí hledat jiný nástroj.
Uveďte, kdo doplňuje
Stojí také za zmínku v runbooku: kdo je vyrozuměn, když zůstatek klesne pod určitou úroveň, a kdo ho doplní. Uveďte jméno osoby.
Zapište krok doplňování jako kontrolní seznam s odkazem na stránku účtu a očekávanou dobou vyřízení. Runbook, který existuje pouze v hlavě jedné osoby, selže při prvním výpadku této osoby. Samotný krok trvá minuty, takže zápis je větší práce.
Logujte zůstatek před jeho vyčerpáním
Pak logujte _credits z každé odpovědi serveru do vašich metrik. Graf zbývajícího zůstatku, vzorkovaný denně, vás upozorní týden předtím, než přijde HTTP 402.
Vyberte úroveň upozornění z vašeho týdenního využití. Číslo zvolené ze šablony zřídka odpovídá vašim skutečným voláním.
Udržujte poslední hodnotu, kterou jste viděli, v paměti i v logu, aby route mohla odmítnout placená volání, jakmile číslo klesne pod úroveň upozornění. Tato kontrola je jeden řádek a zastaví, aby traffic utratil zbytek.
Udržujte seznam povolených akcí na jednom místě
Ještě jedna zvyklost se vyplatí. Udržujte seznam povolených akcí ve sdílené konstantě, importované handlerem i testy, aby nový akce nemohla dosáhnout produkce bez toho, aby někdo upravil viditelný seznam a review to všiml.
Jeden soubor, jeden import. Nikdo by neměl muset hledat seznam ve třech komponentech, aby věděl, co proxy přijme.
Testujte runtime, na který nasazujete
Nakonec spusťte handler pod Node 20 nebo edge runtime dle vašeho výběru a otestujte oba. Streamování těla serveru, jak kód dělá, funguje v obou, ale zpracování hlaviček se liší a stojí za test.
Přidejte jeden test, který volá route s špatnou akcí a jeden s platnou akcí, a assertujte na status kódech. Tyto dva testy chytí většinu regresí v seznamu povolených akcí a zpracování hlaviček.
Propojení proxy v pořadí
- 1
Přidejte klíč na server
Uložte klíč do proměnných prostředí serveru pod jménem bez veřejného prefixu, poté restartujte dev server, aby se načetl.
- 2
Nejprve napište seznam povolených akcí
Vypište akce, které vaše obrazovky skutečně volají. Všechno ostatní dostane 400 od vaší route, než dosáhne serverového API.
- 3
Načtěte svou route z serverového komponentu
Vykreslete řádky tam, kde data přicházejí. Prohlížeč volá pouze vaši doménu, takže adresa serveru zůstane mimo stránku.
- 4
Sledujte zůstatek
Logujte
_creditspři každé odpovědi serveru a nastavte úroveň upozornění, na kterou můžete reagovat, než 402 dosáhne návštěvníka jako 503.