2022 के आखिर में कहीं मैंने खुद को समझा लिया कि एक वेब होस्टिंग डायरेक्टरी बनाना सीधा-सादा होगा। डेटा जमा करो, पेज बनाओ, रैंक करो, पैसा बनाओ। साफ़। मैंने इससे पहले प्रोग्रामेटिक SEO काम किए हैं -- एक UK क्लाइंट के लिए लोकलाइज़्ड रीयल एस्टेट टूल, एक SaaS कंपेरिज़न साइट जो 40k मासिक विज़िट पर पहुँची थी -- तो मुझे लगा कि HostList छः हफ़्तों का प्रोजेक्ट होगा। सात महीने के करीब लगा। और इसने कुछ चीज़ों को लगभग तोड़ दिया: मेरी नींद की दिनचर्या, एक जूनियर डेवलपर का आत्मविश्वास, और एक £180/माह की Vercel बिल जिसके लिए मैंने बजट नहीं रखा था।Vercel bill I hadn't budgeted for.
यह पोस्ट-मॉर्टम है। असली वाला, LinkedIn वाला नहीं।
---
ब्रीफ जो मैंने खुद के लिए लिखा
HostList सीधा-सादा होने वाला था। एक वेब होस्टिंग प्रोवाइडर्स की डायरेक्टरी -- शेयर्ड, VPS, डेडिकेटेड, मैनेज्ड WordPress -- हर प्रोवाइडर के लिए अलग पेज, कंपेरिज़न पेज, कैटेगरी पेज, और लोकेशन-बेस्ड पेज (जैसे "Germany में बेस्ट होस्टिंग")। गणित करो: ~400 प्रोवाइडर × कई पेज टाइप × 20+ फ़िल्टर कॉम्बिनेशन। आप 25,000 पेजों तक जितना सोचते हो उससे तेज़ी से पहुँच जाते हो।WordPress -- with individual pages for each provider, comparison pages, category pages, and location-based pages (e.g. "best hosting in Germany"). Run the maths: ~400 providers × several page types × 20+ filter combinations. You get to 25,000 pages faster than you'd think.
मैंने Next.js को लगभग सोचे-समझे बिना ही चुन लिया। हम Seahawk में इसका इस्तेमाल अपनी ज्यादातर बड़ी React-आधारित बिल्ड के लिए करते हैं। इकोसिस्टम परिपक्व है, getStaticProps और getStaticPaths SEO-भारी स्टैटिक जनरेशन के लिए समझदारी रखते हैं, और मुझे व्यक्तिगत रूप से फाइल-आधारित राउटिंग को Remix या Gatsby से इस स्केल पर समझना आसान लगता है।Next.js almost without thinking about it. We use it at Seahawk for most of our bigger React-based builds. The ecosystem is mature,getStaticProps and getStaticPaths make sense for SEO-heavy static generation, and I personally find the file-based routing easier to reason about than Remix or Gatsby at this scale.
पहला असली फ़ैसला डेटा लेयर का था। मैंने हेडलेस CMS को जल्दी ही खारिज कर दिया -- मैं 25,000 एंट्रीज़ के लिए Contentful दरें नहीं देना चाहता था, और मुझे CMS पर भरोसा नहीं था कि वह बल्क प्रोग्रामेटिक राइट को साफ़-सुथरे ढंग से संभालेगा। हम Supabase पर एक Postgres डेटाबेस पर पहुँचे, जिसके सामने एक हल्के-फुल्के Next.js API लेयर थे। वह हिस्सा असल में ठीक काम किया। लगभग सब कुछ और ही जटिल हो गया।Supabase, with a lightweight Next.js API layer sitting in front of it. That part actually worked fine. It's almost everything else that got complicated.
---
Static Generation at Scale: What Nobody Warns You About
25,000 राउट्स के साथ getStaticPaths के बारे में यह बात है। यह काम करता है। तकनीकी रूप से। लेकिन आपका बिल्ड टाइम आपको अपनी जीवन के चुनावों पर सवाल उठाने पर मजबूर करेगा।getStaticPaths with 25,000 routes. It works. Technically. But your build times will make you question your life choices.
हमारे पहले पूरे build में 4 घंटे 47 मिनट लगे। Vercel पर। जो, अगर आप अपनी plan limits के साथ सावधानी नहीं रखते, तो यह वह तरह की चीज है जो रात 2 बजे एक billing notification का कारण बनती है। मैंने अपने फोन से उस Slack alert को देखा और सच में WordPress इस्तेमाल करने पर विचार किया।
`fallback: 'blocking'` Trap
मेरी शुरुआती सहज समझ सब कुछ पहले से रेंडर करने की थी। हर पेज, हर कॉम्बिनेशन। ख़राब आइडिया -- और उस वजह से नहीं जिसके बारे में ज़्यादातर ट्यूटोरियल आपको सचेत करते हैं (जो आमतौर पर सिर्फ़ "इसमें समय लगता है")। असली समस्या कैश इन्वेलिडेशन है। जब कोई होस्टिंग प्रोवाइडर अपनी कीमतें अपडेट करता है (और वे करते हैं, लगातार), आपको प्रभावित पेजों को फिर से बनाना होता है। अगर सब कुछ स्टैटिकली प्री-रेंडर है और ISR नहीं है, तो आप डेटा परिवर्तनों के लिए पूरे रीबिल्ड ट्रिगर कर रहे हैं जो शायद 25,000 में से 30 पेजों को असर करते हैं।cache invalidation. When a hosting provider updates their pricing (and they do, constantly), you need to rebuild affected pages. If everything is statically pre-rendered with no ISR, you're triggering full rebuilds for data changes that affect maybe 30 pages out of 25,000.
मैं Incremental Static Regeneration पर चला गया, ज्यादातर पेजों के लिए 86400 सेकंड (24 घंटे) का revalidate और कीमत-भारी प्रदाता पेजों के लिए 3600 सेकंड। यह पूरे प्रोजेक्ट में सबसे बड़ा क्वालिटी-ऑफ-लाइफ सुधार था। बिल्ड टाइम 40 मिनट से भी कम हो गया क्योंकि हम सिर्फ ट्रैफिक प्राथमिकता के आधार पर शीर्ष ~2,000 पेजों को पहले से रेंडर कर रहे थे और बाकी को fallback: 'blocking' के साथ ऑन-डिमांड जनरेट होने देते थे।Incremental Static Regeneration with a revalidate of 86400 seconds (24 hours) for most pages, and 3600 seconds for pricing-heavy provider pages. This was the single biggest quality-of-life improvement in the entire project. Build times dropped to under 40 minutes because we were only pre-rendering the top ~2,000 pages by traffic priority and letting the rest generate on-demand with fallback: 'blocking'.
Splitting the Route Tree
एक चीज जो मैं अलग तरीके से करूंगा, और जो मैं अभी हर डेव को Seahawk में बताता हूं जो किसी बड़े प्रोग्रामेटिक प्रोजेक्ट पर काम करता है: जल्दी अपनी राउट ट्री को स्प्लिट करो। एक monolithic getStaticPaths फंक्शन न रखो जो 25,000 slugs रिटर्न करने की कोशिश कर रहा हो। हमने अपने को इनमें तोड़ा:getStaticPaths function trying to return 25,000 slugs. We broke ours into:
/providers/[slug] -- अलग-अलग प्रोवाइडर पेज (~400)-- individual provider pages (~400)/compare/[slugA]-vs-[slugB] -- आमने-सामने कंपेरिज़न पेज (~8,000)-- head-to-head comparison pages (~8,000)/category/[type] -- कैटेगरी लैंडिंग पेज (~40)-- category landing pages (~40)/location/[country]/[type] -- जियो × कैटेगरी कॉम्बिनेशन (~16,000+)-- geo × category combinations (~16,000+)/best/[use-case] -- क्यूरेटेड लिस्ट पेज (~600)-- curated list pages (~600)
हर route group का अपना revalidation cadence है, अपना data-fetching logic है, और यह काफ़ी महत्वपूर्ण है, अपनी build priority है। location pages लगभग पूरी तरह on-demand हैं। provider pages हमेशा pre-rendered होते हैं। साफ़-सुथरा अलगाव।
---
The Data Pipeline Mess (And How We Fixed It)
2023 की शुरुआत में मैंने HostList के data collection side को बहुत loosely build करने की गलती की। हमारे पास एक scraping script था (Python में लिखा, BeautifulSoup और Webshare से rotating proxy pool का इस्तेमाल करते हुए), सुधार के लिए एक manual Google Sheet, और एक Supabase table। सत्य के तीन स्रोत। कोई भी एक-दूसरे से सही तरीके से बात नहीं कर रहा था।
एक junior dev -- अच्छा लड़का, bootcamp से निकला ही था -- ने तीन हफ्ते Sheet और Supabase के बीच एक sync script को maintain करते हुए बिताए जो हर बार column name बदलने पर टूट जाता था। मुझे पहले हफ्ते ही Sheet को खत्म कर देना चाहिए था और एक सही internal admin UI बना देना चाहिए था। आखिरकार हमने किया, Next.js API routes और एक Retool dashboard का इस्तेमाल करके, लेकिन हमने शायद 60 engineering घंटे बर्बाद कर दिए वहां तक पहुंचने में।
समाधान: एक ही सत्य का स्रोत, हमेशा। डेटाबेस आधिकारिक है। हर चीज़ डेटाबेस में लिखी जाती है। एडमिन यूआई डेटाबेस से पढ़ता और लिखता है। स्क्रेपर डेटाबेस में लिखता है। यह स्पष्ट लगता है। पीछे मुड़कर देखने पर हमेशा ऐसा ही लगता है।one source of truth, always. The database is canonical. Everything writes to the database. The admin UI reads from and writes to the database. The scraper writes to the database. Sounds obvious. It always does, in hindsight.
डेटा को बड़े पैमाने पर ताज़ा रखना
इसी साइज़ की डायरेक्टरी के लिए, डेटा ताज़ापन SEO से उतना ही चिंता का विषय है जितना UX का। Google देखता है जब प्राइसिंग टेबल £2.99/month दिखाता है किसी ऐसी योजना के लिए जो आठ महीने से £5.99 की है। हमने सेट अप किया:
- Railway cron पर चलने वाली साप्ताहिक स्क्रेप जॉब (सस्ता, भरोसेमंद, डेडिकेटेड सर्वर की ज़रूरत नहीं)
- एक Supabase डेटाबेस webhook जो तब फायर होता है जब price_updated_at कॉलम बदलता है, एक Next.js revalidation एंडपॉइंट को हिट करता है
price_updated_atcolumn changes, hitting a Next.js revalidation endpoint - Retool में मैनुअल ओवरराइड फ़्लैग ~30 प्रोवाइडर्स के लिए जिनकी साइट्स सक्रिय रूप से स्क्रेपर्स को ब्लॉक करती हैं
वह revalidation endpoint -- /api/revalidate?secret=TOKEN&path=/providers/siteground -- एक standard Next.js feature है, लेकिन इसे database webhook से जोड़ने में कुछ plumbing की जरूरत पड़ी। हर मिनट की कोशिश काबिले-गौर थी।/api/revalidate?secret=TOKEN&path=/providers/siteground -- is a stock Next.js feature, but wiring it to a database webhook took a bit of plumbing. Worth every minute.
---
SEO आर्किटेक्चर: असली में क्या काम आया
मैंने पर्याप्त कंटेंट साइटें बनाई हैं यह जानने के लिए कि 25,000 पेज होना 25,000 पेज होने जैसा नहीं है जो रैंक करते हैं। तुलना पेज जाल थे। हमने अपने ~400 प्रदाताओं के लिए हर संभावित A-vs-B संयोजन तैयार किया, जिससे हमें लगभग 79,800 सैद्धांतिक जोड़े मिले। हमने उनमें से ~8,000 बनाए। और उनमें से अधिकांश, ईमानदारी से कहूं तो, पतले थे।
ईमानदारी से कहूं: मैं लालची हो गया। SEO logic सही था -- "SiteGround vs Bluehost" को real search volume मिलता है, comparison queries की long-tail बहुत बड़ी है -- लेकिन हमने हर एक page की मौजूदगी को justify करने के लिए काफी unique content नहीं बनाया। Google ने comparison section को crawl करना शुरू किया और साफ तौर पर फैसला कर लिया कि यह उसके लिए कीमती नहीं है। Google की अपनी guidance thin content के बारे में इस बारे में साफ है, और मुझे खुद से जल्दी ही और सीधा बात करनी चाहिए थी।Google's own guidance on thin content is blunt about this, and I should have been blunter with myself earlier.
हमने क्या किया रिकवरी के लिए
हमने छांट-बीन की। 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 time पर Supabase को query करता है (getStaticProps में) और category और location overlap के आधार पर पांच सबसे ज्यादा relevant adjacent pages return करता है। कोई editorial intervention जरूरी नहीं है। यह perfect नहीं है -- कभी-कभी एक VPS hosting page कुछ थोड़ा-बहुत अलग से link करता है -- लेकिन यह 90% सही है, और इसका मतलब है कि हर page के पास दिन पहले से contextually relevant internal links थे।getStaticProps) and returns the five most relevant adjacent pages based on category and location overlap. No editorial intervention needed. It's not perfect -- occasionally a VPS hosting page links to something a bit sideways -- but it's 90% right, and it meant every page had contextually relevant internal links from day one.
---
परफॉर्मेंस: वह हिस्सा जो आपको विनम्र करता है
आप सोचते होंगे कि static generation performance को आसान बनाता है। और conceptual level पर, यह करता है -- pre-rendered HTML, Vercel के CDN पर edge-cached, कोई server-rendering overhead नहीं। लेकिन 25,000 pages का मतलब 25,000 मौके हैं कि आपने अपने component tree के बारे में एक गलत फैसला लिया हो।
हमारी सबसे बड़ी performance की समस्या provider comparison table थी। यह एक heavy client-side React component था -- बहुत सारा state, बहुत सारा conditional rendering, provider pages और comparison pages दोनों पर इस्तेमाल होता है। Mobile पर, यह एक Largest Contentful Paint 4.8 सेकंड के आसपास पैदा कर रहा था। बुरा। एक site के लिए सचमुच खराब जहां primary traffic खरीद के फैसले पर लोग हों।Largest Contentful Paint of around 4.8 seconds. Bad. Really bad for a site where the primary traffic is people mid-decision on a purchase.
हमने इसे एक server-rendered static table के रूप में फिर से बनाया interactive filter bits के लिए एक thin React hydration layer के साथ। LCP 1.9 सेकंड में गिरा। यह magic नहीं है -- बस सही तरीके से boring चीज करना है।
इमेज समस्या
हर provider के पास एक logo है। 400 logos, plus screenshots, UI previews, feature icons। हमने पहले दो महीने इन्हें Vercel के built-in image optimisation पर host करने की गलती की। Bandwidth costs quietly horrifying थी। सब कुछ Cloudflare R2 को एक custom domain के साथ move किया, हमारा Vercel bill £180/month से £40/month तक गिरा। अगर आप कुछ image-heavy बना रहे हैं, तो Cloudflare R2 को जल्दी देखें -- free egress genuinely useful है scale पर।Cloudflare R2 early -- the free egress is genuinely useful at scale.
---
बिल्ड पाइपलाइन असली में अब कैसी दिखती है
किसी के लिए भी जो ठोस तस्वीर चाहता है:
- Data collection -- Python scraper एक Railway cron job पर, Supabase Postgres को write करता है -- Python scraper on a Railway cron job, writes to Supabase Postgres
- Admin layer -- Retool dashboard manual edits, corrections, और provider flags के लिए -- Retool dashboard for manual edits, corrections, and provider flags
- Next.js app -- Pages router (हमने App Router के काफी stable होने से पहले शुरू किया था), Vercel पर deployed -- Pages router (we started before App Router was stable enough to trust), deployed on Vercel
- ISR + on-demand revalidation -- top ~2,000 pages pre-built, बाकी on-demand, सभी 24h revalidation के साथ -- top ~2,000 pages pre-built, rest on-demand, all with 24h revalidation
- Images -- Cloudflare R2, custom subdomain से serve किए गए, Cloudflare CDN के साथ आगे -- Cloudflare R2, served via a custom subdomain with Cloudflare CDN in front
- Analytics -- Plausible privacy-friendly traffic data के लिए, Ahrefs ranking tracking के लिए -- Plausible for privacy-friendly traffic data, Ahrefs for ranking tracking
- Uptime monitoring -- BetterUptime five सबसे ज्यादा traffic-heavy page types को देख रहा है -- BetterUptime watching the five most traffic-heavy page types
यह glamorous नहीं है। यह maintain करने के लिए ज्यादातर बोरिंग भी है, जो कि बिल्कुल वही है जो आप चाहते हैं infrastructure से जिसे आप तीन साल तक चलाने वाले हैं।
---
ईमानदारी से की गई गलतियाँ, क्रमांकित
- बहुत व्यापक शुरुआत की। 25,000 पेज हमेशा लक्ष्य था, लेकिन मुझे 500 हाई-क्वॉलिटी पेज के साथ लॉन्च करना चाहिए था और विस्तार करना चाहिए था। इसके बजाय मैंने सब कुछ के साथ लॉन्च किया और पहले चार महीनों के लिए Google क्रॉल बजट की समस्या हुई।25,000 pages was always the goal, but I should have launched with 500 high-quality pages and expanded. Instead I launched with everything and had a Google crawl budget problem for the first four months.
- दिन एक से रीवेलिडेशन ठीक से सेटअप नहीं किया। हमने दो महीने पूरी रिबिल्ड्स पर बर्बाद किए जो ISR ने अनावश्यक बना दिया होता।We wasted two months on full rebuilds that ISR would have made unnecessary.
- Google शीट रखी। सिंगल सोर्स ऑफ ट्रुथ पहले सप्ताह से ही नॉन-नेगोशिएबल होना चाहिए था।Single source of truth should have been non-negotiable from week one.
- कम्पेरिजन पेज क्वॉलिटी को कम आंका। वॉल्यूम एक स्ट्रेटेजी नहीं है।Volume is not a strategy.
- Vercel के image optimisation का इस्तेमाल बहुत लंबे समय तक किया। R2 पर छह हफ्ते बाद शिफ्ट किया, जितना पहले करना चाहिए था।Moved to R2 six weeks later than we should have.
- रूट ट्री को काफी जल्दी स्प्लिट नहीं किया। तेज़ और धीमे रूट को एक ही getStaticPaths कॉल में मिक्स किया और फिर आश्चर्य हुआ कि बिल्ड्स धीमे क्यों हैं।Mixed fast and slow routes in the same
getStaticPathscall and then wondered why builds were slow.
ये सभी फैसले उस समय reasonable लगते थे। यही वह हिस्सा है जो tutorials में नहीं आता -- खराब architectural decisions आमतौर पर अच्छी सुनने वाली justifications रखते हैं जब आप उन्हें बनाते हैं।
---
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 करते हैं?
हमारे पास database में एक status flag है -- active, deprecated, redirected. Deprecated providers को एक slim archive page मिलता है, पूरी तरह के removal की जगह, जो किसी भी backlinks को preserve करता है। Redirected providers (उदाहरण के लिए जब एक host दूसरे को acquire करता है) को एक 301 मिलता है जो Next.js redirects config में next.config.js के through handle किया जाता है। हम monthly status flags की समीक्षा करते हैं।status flag in the database -- active,deprecated,redirected. Deprecated providers get a slim archive page rather than a full removal, which preserves any backlinks. Redirected providers (e.g. when one host acquires another) get a 301 handled via the Next.js redirects config in next.config.js. We review the status flags monthly.
अगर आप यह फिर से करते तो Next.js की जगह क्या use करते?
मुझे सचमुच नहीं पता। Astro mostly-static content sites के लिए दिलचस्प है, और मैं इसे एक छोटी project पर try कर रहा हूँ। लेकिन Next.js ने हमें flexibility दी कि एक ही codebase में static और dynamic दोनों sections हो सकें, जो मायने रखता था। किसी purely static directory के लिए जिसमें कोई interactive features न हों, Astro build करने में तेजी और चलाने में कम खर्च दे सकता है। एक साल बाद फिर से पूछना।Astro is interesting for mostly-static content sites, and I've been playing with it on a smaller project. But Next.js gave us the flexibility to have both static and dynamic sections in the same codebase, which mattered. For a purely static directory with no interactive features, Astro might be faster to build and cheaper to run. Ask me again in a year.
आप 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 कोई runaway success नहीं है। यह पैसे बनाता है -- affiliate commissions, कुछ direct advertising deals -- और यह शायद 600 terms में से reasonably अच्छी तरह rank करता है जो मैंने originally target किए थे। वह ठीक है। यह एक learning project था जो revenue भी generate करता है, जो सबसे अच्छी तरह का है।
अगर आप Next.js पर एक बड़े पैमाने की programmatic SEO साइट बनाने के बारे में सोच रहे हैं, तो मेरी ईमानदारी से सलाह यह है: करो। यह job के लिए genuinely एक अच्छा stack है। लेकिन उतना कम बनाओ जितना तुम्हें लगता है कि तुम्हें ज़रूरत है, इसे उतना बेहतर बनाओ जितना तुम्हें लगता है कि तुम्हारे पास समय है, और एक भी page template लिखने से पहले अपनी data architecture को सही कर लो।
Tech सबसे आसान हिस्सा है। हमेशा ही होता है।
