Narzędzie do sprawdzania backlinków w Next.js z kluczem przechowywanym na serwerze
CORS jest otwarty, więc przeglądarka może wywołać API bezpośrednio. Użycie płatnego klucza w ten sposób to błąd. Ta strona pokazuje cienką warstwę serwera, która to naprawia.

Mit versus rzeczywistość
| Mit | Rzeczywistość |
|---|---|
| "Otwarty CORS oznacza, że mogę umieścić klucz w pakiecie Reacta." | Otwarty CORS oznacza, że wywołanie działa. Każdy może jednak odczytać klucz z zakładki Sieć i wydać Twój balans. |
| "Tabela linków musi być renderowana po stronie klienta, ponieważ jest to dashboard." | Pobierz dane na serwerze i wyślij HTML. Użytkownik widzi wiersze przy pierwszym malowaniu ekranu, a Twój klucz nigdy nie opuszcza serwera. |
| "Darmowe akcje również wymagają ochrony." | domain, overview, stats i suggest nie wymagają klucza, więc przeglądarka może wywoływać je bezpośrednio. |
| "Powinienem odświeżać dane przy każdym przeglądaniu strony." | Indeks jest odbudowywany około raz w miesiącu. Buforowanie na 24 godziny nie powoduje utraty danych. |
| "Proxy powinno przekazywać wszystko, co wyśle klient." | Zezwalaj tylko na używane akcje. Przekazany action=search kosztuje 3 kredyty. |
Handler trasy
App Router, plik app/api/links/route.ts. Zmienna środowiskowa nigdy nie trafi do pakietu klienta, ponieważ nie ma prefiksu 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" } });
}Dlaczego handler wygląda tak, jak wygląda
Lista dozwolonych akcji
Błąd w Twoim UI nie powinien przypadkowo uruchomić kosztownego wywołania search za 3 kredyty.
Regex na q
Przechodzi tylko sama nazwa hosta. Bez schematu, ścieżki ani stringu zapytania przesyłanego dalej.
revalidate: 86400
Jedno wywołanie upstream na domenę dziennie. Przy miesięcznych danych dłuższy czas również jest OK.
402 mapowane na 503
Twoi odwiedzający nie muszą wiedzieć, że Twój balans jest pusty. Ty musisz, więc zaloguj to zdarzenie.

Renderowanie: to, co widzą zarówno crawler, jak i klient
Jeśli ten dashboard jest za logowaniem, renderowanie po stronie serwera czy klienta to kwestia UX. Jeśli coś jest publiczne, np. strona darmowego lookupa, którą chcesz zaindeksować, renderuj pierwszą tabelę na serwerze.
Strona, która przychodzi jako pusta powłoka i wypełnia się po hydratacji, jest ryzykiem zarówno dla indeksowania, jak i dla wolnych telefonów. Google może wykonywać JavaScript, ale umieszcza ten krok w kolejce i nie zawsze dociera do niego szybko.
Zachowaj darmowy overview na serwerze
Umieść tanie, darmowe wywołanie overview w komponencie serwera, a odwiedzający niech uruchamia płatne, głębsze zapytania przyciskiem. Ten podział sprawia też, że wydatki na kredyty są przewidywalne.
Darmowe wywołania nic nie kosztują, więc renderowany serwerowo overview nie generuje wydatków na kredyty. Tylko płatne zapytania wymagają przycisku i sesji.
Pomyśl o stanie pustym jako pierwszym. Odwiedzający, który trafi na pustą tabelę, założy, że narzędzie jest zepsute, a szybkie drugie malowanie ekranu nie cofnie tego pierwszego wrażenia.
Wybierz darmowe akcje na pierwszy ekran, a płatne na dalsze szczegóły. Odwiedzający, który otwiera stronę i widzi darmowe podsumowanie, nie ma powodu, by przypadkowo nacisnąć przycisk płatny.
Linkuj wyjaśnienie, a nie tylko liczbę
Podpięcie liczb to łatwa część. Wyjaśnienie ich jest trudniejsze, więc linkuj do formuły DR z tooltipa i wspomnij, że indeks odświeża się miesięcznie.
Odwiedzający, który widzi wynik bez jego formuły, będzie się go domyślać, więc link powinien znajdować się obok liczby na ekranie.

Części serwerowe i klienckie jednego dashboardu
Większość dashboardów wymaga obu rodzajów kodu. Własność decyduje o podziale. Klucz, dane i reguły dostępu mają swoje jedno miejsce. Strona narzędzia do sprawdzania backlinków, która jest publiczna, wymaga tego samego podziału co ta za logowaniem.
Pobieranie na serwerze, sortowanie na kliencie
Tabela backlinków to rozsądny komponent kliencki, gdy ludzie sortują, filtrują i paginują ją. Wiersze powinny pochodzić z komponentu serwera, który już wywołał Twoją trasę, więc przeglądarka nigdy nie trzyma klucza ani adresu upstream.
Sortowanie dziesięciu wierszy jest natychmiastowe na kliencie. Sortowanie pięciu tysięcy wierszy przy każdym nowym żądaniu jest wolne, więc trzymaj pełny zestaw w komponencie, który już załadowałeś.
Zostań prosty. Tabela, która sortuje natychmiastowo, bije tę, która pobiera dane ponownie przy każdym kliknięciu.
Trzymaj granicę klienta niską
Przesuń granicę jak najniżej w drzewie. Dynamicznie importowany widget tylko kliencki renderuje się w przeglądarce, podczas gdy komponent, który pobrał wiersze, zostaje na serwerze. Przekazuj wiersze jako props i trzymaj fetch powyżej granicy.
Jeśli przeniesiesz fetch poniżej granicy, przeglądarka wykona wywołanie sama. To błąd, o którym ostrzega sekcja wycieku klucza, więc sprawdzaj zakładkę Sieć po każdej zmianie tej granicy.
Gdzie dokładnie spada linia? Narysuj ją najpierw na papierze, z jednym polem dla fetcha i jednym dla każdej interaktywnej kontrolki, i policz strzałki, które ją przekraczają.
Powłoka i sprawdzenia sesji zostają na serwerze
Layout i sprawdzenia sesji decydują, co odwiedzający w ogóle może zobaczyć, więc należą do serwera. Interaktywne tabele, formularze i wykresy mogą się hydratać później bez zmiany tej decyzji.
Guard po stronie klienta może ukryć przycisk, ale nie zatrzyma żądania. Handler trasy i layout serwera są miejscami, gdzie dostęp jest faktycznie egzekwowany.
Następnie wyślij to samo żądanie bez ciasteczka i potwierdź, że serwer je odrzuca, a nie tylko strona.
Ta sama zasada dotyczy samego proxy. Sprawdzenie sesji, które dzieje się tylko w przeglądarce, nie chroni trasy, więc trasa również sprawdza sesję przed wydaniem kredytu.
Buforuj na domenę, nie na odwiedzającego
Handler buduje URL upstream na podstawie domeny, więc klucz bufora również odnosi się do domeny. Jeden odwiedzający, który sprawdzi stronę, nagrzewa stronę dla następnego odwiedzającego pytającego o tę samą stronę.
O to chodzi w codziennym revalidate w handlerze. Dwóch odwiedzających na tej samej domenie kosztuje jedno wywołanie upstream, a tysiąc odwiedzających na tysiącu domen kosztuje tysiąc wywołań, więc bufor to Twój budżet.
Obserwuj również przeciwny przypadek. Strona, która zmienia string zapytania o znacznik czasu, całkowicie pokonuje bufor, a rachunek pokaże to zanim zrobią to logi.
Revalidate w harmonogramie dopasowanym do indeksu. Okno dzienne pasuje do danych miesięcznych, a krótsze tylko dodaje wywołań bez dodawania nowych faktów.
Wzmocnienie, w kolejności, w jakiej powinniśmy to zrobić
Handler to łatwa część. Wzmocnienie to miejsce, gdzie klucz wycieka lub balans się wyczerpuje, więc przejdź przez to w kolejności.
Przeskanuj własną zakładkę Network
Otwórz Chrome DevTools na swoim dashboardzie i przeczytaj zakładkę Network. Przyjrzyj się uważnie. Jeśli jakieś żądanie tam pokazuje sk_live_ gdziekolwiek, obróć klucz jeszcze dziś. To sprawdzenie zajmuje minutę i znajduje najczęstszy wyciek.
Przeszukaj również ciała odpowiedzi oraz nagłówki żądań. Klucz echoowany w komunikacie o błędzie to również wyciek.
Ogranicz liczbę wywołań na użytkownika
Następnie dodaj limit na użytkownika. Umieść go przed trasą. Powyższy handler wydaje Twój kredyt dla każdego, kto do niego dotrze, więc powiąż go z sesją i ogranicz liczbę wywołań na godzinę. Wybierz limit z matematyki budżetu na stronie kredytów.
Zacznij hojnie i zaostrzaj później. Limit, który blokuje prawdziwych użytkowników w pierwszym dniu, uczy ich znalezienia innego narzędzia.
Wymień osobę, która dołania
Warto również dodać linię w instrukcji: kto jest powiadamiany, gdy balans spadnie poniżej ustalonego poziomu, i kto go dołania. Wymień osobę.
Zapisz krok dołania jako checklistę z linkiem do strony konta i oczekiwanym czasem realizacji. Instrukcja, która istnieje tylko w głowie jednej osoby, zawodzi za pierwszym razem, gdy ta osoba jest na urlopie. Sam krok zajmuje minuty, więc zapisanie go jest większym zadaniem.
Loguj balans przed jego wyczerpaniem
Następnie loguj _credits z każdej odpowiedzi upstream do swoich metryk. Wykres pozostałego balansu, próbkowany dziennie, ostrzeże Cię tydzień przed HTTP 402.
Wybierz poziom alertu na podstawie własnego tygodniowego zużycia. Liczba wybrana z szablonu rzadko pasuje do Twoich rzeczywistych wywołań.
Trzymaj ostatnią widzianą wartość w pamięci oraz w logu, więc trasa może odrzucić płatne zapytania, gdy liczba spadnie poniżej poziomu alertu. To sprawdzenie to jedna linia, a zatrzymuje ona burst ruchu, który wydałby resztę.
Trzymaj listę dozwolonych w jednym miejscu
Jeszcze jedna nawyk, który się opłaci. Trzymaj listę dozwolonych w współdzielonej stałej, importowanej przez handler i testy, więc nowa akcja nie może trafić do produkcji bez edycji widocznej listy i zauważenia jej przez recenzenta.
Jeden plik, jeden import. Nikt nie powinien łapać się za głowę szukając listy w trzech komponentach, aby wiedzieć, co proxy zaakceptuje.
Testuj środowisko uruchomieniowe, do którego wdrażasz
Ostatnie, uruchom handler pod Node 20 lub edge runtime według wyboru i przetestuj oba. Strumieniowanie ciała upstream, jak robi to kod, działa w każdym, ale obsługa nagłówków się różni i warto to przetestować.
Dodaj jeden test, który wywołuje trasę z błędną akcją i jeden z poprawną akcją, i asertywuj kody statusu. Te dwa testy łapią większość regresji w liście dozwolonych i obsłudze nagłówków.
Podłączanie proxy w kolejności
- 1
Dodaj klucz na serwerze
Przechowuj klucz w środowisku serwera pod nazwą bez publicznego prefiksu, a następnie zrestartuj serwer dev, aby się załadował.
- 2
Napisz listę dozwolonych jako pierwszą
Wymień akcje, które Twoje ekrany naprawdę wywołują. Wszystko inne zwróci 400 z Twojej trasy, zanim dotrze do API upstream.
- 3
Pobierz swoją trasę z komponentu serwera
Renderuj wiersze tam, gdzie przychodzą dane. Przeglądarka wywołuje tylko Twoją domenę, więc adres upstream pozostaje poza stroną.
- 4
Obserwuj balans
Loguj
_creditsprzy każdej odpowiedzi upstream i ustaw poziom alertu, na który możesz zareagować, zanim 402 dotrze do odwiedzającego jako 503.