Pemeriksa backlink web di Next.js, kunci disimpan di server
CORS terbuka, sehingga browser dapat memanggil API secara langsung. Melakukan hal ini dengan kunci berbayar adalah kesalahannya. Halaman ini menunjukkan lapisan server tipis yang memperbaikinya.

Mitos versus kenyataan
| Mitos | Kenyataan |
|---|---|
| "CORS terbuka berarti saya dapat mengirimkan kunci dalam bundel React saya." | CORS terbuka berarti panggilan berhasil. Siapa pun masih dapat membaca kunci dari tab jaringan dan menghabiskan saldo Anda. |
| "Tabel tautan harus dirender di sisi klien karena ini adalah dasbor." | Ambil data di server dan kirim HTML. Pengguna Anda melihat baris pada lukisan pertama dan kunci Anda tidak pernah meninggalkan kotak. |
| "Tindakan gratis juga perlu dilindungi." | domain, overview, stats dan suggest tidak memerlukan kunci, sehingga browser dapat memanggilnya secara langsung. |
| "Saya harus mengambil ulang data setiap kali melihat halaman." | Indeks dibangun ulang sekitar sebulan sekali. Cache 24 jam tidak kehilangan apa pun. |
| "Proxy harus meneruskan apa pun yang dikirim klien." | Daftar tindakan yang diizinkan. action=search yang diteruskan memakan biaya 3 kredit. |
Handler rute
App Router, file app/api/links/route.ts. Variabel lingkungan tidak pernah mencapai bundel klien karena tidak memiliki awalan 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" } });
}Mengapa handler terlihat seperti ini
Daftar tindakan yang diizinkan
Bug di UI Anda seharusnya tidak dapat memicu panggilan search seharga 3 kredit secara tidak sengaja.
Regex pada q
Hanya nama host polos yang lolos. Tidak ada skema, tidak ada jalur, tidak ada string kueri yang diselupkan ke hulu.
revalidate: 86400
Satu panggilan hulu per domain per hari. Dengan data bulanan, jangka waktu yang lebih lama juga tidak masalah.
402 dipetakan ke 503
Pengunjung Anda tidak perlu mengetahui bahwa saldo Anda kosong. Anda yang perlu tahu, jadi catatlah.

Rendering: apa yang dilihat oleh crawler dan pelanggan
Jika dasbor ini berada di balik login, rendering sisi server atau klien adalah pilihan UX. Jika ada yang bersifat publik, misalnya halaman pencarian gratis yang ingin Anda indekskan, render tabel pertama di server.
Halaman yang tiba sebagai cangkang kosong dan terisi setelah hidrasi adalah risiko untuk pengindeksan dan untuk ponsel lambat. Google dapat mengeksekusi JavaScript, tetapi ia mengantri langkah itu dan tidak selalu sampai ke sana dengan cepat.
Pertahankan ringkasan gratis di server
Masukkan panggilan overview murah dan gratis ke dalam Komponen Server dan biarkan pengunjung memicu penarikan berbayar yang lebih dalam dari tombol. Pemisahan ini juga menjaga pengeluaran kredit Anda dapat diprediksi.
Panggilan gratis tidak memakan biaya, sehingga ringkasan yang dirender server tidak menambah pengeluaran kredit. Hanya penarikan berbayar yang memerlukan tombol dan sesi.
Pikirkan keadaan kosong terlebih dahulu. Pengunjung yang mendarat di tabel kosong akan menganggap alat ini rusak, dan lukisan kedua yang cepat tidak membatalkan kesan pertama itu.
Pilih tindakan gratis untuk layar pertama dan tindakan berbayar untuk pendalaman. Pengunjung yang membuka halaman dan melihat ringkasan gratis tidak memiliki alasan untuk menekan tombol berbayar secara tidak sengaja.
Tautkan penjelasannya, bukan hanya angkanya
Menghubungkan angka adalah bagian yang mudah. Menjelaskannya lebih sulit, jadi tautkan rumus DR dari tooltip dan sebutkan bahwa indeks diperbarui setiap bulan.
Pengunjung yang melihat skor tanpa rumusnya akan menebak-nebak, jadi tautan tersebut harus berada di sebelah angka di layar.

Bagian server dan klien dari satu dasbor
Sebagian besar dasbor membutuhkan kedua jenis kode tersebut. Kepemilikan menentukan pemisahan. Kunci, data, dan aturan akses masing-masing memiliki satu rumah. Halaman pemeriksa backlink web yang bersifat publik membutuhkan pemisahan yang sama dengan yang di balik login.
Ambil data di server, urutkan di klien
Tabel backlink adalah komponen klien yang wajar ketika orang mengurutkan, memfilter, dan menavigasi halaman. Baris harus berasal dari komponen server yang sudah memanggil rute Anda, sehingga browser tidak pernah memegang kunci atau URL hulu.
Mengurutkan sepuluh baris seketika di klien. Mengurutkan lima ribu baris dalam permintaan baru setiap kali lambat, jadi simpan set lengkap di komponen yang sudah Anda muat.
Jaga agar tetap sederhana. Tabel yang mengurutkan seketika mengalahkan tabel yang mengambil ulang data setiap kali klik.
Pertahankan batas klien tetap rendah
Dorong batas sedalam mungkin ke dalam pohon. Widget yang diimpor secara dinamis dan hanya di sisi klien dirender di browser, sementara komponen yang mengambil data baris tetap di server. Teruskan baris sebagai prop, dan pertahankan pengambilan data di atas batas.
Jika Anda memindahkan pengambilan data di bawah batas, browser membuat panggilan sendiri. Itu adalah kesalahan yang diperingatkan oleh bagian kebocoran kunci, jadi periksa tab Jaringan setelah setiap perubahan pada batas ini.
Di mana tepatnya garisnya jatuh? Gambarkan di kertas terlebih dahulu, dengan satu kotak untuk pengambilan data dan satu kotak untuk setiap kontrol interaktif, dan hitung panah yang melintasinya.
Cangkang dan pemeriksaan sesi tetap di server
Tata letak dan pemeriksaan sesi menentukan apa yang boleh dilihat pengunjung, sehingga mereka berada di server. Tabel interaktif, formulir, dan grafik dapat dihidrasi setelahnya tanpa mengubah keputusan tersebut.
Penjaga sisi klien dapat menyembunyikan tombol, tetapi tidak dapat menghentikan permintaan. Handler rute dan tata letak server adalah tempat akses sebenarnya ditegakkan.
Kemudian kirimkan permintaan yang sama tanpa cookie dan konfirmasi bahwa server menolaknya, bukan hanya halaman.
Aturan yang sama mencakup proxy itu sendiri. Pemeriksaan sesi yang hanya terjadi di browser tidak dapat melindungi rute, sehingga rute memeriksa sesi juga sebelum menghabiskan kredit.
Cache per domain, bukan per pengunjung
Rute membangun URL hulu dari domain, sehingga kunci cache mengikuti domain juga. Satu pengunjung yang melihat situs memanaskan halaman untuk pengunjung berikutnya yang menanyakan situs yang sama.
Itulah tujuan revalidasi harian di handler. Dua pengunjung pada domain yang sama memakan satu panggilan hulu, dan seribu pengunjung pada seribu domain memakan seribu panggilan, sehingga cache adalah anggaran.
Perhatikan juga kasus sebaliknya. Halaman yang mengubah string kuerinya dengan stempel waktu membatalkan cache sepenuhnya, dan tagihan menampilkannya sebelum log melakukannya.
Revalidasikan pada jadwal yang sesuai dengan indeks. Jendela harian sesuai untuk data bulanan, dan yang lebih pendek hanya menambah panggilan tanpa menambah fakta baru.
Penguatan, sesuai urutan yang akan kami lakukan
Handler adalah bagian yang mudah. Penguatan adalah tempat kunci bocor atau saldo terkuras, jadi kerjakan ini secara berurutan.
Pindai tab Jaringan Anda sendiri
Buka Chrome DevTools di dasbor Anda sendiri dan baca tab Jaringan. Perhatikan dengan cermat. Jika permintaan di sana menunjukkan sk_live_ di mana pun, putar kunci hari ini. Pemeriksaan ini memakan waktu satu menit dan menemukan kebocoran paling umum.
Telusuri juga badan respons serta header permintaan. Kunci yang dipantulkan dalam pesan kesalahan masih merupakan kebocoran.
Batasi panggilan per pengguna
Selanjutnya, tambahkan batas per pengguna. Letakkan di depan rute. Handler di atas menghabiskan kredit Anda untuk siapa pun yang dapat mencapainya, jadi kaitkan dengan sesi dan batasi panggilan per jam. Pilih batas dari matematika anggaran di halaman kredit.
Mulailah dengan murah hati dan kencangkan nanti. Batas yang memblokir pengguna sungguhan pada hari pertama mengajarkan mereka untuk menemukan alat lain.
Sebutkan siapa yang mengisi ulang
Juga layak satu baris dalam buku panduan: siapa yang dipanggil ketika saldo turun di bawah tingkat tertentu, dan siapa yang mengisinya. Sebutkan nama orangnya.
Tuliskan langkah pengisian ulang sebagai daftar periksa dengan tautan halaman akun dan waktu penyelesaian yang diharapkan. Buku panduan yang hanya ada di kepala satu orang gagal saat orang itu sedang cuti. Langkah itu sendiri memakan waktu beberapa menit, sehingga penulisan adalah pekerjaan yang lebih besar.
Catat saldo sebelum habis
Kemudian catat _credits dari setiap respons hulu ke metrik Anda sendiri. Grafik saldo yang tersisa, yang diambil sampelnya setiap hari, memperingatkan Anda seminggu sebelum HTTP 402 melakukannya.
Pilih tingkat peringatan dari penggunaan mingguan Anda sendiri. Angka yang dipilih dari template jarang cocok dengan panggilan nyata Anda.
Simpan nilai terakhir yang Anda lihat di memori serta di log, sehingga rute dapat menolak penarikan berbayar sekali angkanya turun di bawah tingkat peringatan. Pemeriksaan ini hanya satu baris, dan itu menghentikan lalu lintas ledakan dari menghabiskan sisa.
Pertahankan daftar yang diizinkan di satu tempat
Kebiasaan lain juga bermanfaat. Simpan daftar yang diizinkan dalam konstanta bersama, yang diimpor oleh handler dan oleh pengujian, sehingga tindakan baru tidak dapat mencapai produksi tanpa seseorang mengedit daftar yang terlihat dan seorang peninjau menyadarinya.
Satu file, satu impor. Tidak ada yang harus mencari daftar di tiga komponen untuk mengetahui apa yang akan diterima proxy.
Uji runtime yang Anda terapkan
Terakhir, jalankan handler di bawah Node 20 atau edge runtime pilihan Anda dan uji keduanya. Mengalirkan badan hulu, seperti yang dilakukan kode, berfungsi di masing-masing, tetapi penanganan header berbeda dan layak diuji.
Tambahkan satu pengujian yang memanggil rute dengan tindakan buruk dan satu dengan tindakan valid, dan assert pada kode status. Dua pengujian itu menangkap sebagian besar regresi dalam daftar yang diizinkan dan penanganan header.
Menghubungkan proxy secara berurutan
- 1
Tambahkan kunci di server
Simpan kunci di lingkungan server Anda dengan nama tanpa awalan publik, lalu mulai ulang server pengembangan agar dimuat.
- 2
Tulis daftar yang diizinkan terlebih dahulu
Daftarkan tindakan yang benar-benar dipanggil oleh layar Anda. Apa pun selain itu akan mendapatkan 400 dari rute Anda sendiri, sebelum mencapai API hulu.
- 3
Ambil data rute Anda dari komponen server
Render baris di mana data tiba. Browser hanya memanggil domain Anda sendiri, sehingga alamat hulu tetap di luar halaman.
- 4
Pantau saldo
Catat
_creditspada setiap respons hulu dan tetapkan tingkat peringatan yang dapat Anda tindaklanjuti, sebelum 402 mencapai pengunjung sebagai 503.