Netlify Functions में कैप्चा कैसे हल करें (Node.js SDK)

netlify functions captcha - How to Solve CAPTCHAs in Netlify Functions (Node.js SDK)

Netlify Functions में कैप्चा solve उस सिंक्रोनस function में नहीं चल सकता जो रिक्वेस्ट का जवाब देता है। Netlify उस function को 60 सेकंड पर रोक देता है और यह सीमा बढ़ाने नहीं देता, जबकि reCAPTCHA solve में कई मिनट लग सकते हैं। इसके बजाय बैकग्राउंड function में हल करें, जिसे 15 मिनट मिलते हैं, फॉर्म भी उसी से पोस्ट करवाएँ, और नतीजा Netlify Blobs में दर्ज करें ताकि दूसरा function उसकी रिपोर्ट दे सके। CapSkip Node.js SDK को Server मोड में चल रहे अपने सॉल्वर से जोड़ें, क्योंकि Netlify function के अंदर 127.0.0.1 Netlify की मशीन है, आपकी नहीं। फिर retries को सँभालें: जो बैकग्राउंड function error throw करता है वह दोबारा चलता है, और लापरवाही से लिखा function फॉर्म दो बार पोस्ट कर देता है।

आपको क्या चाहिए

  • आपके नियंत्रण वाली किसी Windows मशीन पर Server मोड में चलता हुआ CapSkip, जिसके पास स्टैटिक पब्लिक IP हो और जिसका सॉल्वर पोर्ट इंटरनेट से पहुँचा जा सके। Server मोड कनेक्शन सेटिंग्स में मिलता है, और बाकी सब स्टेप 1 में बताया गया है।
  • एक Netlify साइट, जिसके functions netlify/functions में हों और जो Node.js 22.12 या उसके बाद के वर्ज़न से build होती हो, क्योंकि @netlify/blobs को यही वर्ज़न चाहिए। Netlify functions को उसी Node.js वर्ज़न पर चलाता है जो आपका build इस्तेमाल करता है। बैकग्राउंड functions हर credit-based प्लान पर मिलते हैं, Free प्लान समेत; legacy प्लान में यह अलग-अलग है, इसलिए अपना प्लान जाँच लें।
  • capskip पैकेज, वर्ज़न 1.3.0 या उसके बाद का, साथ में job रिकॉर्ड के लिए @netlify/blobs और पेज पढ़ने के लिए cheerio। function के अंदर Blobs को किसी सेटअप की ज़रूरत नहीं: Netlify साइट और token आपके लिए खुद भर देता है।
  • उस पेज का URL जिस पर कैप्चा है। सैंपल reCAPTCHA v2 हल करते हैं, और यही ढाँचा हर उस टाइप के लिए काम करता है जिसे CapSkip सँभालता है।
# npm install capskip @netlify/blobs cheerio
npm install capskip @netlify/blobs cheerio

सिंक्रोनस function यह क्यों नहीं कर सकता

Netlify हर तरह के function को एक तय एक्ज़ीक्यूशन लिमिट देता है, जिसकी सूची उसके functions configuration डॉक्स में है। तीनों में से कोई भी लिमिट बदली नहीं जा सकती:

function का प्रकारएक्ज़ीक्यूशन लिमिटइस सेटअप में इसका काम
सिंक्रोनस60 सेकंडjob का नतीजा बताना
शेड्यूल्ड30 सेकंडटाइमर पर job शुरू करना
बैकग्राउंड15 मिनटहल करना, फिर फॉर्म पोस्ट करना

अब इसकी तुलना Netlify Functions में कैप्चा solve से करें। SDK reCAPTCHA के जवाब के लिए recaptchaTimeout तक इंतज़ार करता है, जो डिफ़ॉल्ट रूप से 300 सेकंड है, और CapSkip खुद किसी task को खाली thread के लिए 250 सेकंड इंतज़ार करने देता है और फिर हल करने में 250 सेकंड और लगाने देता है। जो solve किसी शांत दोपहर में 20 सेकंड लेता है, वही 90 सेकंड लेता है जब हर thread व्यस्त हो या कोई प्रॉक्सी धीमी हो। जब कोई सिंक्रोनस function 60 सेकंड पर पहुँचता है, तो Netlify उसे खत्म कर देता है। CapSkip को यह पता नहीं होता, इसलिए वह job को आख़िर तक पूरा करता है और जवाब लेने कोई कभी नहीं आता।

context.waitUntil बच निकलने का रास्ता लगता है, क्योंकि यह response जाने के बाद भी function को चालू रखता है। पर ऐसा है नहीं। Netlify के डॉक्स कहते हैं कि function फिर भी सिर्फ़ अपनी एक्ज़ीक्यूशन लिमिट तक ही चल सकता है, async काम समेत, इसलिए waitUntil को सौंपा गया solve भी ठीक उसी तरह 60 सेकंड पर दम तोड़ देता है। Streaming responses पर भी यही 60 सेकंड की सीमा लागू होती है।

बैकग्राउंड function caller को तुरंत 202 से जवाब देता है और 15 मिनट तक चलता रहता है। इसकी कीमत उसी 202 में छिपी है: function की return value किसी को नहीं मिलती। और reCAPTCHA token जारी होने के करीब दो मिनट बाद expire हो जाता है, इसलिए वह किसी caller के आकर उसे ले जाने का इंतज़ार नहीं कर सकता। बैकग्राउंड function को token खुद इस्तेमाल करना होगा, फॉर्म पोस्ट करके, और जो हुआ उसका रिकॉर्ड छोड़ना होगा।

स्टेप 1: SDK को Server मोड में चल रहे अपने सॉल्वर से जोड़ें

Netlify function के अंदर 127.0.0.1 उस function का अपना sandbox है। डिफ़ॉल्ट सेटिंग्स से बना क्लाइंट वहाँ किसी से नहीं जुड़ पाता और ECONNREFUSED के साथ NetworkException throw करता है। CapSkip को Server मोड पर करें ताकि वह आपके पब्लिक IP पर सुने, अगर Windows मशीन किसी राउटर के पीछे है तो पोर्ट उस तक forward करें, और Windows Firewall में उस पोर्ट को अनुमति दें।

फिर तय करें कि कौन जुड़ सकता है। डिफ़ॉल्ट रूप से Netlify functions जिन पतों से जुड़ते हैं, वे Netlify के scale होने के साथ बदलते रहते हैं, इसलिए कोई फ़ायरवॉल नियम उनकी सूची नहीं बना सकता। Netlify की Private Connectivity functions को IPs का एक तय सेट देती है जिसे आप अनुमति दे सकते हैं, लेकिन यह Enterprise प्लान के लिए एक add-on है। इसके बिना ताला CapSkip की API Key Validation सेटिंग है: इसे चालू करें, इस साइट के लिए एक key जोड़ें, और उसे apiKey के रूप में पास करें। हर SDK रिक्वेस्ट key साथ ले जाती है, बाकी कॉल की तरह सादे HTTP पर, इसलिए function को उसकी अपनी अलग key दें, जिसे आप बाकी कुछ तोड़े बिना हटा सकें।

पता और key को Netlify में एनवायरनमेंट वेरिएबल के रूप में रखें: Project configuration, फिर Environment variables में, ऐसे scope के साथ जिसमें Functions शामिल हो। यहाँ Netlify के दो नियम लोगों को उलझा देते हैं। netlify.toml में घोषित वेरिएबल functions तक कभी पहुँचते ही नहीं। और हर डिप्लॉय वही वैल्यू रखता है जो उसके build के समय सेट थीं, इसलिए नया CAPSKIP_HOST तब तक कुछ नहीं करता जब तक आप दोबारा डिप्लॉय न करें।

import { CapSkip } from "capskip";

// The SDK does not read these by itself, so pass them in.
if (!process.env.CAPSKIP_HOST) throw new Error("CAPSKIP_HOST is not set");
const solver = new CapSkip({
  apiKey: process.env.CAPSKIP_API_KEY,   // a key from API Key Validation
  host: process.env.CAPSKIP_HOST,        // your public IP, Server mode
  port: Number(process.env.CAPSKIP_PORT ?? 8080),
});

यह जाँच बनाए रखें। अगर वेरिएबल नहीं है, तो host undefined होता है, और SDK बिना कुछ कहे अपने डिफ़ॉल्ट 127.0.0.1 पर लौट जाता है, जिससे आप सीधे फिर ECONNREFUSED पर पहुँच जाते हैं। पूरे उदाहरण में यह जाँच solve के try block के अंदर है, इसलिए गायब वेरिएबल job रिकॉर्ड में दर्ज होता है, बजाय इसके कि वह रिकॉर्ड लिखे जाने से पहले ही function को रोक दे।

स्टेप 2: बैकग्राउंड function में हल करें और पोस्ट करें

function के config में background को true सेट करना ही बैकग्राउंड function बनाने के लिए काफ़ी है। यह function एक shared secret जाँचता है, क्योंकि इसका URL पब्लिक है और इस पर आने वाली हर रिक्वेस्ट आपके सॉल्वर से काम करवाती है। फिर यह पेज fetch करता है, sitekey पढ़ता है और हल करता है:

// netlify/functions/solve-signup.mjs
import { getStore } from "@netlify/blobs";
import * as cheerio from "cheerio";
// solver from Step 1; PAGE_URL is the page with the CAPTCHA.

export default async (req) => {
  if (req.headers.get("x-job-secret") !== process.env.JOB_SECRET) return;
  const { jobId, email } = await req.json();
  const jobs = getStore({ name: "captcha-jobs", consistency: "strong" });

  const page = await fetch(PAGE_URL);
  const $ = cheerio.load(await page.text());
  const result = await solver.recaptcha($(".g-recaptcha").attr("data-sitekey"), PAGE_URL);
  // Then claim the job and post the form, below.
};

export const config = { path: "/api/solve-signup", method: "POST", background: true };

reCAPTCHA का जवाब result.code में होता है। इसे तुरंत इस्तेमाल करें: दो मिनट की उम्र के साथ, जो रन हल करता है वही रन पोस्ट भी करता है।

अब retries की बात। जब कोई बैकग्राउंड function error पर खत्म होता है, तो Netlify उसे एक मिनट बाद फिर चलाता है, और अगर वह भी फेल हो, तो उसके दो मिनट बाद एक बार और। फॉर्म पोस्ट होने से पहले retry ठीक वही है जो आप चाहते हैं: नया पेज और नया solve। फॉर्म पोस्ट होने के बाद retry का मतलब है दूसरा signup। इसलिए function पोस्ट करने से पहले Blobs में job को claim करता है:

// Only one run can create this key, so only one run posts.
const claim = await jobs.setJSON(`${jobId}-posted`, { at: Date.now() }, { onlyIfNew: true });
if (!claim.modified) return;

const form = $("form").has(".g-recaptcha");
const body = new URLSearchParams(form.serializeArray().map((f) => [f.name, f.value]));
body.set("email", email);
body.set("g-recaptcha-response", result.code);
const cookie = page.headers.getSetCookie().map((c) => c.split(";")[0]).join("; ");

const res = await fetch(new URL(form.attr("action") || PAGE_URL, PAGE_URL), {
  method: "POST",
  headers: { cookie, referer: PAGE_URL },
  body,
});
await jobs.setJSON(jobId, { state: "done", status: res.status });

onlyIfNew के साथ write तभी सफल होता है जब key अभी मौजूद न हो, और modified बताता है कि यह key इसी रन ने बनाई या नहीं। एक ही job के लिए होड़ कर रहे दो रन, दोनों true नहीं पा सकते, इसलिए उनमें से सिर्फ़ एक ही पोस्ट करता है। function शुरू होने से पहले भी उस key की जाँच करता है, इसलिए पूरी हो चुकी job का rerun कोई solve खर्च किए बिना लौट आता है।

पोस्ट खुद पेज की cookies और फॉर्म के अपने फ़ील्ड भेजती है, hidden CSRF token समेत, और ज़्यादातर signup फॉर्म कैप्चा के अलावा यही जाँचते हैं। यह पेज को Referer के रूप में भी भेजती है, क्योंकि fetch कोई Referer नहीं भेजता और कुछ frameworks इसके बिना HTTPS फॉर्म पोस्ट मना कर देते हैं। claim ले लेने के बाद, error throw करने के बजाय हर नाकामी Blobs में दर्ज करें। retry वैसे भी claim पर रुक जाएगा, इसलिए throw करने से कुछ हासिल नहीं होता, और रिकॉर्ड ही वह इकलौती जगह है जहाँ आपका status endpoint error दिखा सकता है। नीचे का पूरा उदाहरण दोनों चरणों को इसी तरह सँभालता है।

स्टेप 3: jobs शुरू करें और उनके नतीजे पढ़ें

किसी भी server-side कोड से बैकग्राउंड function को पोस्ट करके job शुरू करें। job id caller खुद बनाता है, क्योंकि 202 में कोई बॉडी नहीं होती जो id वापस ला सके:

const jobId = crypto.randomUUID();
const start = await fetch("https://YOUR_SITE.netlify.app/api/solve-signup", {
  method: "POST",
  headers: { "x-job-secret": process.env.JOB_SECRET },
  body: JSON.stringify({ jobId, email: "YOUR_EMAIL" }),
});
console.log(start.status);   // 202: accepted, not solved yet

एक छोटा सिंक्रोनस function रिकॉर्ड की रिपोर्ट देता है। यह milliseconds में चलता है, 60 सेकंड की सीमा से बहुत नीचे:

// netlify/functions/job-status.mjs
import { getStore } from "@netlify/blobs";

export default async (req, context) => {
  const jobs = getStore({ name: "captcha-jobs", consistency: "strong" });
  const job = await jobs.get(context.params.id, { type: "json" });
  if (!job) return new Response("unknown or not started", { status: 404 });
  return Response.json(job);
};

export const config = { path: "/api/jobs/:id", method: "GET" };

हर कुछ सेकंड में इसे poll करें। जब तक कोई job पूरी नहीं होती या उसकी कोई कोशिश फेल नहीं होती, उसका कोई रिकॉर्ड नहीं होता, इसलिए 404 का मतलब है कि उसकी पहली कोशिश अब भी चल रही है, या secret मेल न खाने की वजह से वह कभी शुरू ही नहीं हुई। यहाँ की तरह strong consistency के साथ पढ़ें। Blobs डिफ़ॉल्ट रूप से eventually consistent है: नया रिकॉर्ड तुरंत दिख जाता है, लेकिन किसी अपडेट को हर edge location तक पहुँचने में 60 सेकंड तक लग सकते हैं, जो इतना लंबा है कि पूरी हो चुकी job अब भी retry करती हुई दिख सकती है।

job को टाइमर पर चलाने के लिए शेड्यूल्ड function इस्तेमाल करें, लेकिन उसकी सीमा का ध्यान रखें: 30 सेकंड, सिंक्रोनस वाली सीमा का आधा। उसे काम बैकग्राउंड function को सौंपने दें और एक सेकंड से काफ़ी कम में खत्म होने दें:

// netlify/functions/nightly-signup.mjs
export default async () => {
  const res = await fetch(`${process.env.URL}/api/solve-signup`, {
    method: "POST",
    headers: { "x-job-secret": process.env.JOB_SECRET },
    body: JSON.stringify({ jobId: crypto.randomUUID(), email: "YOUR_EMAIL" }),
  });
  console.log("queued:", res.status);   // 202 means accepted, not solved
};

export const config = { schedule: "@daily" };

URL उन read-only वेरिएबल में से एक है जो Netlify functions को runtime पर देता है: यह आपकी साइट का मुख्य पता है। शेड्यूल्ड functions सिर्फ़ पब्लिश हुए डिप्लॉय पर चलते हैं, Deploy Previews या branch डिप्लॉय पर नहीं।

पूरा चलने वाला उदाहरण

// npm install capskip @netlify/blobs cheerio
// netlify/functions/solve-signup.mjs
import { getStore } from "@netlify/blobs";
import * as cheerio from "cheerio";
import { CapSkip } from "capskip";

const PAGE_URL = "https://example.com/signup";

export default async (req) => {
  // Anyone can POST to this URL, so check a shared secret first.
  if (req.headers.get("x-job-secret") !== process.env.JOB_SECRET) return;
  const { jobId, email } = await req.json();
  const jobs = getStore({ name: "captcha-jobs", consistency: "strong" });
  if (await jobs.get(`${jobId}-posted`)) return;   // already posted once

  // Phase 1: fetch and solve. Throwing here is safe: Netlify runs
  // the function again after one minute, then two minutes later.
  let page, $, result;
  try {
    if (!process.env.CAPSKIP_HOST) throw new Error("CAPSKIP_HOST is not set");
    const solver = new CapSkip({
      apiKey: process.env.CAPSKIP_API_KEY,
      host: process.env.CAPSKIP_HOST,
      port: Number(process.env.CAPSKIP_PORT ?? 8080),
    });
    page = await fetch(PAGE_URL);
    $ = cheerio.load(await page.text());
    result = await solver.recaptcha($(".g-recaptcha").attr("data-sitekey"), PAGE_URL);
  } catch (err) {
    const prev = await jobs.get(jobId, { type: "json" });
    const attempt = (prev?.attempt ?? 0) + 1;
    // The first run plus two retries: after the third, nothing reruns.
    const state = attempt < 3 ? "retrying" : "failed";
    await jobs.setJSON(jobId, { state, attempt, error: String(err) });
    throw err;
  }

  // Phase 2: post the form once. Claim the job first, so a rerun
  // that reaches this line finds the claim taken and stops.
  const claim = await jobs.setJSON(`${jobId}-posted`, { at: Date.now() }, { onlyIfNew: true });
  if (!claim.modified) return;
  try {
    const form = $("form").has(".g-recaptcha");
    const body = new URLSearchParams(form.serializeArray().map((f) => [f.name, f.value]));
    body.set("email", email);
    body.set("g-recaptcha-response", result.code);
    const cookie = page.headers.getSetCookie().map((c) => c.split(";")[0]).join("; ");
    const res = await fetch(new URL(form.attr("action") || PAGE_URL, PAGE_URL), {
      method: "POST",
      headers: { cookie, referer: PAGE_URL },
      body,
    });
    await jobs.setJSON(jobId, { state: "done", status: res.status });
  } catch (err) {
    // No rethrow: a retry could not post again, so record it here.
    await jobs.setJSON(jobId, { state: "failed", error: String(err) });
  }
};

export const config = { path: "/api/solve-signup", method: "POST", background: true };

Netlify UI में JOB_SECRET, CAPSKIP_HOST, CAPSKIP_API_KEY और, अगर आपने बदला है, तो CAPSKIP_PORT सेट करें, डिप्लॉय करें, और स्टेप 3 की तरह job शुरू करें। reCAPTCHA कॉल जो भी ऑप्शन लेती है, invisible और Enterprise समेत, वे सब यहाँ बिना बदलाव के काम करते हैं; reCAPTCHA v2 सॉल्वर पेज बताता है कि इस टाइप को क्या चाहिए।

आम errors और उनका मतलब

आप जो देखते हैंकारणफिक्स
करीब एक मिनट बाद रिक्वेस्ट फेल हो जाती है और कोई फॉर्म पोस्ट नहीं होताsolve किसी सिंक्रोनस function में चलता है, या waitUntil के अंदर, जिस पर भी वही 60 सेकंड की सीमा लागू हैsolve को बैकग्राउंड function में ले जाएँ
job रिकॉर्ड में लिखा है कि CAPSKIP_HOST सेट नहीं है, या अगर आपने वह जाँच हटा दी है तो ECONNREFUSED 127.0.0.1:8080 के साथ NetworkExceptionडिप्लॉय हुए function में CAPSKIP_HOST नहीं है, इसलिए SDK loopback पर लौट जातावेरिएबल को ऐसे scope के साथ सेट करें जिसमें Functions शामिल हो, फिर दोबारा डिप्लॉय करें
netlify dev के साथ चलता है, डिप्लॉय होते ही फेल हो जाता हैलोकल रूप से function आपकी अपनी मशीन पर चलता है, जहाँ loopback और आपका लोकल IP सॉल्वर तक पहुँच जाते हैंडिप्लॉय हुई साइट के लिए Server मोड और अपना पब्लिक IP इस्तेमाल करें
netlify.toml का कोई वेरिएबल function में undefined हैnetlify.toml में घोषित वेरिएबल functions तक कभी नहीं पहुँचतेइसके बजाय उसे Netlify UI, CLI या API में सेट करें
function अब भी पुराना CAPSKIP_HOST इस्तेमाल करता हैडिप्लॉय वही वैल्यू रखता है जो उसके build के समय सेट थींवेरिएबल बदलने के बाद दोबारा डिप्लॉय करें
कनेक्शन अटक जाता है, फिर ETIMEDOUT के साथ NetworkExceptionपोर्ट forward नहीं किया गया है, या Windows Firewall उसे ड्रॉप कर देता हैपोर्ट forward करें, Windows Firewall में उसे अनुमति दें, और अपने नेटवर्क के बाहर से उसे टेस्ट करें
ERROR_KEY_DOES_NOT_EXIST वाला ApiExceptionAPI Key Validation चालू है और key CapSkip की सूची में नहीं है, या CAPSKIP_API_KEY सेट नहीं है और SDK ने अपनी डिफ़ॉल्ट key भेज दीCapSkip में key जोड़ें और वेरिएबल सेट करें
फॉर्म दो बार पोस्ट हुआपोस्ट के बाद आए error से retry शुरू हुआ, और दूसरे रन को किसी ने नहीं रोकास्टेप 2 की तरह, पोस्ट करने से पहले onlyIfNew से job को claim करें
status endpoint हमेशा 404 ही लौटाता हैsecret मेल नहीं खाया, इसलिए function कुछ भी लिखने से पहले ही लौट गयाcaller और साइट, दोनों में एक ही JOB_SECRET सेट करें
साइट टोकन ठुकरा देती हैtoken करीब दो मिनट से पुराना था, या पहले ही इस्तेमाल हो चुका थाहल करने के तुरंत बाद पोस्ट करें, हर पोस्ट के लिए एक token

FAQ

क्या मैं Netlify Functions में कैप्चा solve के लिए 60 सेकंड की सीमा बढ़ा सकता हूँ?

नहीं। Netlify सिंक्रोनस, शेड्यूल्ड और बैकग्राउंड, तीनों सीमाओं को तय बताता है, और waitUntil व streaming responses भी 60 सेकंड के भीतर ही रहते हैं। उपलब्ध लंबी सीमा 15 मिनट वाली बैकग्राउंड सीमा है, और यह SDK के 300 सेकंड के इंतज़ार को आराम से कवर कर लेती है।

क्या Netlify function मेरे घर या ऑफ़िस के PC पर चल रहे CapSkip तक पहुँच सकता है?

हाँ, Server मोड के ज़रिए। CapSkip आपके पब्लिक IP पर सुनता है, आपका राउटर पोर्ट को उस PC तक forward करता है, और function उसी HTTP API से जुड़ता है जिसे वह लोकल रूप से इस्तेमाल करता। स्टैटिक पब्लिक IP से CAPSKIP_HOST डिप्लॉय के बीच भी सही बना रहता है। Private Connectivity के बिना आप Netlify को पते के आधार पर अनुमति नहीं दे सकते, इसलिए पहरेदारी API Key Validation करती है।

जब Netlify, CapSkip को कॉल करता है, तो क्या CapSkip हर solve का शुल्क लेता है?

नहीं। Server मोड सिर्फ़ यह बदलता है कि सॉल्वर तक कहाँ से पहुँचा जा सकता है, यह नहीं कि उसे कौन चलाता है: यह अब भी आपकी अपनी Windows मशीन है, और वहाँ solve गिने नहीं जाते। Netlify function के रन टाइम का मीटर चलाता है, इसलिए जो बैकग्राउंड function किसी solve के लिए दो मिनट इंतज़ार करता है, वह उसमें से दो मिनट खर्च करता है।

यह AWS Lambda पर चलाने से कैसे अलग है?

पाबंदियाँ अलग हैं। API Gateway के पीछे Lambda पर दीवार 29 सेकंड का integration timeout है, और Elastic IP वाला NAT gateway हर solve को आपके फ़ायरवॉल के लिए एक तय source पता देता है। Netlify पर दीवार 60 सेकंड है, उससे बचने का built-in रास्ता बैकग्राउंड function है, और Enterprise add-on के बिना कोई तय पता नहीं मिलता। Lambda वाला सेटअप यहाँ है: AWS Lambda कैप्चा गाइड.

संक्षेप में

Netlify Functions में कैप्चा job के लिए, कभी भी सिंक्रोनस function में हल न करें: उसकी 60 सेकंड की सीमा तय है, और waitUntil उससे बाहर नहीं निकलता। बैकग्राउंड function में हल करें, token ताज़ा रहते हुए उसी रन में फॉर्म पोस्ट करें, और पहले onlyIfNew write से job को claim करें, ताकि Netlify के दो retries कभी दो बार पोस्ट न कर सकें। नतीजों की रिपोर्ट Blobs से एक छोटे सिंक्रोनस function के ज़रिए दें। CapSkip से Server मोड में जुड़ें, पता और key ऐसे वेरिएबल में रखें जिनका scope Functions हो, और API Key Validation चालू रखें।

Netlify functions देता है, और हल करने का काम आपकी अपनी Windows मशीन पर ही रहता है। अपना खुद का कैप्चा सॉल्वर चलाने का पूरा मक़सद यही है: प्लेटफ़ॉर्म का मीटर मिनट गिनता है, और solve कोई नहीं गिनता।