← वापस मैंने Next.js में 25,000-पेज की डायरेक्टरी कैसे बनाई: HostList Post-Mortem -- लाइन-आर्ट इलस्ट्रेशन

मैंने Next.js में एक 25,000-पेज की डायरेक्टरी कैसे बनाई: HostList पोस्ट-मॉर्टम

डायरेक्टरी और प्रोग्रामैटिक SEO

2022 के late में sometime मैंने खुद को convince कर लिया कि एक web hosting directory बनाना straightforward होगा। Data aggregate करो, pages generate करो, rank करो, monetise करो। Clean। मैंने पहले programmatic SEO plays किए थे, एक UK client के लिए एक localized real estate tool, एक SaaS comparison site जो 40k monthly visits पर peaked था, तो मैंने सोचा कि HostList एक six-week project होगा। यह सात महीने के करीब लगा। और इसने कुछ चीजों को लगभग तोड़ दिया: मेरी sleep schedule, मेरे एक junior dev की confidence, और एक £180/month Vercel bill जिसके लिए मैंने budget नहीं किया था।

यह पोस्ट-मॉर्टम है। असली वाला, LinkedIn वाला नहीं।

---

ब्रीफ जो मैंने खुद के लिए लिखा

HostList सीधी चीज़ थी। Web hosting providers की एक directory, shared, VPS, dedicated, managed WordPress, हर provider के लिए individual pages, comparison pages, category pages, और location-based pages (मसलन "best hosting in Germany")। गणित करो: ~400 providers × कई page types × 20+ filter combinations। तुम 25,000 पेजों तक सोचते हो उससे जल्दी पहुँच जाते हो।

मैंने Next.js को लगभग सोचे बिना चुना। हम Seahawk में इसे अपनी अधिकतर bigger React-based builds के लिए use करते थे। Ecosystem mature है, getStaticProps और getStaticPaths SEO-heavy static generation के लिए sense बनाते हैं, और मुझे व्यक्तिगत रूप से file-based routing को इस scale पर Remix या Gatsby से आसान लगता है।

पहला असली फैसला data layer का था। मैंने headless CMS को जल्दी खारिज कर दिया, मैं 25,000 entries के लिए Contentful rates नहीं देना चाहता था, और मैं किसी CMS पर bulk programmatic writes को cleanly handle करने का भरोसा नहीं रखता था। हम Supabase पर Postgres database पर उतरे, जिसके सामने एक lightweight Next.js API layer बैठा। वह हिस्सा असल में ठीक काम करा। बाकी सब कुछ complicated हो गया।

---

स्केल पर स्टैटिक जेनरेशन: जिसके बारे में कोई आपको चेतावनी नहीं देता

25,000 राउट्स के साथ getStaticPaths के बारे में यह बात है। यह काम करता है। तकनीकी रूप से। लेकिन आपका बिल्ड टाइम आपको अपनी जीवन के चुनावों पर सवाल उठाने पर मजबूर करेगा।

हमारे पहले पूरे build में 4 घंटे 47 मिनट लगे। Vercel पर। जो, अगर आप अपनी plan limits के साथ सावधानी नहीं रखते, तो यह वह तरह की चीज है जो रात 2 बजे एक billing notification का कारण बनती है। मैंने अपने फोन से उस Slack alert को देखा और सच में WordPress इस्तेमाल करने पर विचार किया।

`fallback: 'blocking'` Trap

मेरी पहली सोच सब कुछ pre-render करने की थी। हर पेज, हर combination। बुरा आइडिया, और उस वजह से नहीं जो ज्यादातर tutorials warn करते हैं (जो आमतौर पर बस "इसमें समय लगता है")। असली समस्या cache invalidation है। जब कोई hosting provider अपनी pricing update करता है (और वे करते हैं, लगातार), तुम्हें affected pages को rebuild करना पड़ता है। अगर सब कुछ statically pre-rendered है ISR के बिना, तो तुम उन data changes के लिए full rebuilds trigger कर रहे हो जो 25,000 में से शायद 30 पेजों को impact करते हैं।

मैंने Incremental Static Regeneration के लिए switch किया, जिसमें 86400 seconds (24 घंटे) के revalidate के साथ अधिकांश pages के लिए, और pricing-heavy provider pages के लिए 3600 seconds। यह पूरी project में single biggest quality-of-life improvement था। Build times 40 minutes से कम में आ गए क्योंकि हम केवल traffic priority द्वारा top ~2,000 pages को pre-render कर रहे थे और बाकी को on-demand के साथ generate करने दे रहे थे fallback: 'blocking' के साथ।

राउट ट्री को स्प्लिट करना

एक चीज जो मैं अलग तरीके से करता, और मैंने Seahawk में हर dev को बताया जो एक big programmatic project को touch करता था: अपने route tree को जल्दी split करें। 25,000 slugs return करने की कोशिश में एक monolithic getStaticPaths function मत रखें। हमने अपने को split किया:

  1. /providers/[slug], इंडिविजुअल प्रोवाइडर पेजेस (~400)
  2. /compare/[slugA]-vs-[slugB], हेड-टू-हेड कम्पेरिजन पेजेस (~8,000)
  3. /category/[type], कैटेगरी लैंडिंग पेजेस (~40)
  4. /location/[country]/[type], जियो × कैटेगरी कॉम्बिनेशन्स (~16,000+)
  5. /best/[use-case], क्यूरेटेड लिस्ट पेजेस (~600)

हर route group का अपना revalidation cadence है, अपना data-fetching logic है, और यह काफ़ी महत्वपूर्ण है, अपनी build priority है। location pages लगभग पूरी तरह on-demand हैं। provider pages हमेशा pre-rendered होते हैं। साफ़-सुथरा अलगाव।

---

द डेटा पाइपलाइन मेस (और हमने इसे कैसे ठीक किया)

2023 की शुरुआत में मैंने HostList के data collection side को बहुत loosely build करने की गलती की। हमारे पास एक scraping script था (Python में लिखा, BeautifulSoup और Webshare से rotating proxy pool का इस्तेमाल करते हुए), सुधार के लिए एक manual Google Sheet, और एक Supabase table। सत्य के तीन स्रोत। कोई भी एक-दूसरे से सही तरीके से बात नहीं कर रहा था।

एक junior developer, अच्छा बच्चा, अभी bootcamp से निकला, तीन हफ्ते एक sync script को maintain करता रहा जो Sheet और Supabase के बीच काम कर रहा था और हर बार जब कोई column का नाम बदलता था तो टूट जाता था। मुझे पहले ही हफ्ते Sheet को खत्म कर देना चाहिए था और एक proper internal admin UI build करना चाहिए था। हमने आखिरकार किया, Next.js API routes और एक Retool dashboard का इस्तेमाल करके, लेकिन हमने probably 60 engineering hours बर्बाद कर दिए।

समाधान: एक ही सत्य का स्रोत, हमेशा। डेटाबेस आधिकारिक है। हर चीज़ डेटाबेस में लिखी जाती है। एडमिन यूआई डेटाबेस से पढ़ता और लिखता है। स्क्रेपर डेटाबेस में लिखता है। यह स्पष्ट लगता है। पीछे मुड़कर देखने पर हमेशा ऐसा ही लगता है।

डेटा को बड़े पैमाने पर ताज़ा रखना

इसी साइज़ की डायरेक्टरी के लिए, डेटा ताज़ापन SEO से उतना ही चिंता का विषय है जितना UX का। Google देखता है जब प्राइसिंग टेबल £2.99/month दिखाता है किसी ऐसी योजना के लिए जो आठ महीने से £5.99 की है। हमने सेट अप किया:

  • Railway cron पर चलने वाली साप्ताहिक स्क्रेप जॉब (सस्ता, भरोसेमंद, डेडिकेटेड सर्वर की ज़रूरत नहीं)
  • एक Supabase डेटाबेस webhook जो तब फायर होता है जब price_updated_at कॉलम बदलता है, एक Next.js revalidation एंडपॉइंट को हिट करता है
  • Retool में मैनुअल ओवरराइड फ़्लैग ~30 प्रोवाइडर्स के लिए जिनकी साइट्स सक्रिय रूप से स्क्रेपर्स को ब्लॉक करती हैं

वह revalidation endpoint, /api/revalidate?secret=TOKEN&path=/providers/siteground, एक standard Next.js feature है, लेकिन इसे database webhook से जोड़ने में कुछ plumbing करनी पड़ी। हर minute की कीमत थी।

---

SEO आर्किटेक्चर: असली में क्या काम आया

मैंने पर्याप्त कंटेंट साइटें बनाई हैं यह जानने के लिए कि 25,000 पेज होना 25,000 पेज होने जैसा नहीं है जो रैंक करते हैं। तुलना पेज जाल थे। हमने अपने ~400 प्रदाताओं के लिए हर संभावित A-vs-B संयोजन तैयार किया, जिससे हमें लगभग 79,800 सैद्धांतिक जोड़े मिले। हमने उनमें से ~8,000 बनाए। और उनमें से अधिकांश, ईमानदारी से कहूं तो, पतले थे।

ईमानदार confession: मैंने लालच किया। SEO logic sound था, "SiteGround बनाम Bluehost" को real search volume मिलता है, comparison queries की long-tail huge है, लेकिन हमने प्रत्येक single page की existence को justify करने के लिए पर्याप्त unique content build नहीं किया। Google ने comparison section को crawl करना शुरू किया और clearly decide किया कि यह इसका time नहीं है।Google की अपनी thin content पर guidance blunt है इसके बारे में, और मुझे अपने आप के साथ पहले blunt होना चाहिए था।

हमने क्या किया रिकवरी के लिए

हमने साफ किया। Comparison pages को ~8,000 से घटाकर ~1,200 रखा, सिर्फ वे pairs जिनके पास demonstrable search volume है (Ahrefs में verified, कम से कम 50 monthly searches globally)। फिर हमने बाकी बचे pages को enrich किया:

  • डायनामिक "किसके लिए सर्वश्रेष्ठ है" सेक्शन संरचित प्रदाता डेटा से खींचे गए
  • असली अपटाइम डेटा (हमने एक तीसरे पक्ष के अपटाइम API के साथ एकीकृत किया)
  • Trustpilot डेटा से सीड किए गए उपयोगकर्ता समीक्षा सारांश, जहां उपलब्ध हो

परिणाम 1,200 पेज थे जो वास्तव में उपयोगी थे बजाय 8,000 पेजों के जो नहीं थे। तुलना सेक्शन पर ऑर्गेनिक ट्रैफिक अगले तीन महीनों में 340% बढ़ा। प्रतिकूल लगता है जब तक कि ऐसा न हो।

इस स्केल पर इंटरनल लिंकिंग

25,000 pages के साथ, internal linking manual नहीं हो सकती। हमने एक related-pages component build किया जो Supabase को build time पर query करता है (getStaticProps में) और category और location overlap के आधार पर पाँच सबसे relevant adjacent pages return करता है। कोई editorial intervention की जरूरत नहीं। यह perfect नहीं है, कभी-कभी एक VPS hosting page किसी थोड़े sideways चीज़ को link करता है, लेकिन यह 90% सही है, और इसका मतलब था कि हर page के पास day one से contextually relevant internal links थे।

---

परफॉर्मेंस: वह हिस्सा जो आपको विनम्र करता है

आप सोचेंगे कि static generation performance को आसान बना देगा। और conceptual level पर, यह करता है, pre-rendered HTML, edge-cached Vercel के CDN पर, कोई server-rendering overhead नहीं। लेकिन 25,000 pages का मतलब 25,000 मौके हैं कि आप अपने component tree के बारे में गलत निर्णय ले सकते हैं।

हमारी सबसे बड़ी performance problem provider comparison table था। यह एक heavy client-side React component था, lots state, lots conditional rendering, provider pages और comparison pages दोनों पर use किया गया। Mobile पर, यह Largest Contentful Paint लगभग 4.8 seconds का cause कर रहा था। Bad। वाकई एक site के लिए bad जहाँ primary traffic लोग एक purchase पर mid-decision हैं।

हमने इसे एक server-rendered static table के रूप में rebuild किया जिसमें interactive filter bits के लिए एक thin React hydration layer था। LCP 1.9 seconds तक गिर गया। यह magic नहीं है, यह सिर्फ boring चीज़ को properly करना है।

इमेज समस्या

हर provider के पास एक logo है। 400 logos, साथ ही screenshots, UI previews, feature icons। हमने पहले दो महीनों के लिए Vercel के built-in image optimisation पर ये सब host करने की गलती की। Bandwidth costs quietly भयानक थे। सब कुछ Cloudflare R2 पर move किया एक custom domain के साथ, अपना Vercel bill £180/month से £40/month तक drop किया। अगर आप कोई image-heavy चीज़ build कर रहे हैं, तो Cloudflare R2 को जल्दी देखें, free egress genuinely scale पर useful है।

---

बिल्ड पाइपलाइन असली में अब कैसी दिखती है

किसी के लिए भी जो ठोस तस्वीर चाहता है:

  1. Data collection, Python scraper एक Railway cron job पर, Supabase Postgres में लिखता है।
  2. एडमिन लेयर, Retool डैशबोर्ड मैनुअल एडिट्स, करेक्शन्स, और प्रोवाइडर फ्लैग्स के लिए
  3. Next.js ऐप, Pages router (हमने शुरुआत की थी App Router के स्टेबल होने से पहले), Vercel पर डिप्लॉय किया हुआ
  4. ISR + on-demand रीवैलिडेशन, टॉप ~2,000 पेज प्री-बिल्ट, बाकी on-demand, सभी के साथ 24h रीवैलिडेशन
  5. इमेजेस, Cloudflare R2, एक कस्टम सबडोमेन से सर्व की जाती हैं Cloudflare CDN के साथ फ्रंट में
  6. एनालिटिक्स, Plausible प्राइवेसी-फ्रेंडली ट्रैफिक डेटा के लिए, Ahrefs रैंकिंग ट्रैकिंग के लिए
  7. अपटाइम मॉनिटरिंग, BetterUptime पांच सबसे ट्रैफिक-हेवी पेज टाइप्स को देखता हुआ

यह glamorous नहीं है। यह maintain करने के लिए ज्यादातर बोरिंग भी है, जो कि बिल्कुल वही है जो आप चाहते हैं infrastructure से जिसे आप तीन साल तक चलाने वाले हैं।

---

ईमानदारी से की गई गलतियाँ, क्रमांकित

  1. बहुत व्यापक शुरुआत की। 25,000 पेज हमेशा लक्ष्य था, लेकिन मुझे 500 हाई-क्वॉलिटी पेज के साथ लॉन्च करना चाहिए था और विस्तार करना चाहिए था। इसके बजाय मैंने सब कुछ के साथ लॉन्च किया और पहले चार महीनों के लिए Google क्रॉल बजट की समस्या हुई।
  2. दिन एक से रीवेलिडेशन ठीक से सेटअप नहीं किया। हमने दो महीने पूरी रिबिल्ड्स पर बर्बाद किए जो ISR ने अनावश्यक बना दिया होता।
  3. Google शीट रखी। सिंगल सोर्स ऑफ ट्रुथ पहले सप्ताह से ही नॉन-नेगोशिएबल होना चाहिए था।
  4. कम्पेरिजन पेज क्वॉलिटी को कम आंका। वॉल्यूम एक स्ट्रेटेजी नहीं है।
  5. Vercel के image optimisation का इस्तेमाल बहुत लंबे समय तक किया। R2 पर छह हफ्ते बाद शिफ्ट किया, जितना पहले करना चाहिए था।
  6. रूट ट्री को काफी जल्दी स्प्लिट नहीं किया। तेज़ और धीमे रूट को एक ही getStaticPaths कॉल में मिक्स किया और फिर आश्चर्य हुआ कि बिल्ड्स धीमे क्यों हैं।

इनमें से हर एक चीज एक फैसला है जो उस वक्त रीजनेबल लगा था। यही वह हिस्सा है जो ट्यूटोरियल्स कैप्चर नहीं करते, बुरे आर्किटेक्चरल डिसीजन्स आमतौर पर अच्छे-सुनने वाले जस्टिफिकेशन्स होते हैं जब आप उन्हें लेते हो।

---

FAQ

शुरुआती बिल्ड को live जाने में कितना समय लगा?

पहली commit से v1 कहलाने लायक version तक सात महीने लगे। पहला rough public version करीब चौथे महीने में live था, लेकिन उसमें गंभीर content की कमी थी और comparison section ज्यादातर बेकार था। मैं कहूँ तो "technically live" होने में चार महीने और फिर "actually good" होने में तीन महीने और लगे।

क्या आप आज शुरुआत करते तो App Router use करते?

शायद हाँ, अगर नई projects 2023 के late से शुरू हों। App Router के server components इस तरह के data-heavy page generation के लिए बिल्कुल सटीक होते। लेकिन existing 25,000-page Pages Router app को migrate करना ऐसा project नहीं है जो मैं जल्द ही शुरू करूँ। Pages Router अभी भी काम करता है, और "काम करता है" को कम आँका जाता है।

जब providers business से बाहर निकल जाएँ या अपनी offering को significantly बदलें, तो आप कैसे handle करते हैं?

हमारे पास डेटाबेस में एक स्टेटस फ्लैग है, active, deprecated, redirected। Deprecated प्रोवाइडर्स को एक स्लिम आर्काइव पेज मिलता है पूरी तरह रिमूवल के बजाय, जो किसी भी बैकलिंक्स को प्रीजर्व करता है। Redirected प्रोवाइडर्स (जैसे जब एक होस्ट दूसरे को एक्वायर करता है) को 301 मिलता है जो Next.js redirects कॉन्फिग के जरिए हैंडल किया जाता है next.config.js में। हम स्टेटस फ्लैग्स को मंथली रिव्यू करते हैं।

अगर आप यह फिर से करते तो Next.js की जगह क्या use करते?

मुझे सचमुच नहीं पता। Astro mostly-static content sites के लिए दिलचस्प है, और मैं इसे एक छोटी project पर try कर रहा हूँ। लेकिन Next.js ने हमें flexibility दी कि एक ही codebase में static और dynamic दोनों sections हो सकें, जो मायने रखता था। किसी purely static directory के लिए जिसमें कोई interactive features न हों, Astro build करने में तेजी और चलाने में कम खर्च दे सकता है। एक साल बाद फिर से पूछना।

आप scrapers को पूरी directory copy करने से कैसे रोकते हैं?

सच कहूँ तो? आप पूरी तरह नहीं रोक सकते। हम API routes को rate-limit करते हैं, Cloudflare के bot management का frontend पर use करते हैं, और structured data को rotate करते हैं ताकि scraped copies जल्दी stale हो जाएँ। लेकिन अगर कोई public-facing directory को clone करना चाहे, तो वह कोई न कोई रास्ता ढूँढ ही लेगा। Moat data freshness और UX quality की है, technical obfuscation की नहीं।

---

समापन विचार

HostList कोई रनअवे सक्सेस नहीं है। यह पैसा बनाता है, एफिलिएट कमीशन्स, कुछ डायरेक्ट एडवर्टाइजिंग डील्स, और यह रीजनेबली वेल रैंक करता है शायद 600 टर्म्स के लिए जो मैंने ओरिजिनली टार्गेट किए थे। वह ठीक है। यह एक लर्निंग प्रोजेक्ट था जो होता है रेवेन्यू भी जेनरेट करता है, जो सबसे अच्छी किस्म है।

अगर आप Next.js पर एक बड़े पैमाने की programmatic SEO साइट बनाने के बारे में सोच रहे हैं, तो मेरी ईमानदारी से सलाह यह है: करो। यह job के लिए genuinely एक अच्छा stack है। लेकिन उतना कम बनाओ जितना तुम्हें लगता है कि तुम्हें ज़रूरत है, इसे उतना बेहतर बनाओ जितना तुम्हें लगता है कि तुम्हारे पास समय है, और एक भी page template लिखने से पहले अपनी data architecture को सही कर लो।

Tech सबसे आसान हिस्सा है। हमेशा ही होता है।

← वापस