CopyDisable

Wednesday, 11 November 2026

SEO with Claude Code using SearchFit SEO

 What is SearchFit SEO?

SearchFit SEO is a free plugin that brings SEO tools right into Claude Code. It is made by SearchFit.ai. Once you install it, you can check your website's SEO by typing a simple command.

The plugin's skills cover site audits, technical SEO, on-page fixes, broken links, internal linking, schema markup, content planning and keyword grouping. It can even check how your site shows up in AI tools like ChatGPT and Gemini.

The best part is that it works on both a live website and your source code. So you can find a problem and fix it in the same place.

SEO audits at $0 cost

You do not need to pay for any SEO tool to use SearchFit SEO. The plugin itself is free. If you already have a Claude subscription, you can run every command in this post without spending a single extra rupee or dollar.

Paid SEO tools often charge a monthly fee. With this setup, your Claude subscription is all you need. The only thing to keep in mind is that these commands count toward your plan's normal usage limits, just like any other Claude Code task. A full audit of a large website uses more of your limit than a quick single-page check.


Getting started

Setting up SearchFit SEO takes three short steps.

Step 1: Start Claude Code

Open your terminal, go to your project folder and start Claude Code:

claude

Step 2: Install the plugin

You can install it in one of two ways.

Option A: Inside Claude Code. Type this in the Claude Code session:

/plugin install searchfit-seo@claude-plugins-official

This does not install the plugin right away. It first opens the plugin details so you can see what it adds. Then you pick a scope: just for you, for everyone on the project, or only for you in this project.

Option B: From your regular command prompt. You can also install it without opening Claude Code:

claude plugin install searchfit-seo@claude-plugins-official

This installs the plugin for your user account by default.

The part after the @ sign, claude-plugins-official, is the name of Anthropic's official plugin marketplace. Claude Code adds it for you the first time you start an interactive session. On a brand new machine where you have never opened Claude Code, run claude plugin marketplace add anthropics/claude-plugins-official first.

Step 3: Reload the plugins

If Claude Code is already open, load the new plugin with:

/reload-plugins

You need this after installing from the command prompt (Option B). With Option A, Claude Code usually tells you if a reload is needed and often runs it for you. If you simply start a new Claude Code session, the plugin loads on its own.

To confirm it worked, type /searchfit-seo: and you should see the plugin's commands in the list.

1. Full site audit: /searchfit-seo:seo-audit

This is the command to start with. It runs a complete SEO audit on a live website or on a codebase.

How to use it

/searchfit-seo:seo-audit <url>                    # audit a live website
/searchfit-seo:seo-audit <path>                   # audit a codebase
/searchfit-seo:seo-audit                          # audit the current project folder
/searchfit-seo:seo-audit <url> as an HTML file    # same audit, saved as a report file

For example, to audit a website:

/searchfit-seo:seo-audit https://example.com/

The results show up right there in your terminal. 










If you want a report you can open in a browser or share with your team, add "as an HTML file" at the end:

/searchfit-seo:seo-audit https://example.com/ as an HTML file

This saves a standalone HTML report in your current folder.















What it checks

•       Crawlability: robots.txt, sitemaps, noindex tags, canonical tags and orphan pages

•       Head tags: title, meta description, Open Graph, Twitter Card and viewport

•       Headings: a single H1, a clean heading order, keywords and no empty headings

•       Images: alt text quality, modern formats, width and height, and lazy loading

•       Performance signals: render-blocking files and heavy client-side JavaScript

•       Structured data: whether JSON-LD is there, whether the type fits the page, and whether required properties are filled

•       Internal linking: broken links, thin pages and anchor text

•       Mobile and accessibility: responsive layout, tap target size, font size and color contrast

What you get back

You get a score out of 100. The findings are sorted into four groups: Critical, Warnings, Opportunities and Passing. Every finding points to the exact URL, or to the file and line number in your code.

2. Quick page check: /searchfit-seo:seo-check

This is the fast, single-page version of the audit. It checks one page against a fixed checklist in a few seconds. It looks at the title, meta tags, headings, images and structured data.

How to use it

/searchfit-seo:seo-check <url>                    # check one live page
/searchfit-seo:seo-check <file>                   # check a local HTML, Vue or page component file
/searchfit-seo:seo-check                          # check the file open in your editor
/searchfit-seo:seo-check <url> as an HTML file    # save the result as a report

What it checks

•       Title: it exists, is 50 to 60 characters long and has the keyword

•       Meta description: it exists, is 150 to 160 characters long, and has the keyword and a call to action

•       Headings: exactly one H1, no skipped levels, and keywords in the H1 and H2s

•       Images: alt text on every image, descriptive alt text and modern formats

•       Structured data: JSON-LD is present, the type matches the content and required fields are filled

•       Links: internal links are present, anchors are descriptive and there are no broken link patterns

•       Other tags: canonical, Open Graph and Twitter Card

What you get back

You get a score out of 100 and one line for each check, marked Pass, Warn or Fail. After that comes a short list of fixes, with the most important ones first. Keep in mind that this is a quick pass-or-fail scan. It will not explain why something matters or rewrite your content for you.

3. Optimize a page for a keyword: /searchfit-seo:on-page-seo

This command improves a single page for one target keyword. Instead of just listing problems, it gives you ready-to-use fixes that you can copy and paste.

How to use it

/searchfit-seo:on-page-seo <url or file> [keyword]

The keyword is optional. If you leave it out, the command will ask you for it. It will also ask about the search intent and your target audience before it starts the analysis.

What it checks

•       Title tag: where the keyword appears, the length and whether it is unique

•       Meta description: the keyword, the length and a call to action

•       URL slug: short and descriptive, with no IDs or parameters

•       Headings: the keyword in the H1, H2s that cover the subtopics and a clean heading order

•       Content: depth, match with search intent, related keywords and no keyword stuffing

•       Images: alt text, file names, file size and the hero image

•       Internal links: at least 3 to 5 links with descriptive anchor text

•       Schema: the right JSON-LD type with all required properties

What you get back

You get a before-and-after report with your current score and the score you can expect after the fixes. Each fix is written out in full. That includes a new title, a new meta description, a suggested heading outline, content gaps to fill and ready-to-use internal links to add.

seo-audit, seo-check or on-page-seo: which one should you use?

These three commands sound alike, but each one has its own job.

Command

Use it when you want to

seo-audit

Check the whole site, including how well search engines can crawl it

seo-check

Quickly confirm one page after an edit, or find out which pages need work

on-page-seo

Get the title, description and headings of one page rewritten for a keyword

4. Find broken links: /searchfit-seo:broken-links

This command finds links that lead nowhere and tells you how to fix each one. It can crawl a live site or scan your code.

How to use it

/searchfit-seo:broken-links <url>     # crawl a live site, starting from this page
/searchfit-seo:broken-links <path>    # scan a codebase for dead links
/searchfit-seo:broken-links           # scan the current project folder

What it checks on a live site

•       Every internal and external link it finds while crawling from the start URL

•       The HTTP status of each link: 404 and 500 errors are broken, 410 means the page was removed on purpose, and timeouts are flagged

•       Redirect chains with three or more hops

•       Mismatches in letter case and trailing slashes

What it checks in a codebase

•       Anchor tags, framework link components, Markdown links and CSS url() references

•       Each internal link compared with the routes your app actually defines

•       Hardcoded absolute URLs, hash links that point to missing IDs, and links in comments or docs

What you get back

The report starts with a count of pages scanned, links checked, broken links and redirect chains. Broken links are listed in tables with the source page, the URL, the status and a suggested fix. At the end, a quick-fix section gives you redirect rules to add and the exact file-and-line edits to make.

5. Technical SEO audit: /searchfit-seo:technical-seo

This command looks at the technical side of SEO. It checks whether search engines can reach your site, crawl it, index it and load it quickly.

How to use it

/searchfit-seo:technical-seo <url>     # audit a live site
/searchfit-seo:technical-seo <path>    # audit a codebase's config and routing
/searchfit-seo:technical-seo           # audit the current project, or the site you worked on earlier in the session

What it checks

•       Crawlability: robots.txt rules, sitemap validity and coverage, lastmod dates, robots meta tags, X-Robots-Tag headers and canonical tags

•       Indexing: status codes, soft 404s, 301 versus 302 redirects, redirect chains, and duplicate URLs caused by www, trailing slashes or letter case

•       Performance: compression, caching headers, HTTP/2 or HTTP/3, time to first byte (TTFB), render-blocking CSS and JavaScript, font loading, image formats and sizes, and bundle size

•       Mobile: viewport, horizontal scrolling, tap target size, body font size and pop-ups that cover content

•       Security: HTTPS redirect, HSTS, CSP and other security headers, mixed content and exposed dotfiles

•       Other: structured data validity, hreflang tags where needed, and URL structure

What you get back

You get a score for each area plus an overall score. Every finding is linked to a URL or a config file. The fix list is sorted by priority and shows the exact change to make in your nginx config or your framework.

6. Check your AI visibility: /searchfit-seo:ai-visibility

More and more people now ask AI assistants like ChatGPT, Claude, Gemini and Perplexity for answers. This command checks how your brand shows up in those answers and gives you a plan to improve it. This practice is often called Generative Engine Optimization, or GEO.

How to use it

/searchfit-seo:ai-visibility <brand or url>

For example:

/searchfit-seo:ai-visibility Pranab Sharma
/searchfit-seo:ai-visibility https://pranabsharma.in/ "indian digital art assets"

If you give only a brand name or a URL, the command will ask a few more questions. It wants to know your category, your main competitors, what makes you different, and the prompts you want your brand to appear in.

What it does

1.      Builds a set of test prompts, such as "best source for Indian art vectors" or "alternatives to [competitor]".

2.      For each prompt, it checks whether your brand is mentioned, in what position, how accurately and in what tone.

3.      Scores your brand on six areas: presence, accuracy, sentiment, position, completeness and consistency across platforms.

4.      Compares you with your competitors and creates a priority action plan, along with a list of content to create.

What you get back

You get a visibility score out of 100, a prompt-by-prompt table and a comparison with your competitors. You also get an action plan in three parts:

•       Content signals: what to write and publish

•       Technical signals: such as schema markup and making your site crawlable for AI bots

•       Authority signals: such as mentions of your brand on other websites

One limitation to know

This command cannot query ChatGPT, Gemini or Perplexity directly from Claude Code. Instead, it uses web search to see how your brand is described in sources these AI tools learn from, along with Claude's own knowledge. For a true reading across platforms, run the prompts yourself in each assistant and paste the answers back in. 


Final thoughts

SearchFit SEO turns Claude Code into a handy SEO assistant. Because it works on both live sites and code, you can find a problem and fix it without leaving your terminal.

Wednesday, 26 August 2026

GateCHA Hands-On Guide

GateCHA is a self-hosted CAPTCHA management platform designed to block bot spam and automated form submissions on websites without annoying human visitors. It is self-hosted alternative to ALTCHA Sentinel, a paid cloud SaaS.

Unlike traditional CAPTCHA solutions that ask users to solve image puzzles or decipher distorted text, GateCHA leverages Proof-of-Work (PoW) computational challenges. When a user submits a form, their web browser silently solves a light mathematical puzzle in the background. For a real user, this process takes a split second and is completely invisible. For automated spam bots trying to submit thousands of forms at scale, these computational challenges quickly become too expensive to process, forcing them to stop the spam attacks.

Third-party CAPTCHA providers often track user IP addresses and browsing behavior for advertising profiles. GateCHA is self-hosted, meaning no data is sent to external third parties.

GateCHA integrates into a web application using a simple three-step architecture: 

1) GateCHA instance Setup, 

2) Web App's Frontend Integration, and 

3) Web App's Backend Verification.


In this hands-on demo, we are going to deploy GateCHA on a Ubuntu 24.04 server. I will provide simple code for a demo web-application, where we are going to have a simple HTML form as a client application, which going to capture user information and submit the data to a backend API. I will provide backend code for both Python and GoLang, but I will only show the backend deployment process for the Python API. 

GateCHA relies on an embedded SQLite database (MySQL also supported) and ships as a lightweight container, deploying it via Docker on Ubuntu 24.04 is the cleanest and most reliable method.

For this example we are going to use a VM with IP 10.2.11.116. In the code you will see this IP, you can replace this IP with your IP or domain name. 

Step 1: Installation 

I am running the below commands as root user:

Update package lists: apt update && apt install -y curl git jq build-essential

Install Docker and Docker Compose: 

curl -fsSL https://get.docker.com -o get-docker.sh

sh get-docker.sh



Python and GoLang is installed just for developing the API servers, not for running the GateCHA server. 

Install GoLang:

# wget https://go.dev/dl/go1.22.2.linux-amd64.tar.gz

# rm -rf /usr/local/go && tar -C /usr/local -xzf go1.22.2.linux-amd64.tar.gz

# echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.profile

# source ~/.profile



Install Python:

We are going to write the backend API using Python, so we are installing it.

# apt install python3-dev

# apt install software-properties-common

# apt install python3.12-venv


Install Nginx webserver:

Our client application (HTML form) will be served using Nginx webserver.  

# apt install nginx


Step 2: Setup GateCHA

The GateCHA docker image is fully self-contained, that means we don't need to spin up separate database.

Create a new directory for the project: 

# mkdir ~/gatecha 
# cd ~/gatecha

Create a docker-compose.yml file: 
# nano docker-compose.yml

Paste the following configuration into the file:

services:
  gatecha:
    image: ghcr.io/upellift99/gatecha:latest
    container_name: gatecha
    ports:
      - "9090:8080"
    volumes:
      - gatecha_data:/app/data
    environment:
      - GATECHA_ADMIN_PASSWORD=devpassword
volumes:
  gatecha_data:


We are going access the GateCHA dashboard using the port 9090. So the GateCHA dashboard URL will be http://10.2.11.116:9090/.
The default admin username is admin and we specified the password as devpassword (in docker-compose.yaml)

Save the file and start the container in the background: 

# docker compose up -d




Step 3: Create API key















Once the container is up, we can open the dashboard using a webbrowser.

After login we can see the dashboard.














Right now we do not have any API key for our web application. A GateCHA API key is a unique, site-specific security token. GateCHA authenticates form widgets and server-side verification requests using the API key.










We can create new API key by clicking the Create Key button. 





















Explanation of important fields for creating key:

  • Domains: To prevent someone from taking our API key and using it on their own website, GateCHA allows us to configure Domain Restrictions in the dashboard. Once we restrict the key to our production domain (e.g., myapp.com), the backend will reject any verification requests originating from unauthorized domains.
  • Difficulty: When we configure the Difficulty (or maxNumber) for a new API key in GateCHA, we are setting the maximum possible computational effort required by the user's browser to solve the proof-of-work challenge.
    Some use cases for setting this value:
    • Medium / Standard (Recommended)
      • Value: 100,000
      • Use Case: Standard user registrations, login pages, and general forms.
      • Experience: It takes less than 1 second on modern laptops and smartphones. It is enough to deter most cheap spam bots while keeping the experience completely frictionless for legitimate users.
    • Low / Fast
      • Value: 10,000 to 50,000
      •  Use Case: Low-risk applications or internal tools where user experience must be instant.
      •  Experience: Solves almost instantaneously on any device. It stops simple bots that don't execute JavaScript, but determined botnets might still brute-force it easily.
    • High / Strict
      •  Value: 500,000 to 1,000,000
      •  Use Case: High-abuse targets like password resets, payment gateways, or if our site is actively experiencing a bot attack.
      •  Experience: Highly secure. While a modern desktop might solve this in 1–3 seconds, an older budget smartphone might take 5–10 seconds. If a user clicks submit and the button spins for 10 seconds, user experience may suffer.
    • Best Practice: Start with 100,000. If we notice spam getting through, increase it to 250,000 or 500,000. We can change this value on the server at any time without having to touch our frontend HTML code.
  • HIS Sampling: HIS stands for Human Interaction Signature. It is an advanced feature in the ALTCHA (and GateCHA) protocol that acts as a behavioral biometrics collector to detect automated environments and bots.  Rather than just relying on the mathematical proof-of-work challenge, HIS analyzes how a user physically interacts with our webpage before they submit the form.


Once we click the Create Key button, the new API key is created:










We will use the Key ID in our client application and HMAC Secret will remain in the server.



Step 4: Integrating GateCHA

Now that we have installed GateCHA and created our API key, it’s time to integrate it into our web application.

GateCHA works silently in the background using Proof-of-Work (PoW). Let’s look at how the verification process works under the hood.


The 4-Step Verification Flow:

To understand how GateCHA protects our application, think of the verification process as a 4-step conversation between our webpage (Client application), our server (Backend application), and the GateCHA server:

  1. Requesting a Challenge: When a user visits our form with GateCHA widget, the GateCHA widget automatically calls our GateCHA server to request a math challenge.
  2. Solving the Puzzle: The user's web browser solves the puzzle in the background using JavaScript. This usually takes less than a second on modern devices.
  3. Submitting the Payload: Once solved, the widget appends a hidden input field containing the completed solution (called the altcha payload) into our form. When the user clicks Submit, this payload is sent to our application backend alongside their form data (e.g., name, email).
  4. Backend Verification: Before processing the submitted form data, our application backend sends this payload back to GateCHA to ask, "Is this math solution real, valid, and unexpired?" If GateCHA confirms with ok: true, our backend allows the user to proceed.


Client-Side Integration (Frontend)


Adding GateCHA to a HTML forms requires only two things: 
1) importing the official lightweight web component script and 
2) adding the <altcha-widget> HTML tag.

Step 1: Add the JavaScript Script

Add the following script tag inside the <head> of our HTML document.

    Note: Because ALTCHA uses ES Modules, ensure we include type="module".


<script type="module" async defer src="https://cdn.jsdelivr.net/npm/altcha/dist/altcha.min.js"></script>


Step 2: Add the widget to our form

Place the <altcha-widget> tag inside our HTML <form> element wherever we want the CAPTCHA checkbox to appear:

<form action="/submitdata" method="POST">
    <!-- User Input Fields -->
    <label for="email">Email Address:</label>
    <input type="email" id="email" name="email" required>
    <!-- GateCHA Widget -->
    <altcha-widget 
        challengeurl="http://10.2.11.116:9090/api/v1/challenge?apiKey=gk_96fccfb465545285bc3a3bc0"
        hidelogo
        hidefooter>
    </altcha-widget>

    <button type="submit">Submit</button>
</form>


Key Widget Options:
  • challengeurl: Points to the GateCHA server’s challenge endpoint, with the generated apiKey for the site.
  • hidelogo: (Optional) Removes the ALTCHA logo icon for a cleaner look.
  • hidefooter: (Optional) Hides the bottom footer text inside the widget.

My example implementation of client page:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Registration Form</title>
    <!-- Import the ALTCHA web component -->
    <script type="module" async defer src="altcha.min.js"></script>
    <style>
        body { font-family: sans-serif; padding: 20px; max-width: 400px; }
        .form-group { margin-bottom: 15px; }
        label { display: block; margin-bottom: 5px; }
        input[type="text"], input[type="email"], input[type="tel"] { width: 100%; padding: 8px; }
        button { padding: 10px 15px; background: #007bff; color: white; border: none; cursor: pointer; }
    </style>
</head>
<body>

    <h2>User Registration</h2>
   
    <!-- Form submits to our Python API -->
    <form action="http://10.2.11.116:9999/submitdata" method="POST">
        <div class="form-group">
            <label for="name">Name:</label>
            <input type="text" id="name" name="name" required>
        </div>
       
        <div class="form-group">
            <label for="email">Email:</label>
            <input type="email" id="email" name="email" required>
        </div>
       
        <div class="form-group">
            <label for="phone">Phone Number:</label>
            <input type="tel" id="phone" name="phone" required>
        </div>

        <!-- GateCHA/ALTCHA Widget -->
        <div class="form-group">
            <altcha-widget
                challengeurl="http://10.2.11.116:9090/api/v1/challenge?apiKey=gk_96fccfb465545285bc3a3bc0"
        hidelogo
                hidefooter>
            </altcha-widget>
        </div>

        <button type="submit">Register</button>
    </form>

</body>
</html>


Note: Web browsers require a Secure Context (HTTPS or localhost) to run the browser cryptography functions required by GateCHA. 
Otherwise we will get error like:

Uncaught (in promise) Error: Web Crypto is not available. Secure context is required (https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts).

Suppose we are testing from a different local machine on the network and cannot use localhost or HTTPS, we can temporarily tell our browser to treat the local IP as secure.

For Chrome we can do this as follows:
  • Open a new tab in Chrome or Edge and paste this into the URL bar:
    chrome://flags/#unsafely-treat-insecure-origin-as-secure
  • Enable the setting.
  • Add our local server's URL into the text box.
  • Click Relaunch at the bottom of the screen to restart the browser.
  • Reload the form page. The Web Crypto API will now work.











Server-Side Integration (Backend)

When the form is submitted, our backend receives all standard form fields plus the altcha hidden payload field.

To prevent malicious users from tampering with form submissions, our backend must verify this payload with GateCHA before trusting the user.

Backend Verification Logic

Regardless of what language our backend is written in (Python, Go, Node.js, PHP), the verification steps remain identical:
  1. Read the altcha field from the incoming request form data.
  2. If missing, return an error (400 Bad Request).
  3. Send a POST request to our GateCHA server at /api/v1/verify?apiKey=YOUR_API_KEY containing {"payload": "..."}.
  4. Inspect GateCHA's JSON response. If ok is true, accept the submission. Otherwise, reject it (403 Forbidden).

Example Implementation (Python / Flask)

from flask import Flask, request, jsonify
import requests

app = Flask(__name__)

# GateCHA Configuration
GATECHA_VERIFY_URL = "http://127.0.0.1:9090/api/v1/verify"
GATECHA_API_KEY = "gk_96fccfb465545285bc3a3bc0"

@app.route('/submitdata', methods=['POST'])
def submit_data():
    # 1. Extract standard form fields
    name = request.form.get('name')
    email = request.form.get('email')
    phone = request.form.get('phone')
   
    # 2. Extract the CAPTCHA payload
    altcha_payload = request.form.get('altcha')

    print("Payload: ", altcha_payload)

    if not altcha_payload:
        return "Error: CAPTCHA is missing or incomplete.", 400

    # 3. Verify the payload with our GateCHA server
    verify_url = f"{GATECHA_VERIFY_URL}?apiKey={GATECHA_API_KEY}"
   
    try:
        verify_response = requests.post(
            verify_url,
            json={"payload": altcha_payload},
            timeout=5
        )
       
        verify_result = verify_response.json()
        print(verify_result)
        # 4. Check if verification was successful
        if verify_response.status_code == 200 and verify_result.get("ok"):
            # CAPTCHA passed! Process the user registration here
            print(f"New Registration: {name}, {email}, {phone}")
            return f"Registration successful for {name}!"
        else:
            # CAPTCHA failed (expired, tampered, or reused)
            # Print the actual response from GateCHA to our server console for debugging
            print(f"GateCHA rejected payload. Status: {verify_response.status_code}, Response: {verify_result}")
            return "Error: CAPTCHA verification failed.", 403
           
    except Exception as e:
        print(f"GateCHA connection error: {e}")
        return "Internal Server Error during CAPTCHA validation.", 500

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=9999)



This is the example webpage:
























When we click on the "I'm not a robot" button, the altcha widget sends request to provide a math challange or puzzle.

















If we check the response we can the JSON object returned by the GateCHA server








{

    "algorithm": "SHA-256",

    "challenge": "ce66813a276e9c91f8cbd2e798e2f7f1b5d8092af43f21d770f8797be4ae0735",

    "maxNumber": 100000,

    "salt": "1caa882d1b86cf565bead696?expires=1787662905\u0026",

    "signature": "a7853808cb78de93f5218d78c040d8b59598a4e284cc2352fb43c4aab446b985"

}


The JSON block is a hidden math puzzle send to the browser by the GateCHA server.

Here is what each piece of the puzzle means:

  1. algorithm: "SHA-256": This tells our browser to use SHA-256 hashing function to solve the puzzle.
  2. maxNumber (The Guessing Limit): 100000: To solve the puzzle, our browser has to guess a secret number. This field tells the browser: "The secret number is somewhere between 0 and 100,000." The browser will rapidly count up and check each number until it finds the right one.
  3. salt (The Unique Puzzle Piece & Timer): "1caa88...expires=1787662905&": This is a random string of characters combined with an expiration timestamp. It guarantees that every single puzzle generated is unique and can't be solved ahead of time. Once the timestamp passes, the puzzle is useless.
  4. challenge (The Target Answer): "ce66813a...": This long string of characters is the final "target.". The browser's job is to take the salt, attach its guessed number to it, and run it through the algorithm (SHA-256). If the result matches this exact challenge string, the browser has found the correct number.
  5. signature (The Anti-Cheat Seal): "a7853808...": This is a digital seal created by the server. It prevents hackers from cheating.



How the system remains cryptographically secure

The API Key: It simply tells the GateCHA server which configuration (difficulty level, expiration time, etc.) to use to generate the math puzzle. To prevent someone from taking our site's API key and using it on their own website, GateCHA allows us to configure Domain Restrictions for an API key. Once we restrict the key to our production domain (e.g., myapp.com), the backend will reject any verification requests originating from unauthorized domains.


Puzzle Metadata: GateCHA relies on mathematical Proof-of-Work.To solve a math puzzle, our browser needs the instructions. 

The server generates a random secret number (let's call it n). It sends the browser the salt and the resulting final hash, which is the challenge.

The client-side JavaScript must run a loop to guess n using the following logic:

Hash(salt + guess) = challenge

The client guesses 0, 1, 2... up to the maxNumber until the hashes match. 

Because there is no shortcut in cryptography to reverse a hash, the bot is forced to spend time and CPU cycles doing the math.

Once found, it sends the solved number, along with the original challenge, salt, and signature, back to the backend server.


The Signature: If the attacker can see the instructions, what stops them from changing the maxNumber to 1 so the puzzle solves instantly?

The security relies entirely on the signature field. When the GateCHA server generates the puzzle, it signs the salt and challenge using a Private Secret Key that never leaves the server. This creates the signature. 

If a bot tampers with the maxNumber, modifies the salt, or alters the challenge, the signature will instantly become invalid. When our backend sends this tampered payload to the /verify endpoint of GateCHA, the GateCHA server will check the signature, realize it was forged, and return a 403 Forbidden.


How the Signature is Generated:

When the GateCHA server receives a request for a new challenge, it performs the following cryptographic operations:

  • Generate the Puzzle: The server creates a random salt (which includes the expiration timestamp) and selects a random secret number n between 0 and the maxNumber.
  • Compute the Challenge: The server hashes the salt and the secret number together to create the target challenge:
    challenge = SHA-256(salt + n)
  • Sign the Challenge: To prevent tampering, the server uses our private HMAC Secret (let's call it K) to sign the newly created challenge using a Hash-based Message Authentication Code (HMAC):
    signature = HMAC-SHA-256(K, challenge)

    The HMAC Secret generated during the API key creation is the private cryptographic key used to generate that exact signature field.
The server then packages the salt, challenge, maxNumber, and signature and sends them to the client. It discards the secret number n entirely.


GateCHA server verification: When our backend sends this data to GateCHA, GateCHA immediately takes the submitted challenge and re-runs it through the HMAC equation using its private HMAC Secret (K).

If an attacker tried to create their own easy challenge, or alter the salt to remove the expiration date, the signature GateCHA calculates will not match the signature the attacker sent. 

GateCHA immediately drops the request with a 403 Forbidden error without even looking at the math.

Only if the signature is perfectly valid GateCHA perfrom the accuracy check and verify the math:

SHA-256(submitted_salt + submitted_number) == submitted_challenge


Expiration and Replay Protection: In the JSON object returned by GateCHA server, we can see that there is a  timestamp inside the salt (expires=1787560709).

  • Time-to-Live: The server mathematically verifies this timestamp upon submission to ensure the puzzle hasn't expired.
  • Single Use: Once a bot solves the puzzle and submits it, the server logs the transaction. If the bot tries to reuse that exact same solved payload for a second form submission (a replay attack), the server recognizes the duplicate and rejects it.


I hope, I am able to explain how GateCHA works and how to easily implement it in your websites. 

The sample code used in this blog is shared in this Github repository so that you can easily refer:

https://github.com/pranabsharma/gatecha-demo