HTTP Inspector is a self-hosted debugging console for capturing, inspecting, and viewing requests traffic in real time. It pairs an intuitive client UI with a full HTTP API so teams can test integrations with predictable, isolated endpoints.
- Create disposable endpoints with session-scoped isolation
- Capture every HTTP method with full headers, bodies, metadata
- Configure per-endpoint default responses: status codes, headers, and body templates
- Stream live activity through Server-Sent Events for dashboards or automation
- Restore previous sessions with friendly IDs for persistent debugging workflows
- Protect deployments with optional username/password authentication and signed cookies
- Expire sessions, tokens, and stored requests automatically using configurable TTLs
- Export or download raw requests for view/use in other tools.
Both Docker and Podman work with the same flags; substitute podman for docker if you prefer Podman. The example below starts the inspector with a persistent volume for the SQLite database and publishes the UI on port 3001.
docker run -d \
--name http_inspector \
-p 3001:3000 \
-v http_inspector_data:/config \
ghcr.io/arabcoders/http_inspector:latestservices:
http_inspector:
image: ghcr.io/arabcoders/http_inspector:latest
container_name: http_inspector
restart: unless-stopped
ports:
- "3001:3000"
volumes:
- http_inspector_data:/config
environment:
- TRUST_PROXY_CLIENT_IP=false # Set to true if running behind a trusted proxy
volumes:
http_inspector_data:Use the provided compose.yaml to launch the service from the repository root.
docker compose up -d
# podman compose up -dThe UI is available at http://localhost:3001 and the container persists until you run docker compose down (or podman compose down).
![NOTE] The default container run using the root user for simplicity. For production deployments, consider running as a non-root user by adding
user: "1000:1000"(or similar) to the Compose service definition or Docker run command.
| Variable | Required | Default | Description |
|---|---|---|---|
| STORAGE_PATH | No | /config or ./var) | Path for storing db and request bodies |
| SESSION_TTL_DAYS | No | 30 | Session lifetime in days before automatic cleanup |
| TOKEN_TTL_DAYS | No | 30 | Token lifetime in days before automatic cleanup |
| REQUEST_TTL_DAYS | No | 7 | Request history lifetime in days |
| CLEANUP_ENABLED | No | true | Enable automatic cleanup of expired data |
| CLEANUP_ON_STARTUP | No | true | Run cleanup when server starts (in addition to scheduled runs) |
| CLEANUP_INTERVAL_HOURS | No | 1 | How often to run cleanup (in hours) |
| TRUST_PROXY_CLIENT_IP | No | false | Honor X-Forwarded-For when running behind a trusted proxy |
| AUTH_USERNAME | No | - | Username required for login when authentication is enabled |
| AUTH_PASSWORD | No | - | Password required for login when authentication is enabled |
| SESSION_RESTORE_ENABLED | No | true | Enable restoring previous sessions by friendly ID |
| RAW_FULL_URL | No | false | Include full URL in raw request output |
Unless noted, endpoints require an authenticated session when authentication is enabled. Dates in examples use ISO 8601 format.
Returns the active session.
Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"friendlyId": "famous-amethyst-panda",
"createdAt": "2025-01-15T10:30:00.000Z",
"lastAccessedAt": "2025-01-15T10:30:00.000Z"
}Restore a prior session by its friendly ID.
{
"sessionId": "famous-amethyst-panda"
}Response: { "ok": true }
List tokens for the current session.
Response:
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"createdAt": "2025-01-15T10:30:00.000Z",
"_count": {
"requests": 5
}
}
]Create a new token.
Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000"
}Retrieve token configuration.
Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"createdAt": "2025-01-15T10:30:00.000Z",
"responseEnabled": true,
"responseStatus": 200,
"responseHeaders": {
"Content-Type": "application/json"
},
"responseBody": "{\"status\": \"ok\"}"
}Update token response configuration.
Request Body:
{
"enabled": true,
"status": 204,
"headers": {
"X-Request-Signature": "abc123"
},
"body": "{\"processed\": true}"
}Response: { "ok": true }
Delete a token and its stored requests.
Response: { "ok": true }
List requests captured for a token.
Response:
[
{
"id": "650e8400-e29b-41d4-a716-446655440000",
"tokenId": "550e8400-e29b-41d4-a716-446655440000",
"sessionId": "450e8400-e29b-41d4-a716-446655440000",
"method": "POST",
"url": "/api/payload/550e8400-e29b-41d4-a716-446655440000?status=success",
"headers": "{\"content-type\":\"application/json\",\"user-agent\":\"curl/7.79.1\"}",
"contentType": "application/json",
"contentLength": 45,
"isBinary": false,
"clientIp": "192.168.1.100",
"remoteIp": "203.0.113.1",
"bodyPath": "450e8400-e29b-41d4-a716-446655440000/550e8400-e29b-41d4-a716-446655440000/1.bin",
"createdAt": "2025-01-15T10:30:00.000Z"
}
]Fetch metadata for a specific request.
Response:
{
"id": "650e8400-e29b-41d4-a716-446655440000",
"tokenId": "550e8400-e29b-41d4-a716-446655440000",
"sessionId": "450e8400-e29b-41d4-a716-446655440000",
"method": "POST",
"url": "/api/payload/550e8400-e29b-41d4-a716-446655440000?status=success",
"headers": "{\"content-type\":\"application/json\"}",
"contentType": "application/json",
"contentLength": 45,
"isBinary": false,
"clientIp": "192.168.1.100",
"remoteIp": "203.0.113.1",
"bodyPath": "450e8400-e29b-41d4-a716-446655440000/550e8400-e29b-41d4-a716-446655440000/1.bin",
"createdAt": "2025-01-15T10:30:00.000Z"
}Return decoded request body with metadata when the payload is text-based.
{
"body": "{\"event\": \"test\"}",
"headers": {
"content-type": "application/json"
},
"isBinary": false
}Return the raw HTTP request payload as a string. Binary content returns 400 Bad Request with a JSON error payload.
Download the original request body. Responses stream the binary payload with a Content-Disposition header.
Delete a single stored request. Response: { "ok": true }
Delete all stored requests for a token. Response: { "ok": true }
Manually ingest a raw HTTP request into the system.
Request Body:
{
"raw": "POST /api/webhook HTTP/1.1\r\nHost: example.com\r\nContent-Type: application/json\r\n\r\n{\"event\":\"test\"}",
"clientIp": "192.168.1.100",
"remoteIp": "203.0.113.1"
}raw(required): The raw HTTP request in standard HTTP/1.1 format. Supports both path-only URLs (/api/test) and full URLs (http://example.com/api/test)clientIp(optional): Override the client IP addressremoteIp(optional): Override the remote IP address
Response:
{
"ok": true,
"request": {
"id": "650e8400-e29b-41d4-a716-446655440000",
"method": "POST",
"url": "/api/webhook",
"createdAt": "2025-01-15T10:30:00.000Z"
}
}Example:
# Ingest a previously exported raw request with path-only URL
curl -X POST http://localhost:3000/api/token/your-token-id/ingest \
-H "Content-Type: application/json" \
-d '{
"raw": "POST /api/data HTTP/1.1\r\nHost: api.example.com\r\nContent-Type: application/json\r\n\r\n{\"name\":\"test\"}",
"clientIp": "10.0.0.5"
}'
# Ingest a request with full URL
curl -X POST http://localhost:3000/api/token/your-token-id/ingest \
-H "Content-Type: application/json" \
-d '{
"raw": "GET https://api.example.com/webhook?token=abc123 HTTP/1.1\r\nHost: api.example.com\r\nAuthorization: Bearer token\r\n\r\n"
}'Public endpoint used by third-party services to deliver requests. Accepts every HTTP method and captures:
- Method and URL (including query string)
- All headers
- Request body (text or binary)
- Sender IP address
- Timestamp
The response mirrors the token’s default configuration or returns 200 OK when unset.
Examples:
# JSON payload
curl -X POST http://localhost:3000/api/payload/your-token-id \
-H "Content-Type: application/json" \
-d '{"event": "user.created", "id": "123"}'
# Custom headers
curl -X POST http://localhost:3000/api/payload/your-token-id \
-H "Content-Type: application/json" \
-H "X-Request-Signature: abc123" \
-H "X-Event-Type: user.created" \
-d '{"user_id": 123, "email": "[email protected]"}'
# Binary upload
curl -X POST http://localhost:3000/api/payload/your-token-id \
-H "Content-Type: image/png" \
--data-binary @image.png
# GET with query parameters
curl "http://localhost:3000/api/payload/your-token-id?status=success&id=123"Subscribe to session-wide Server-Sent Events. Example client:
const eventSource = new EventSource('/api/events');
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Event:', data);
};Events include:
request.receivedrequest.deletedrequest.clearedtoken.createdtoken.deletedtoken.clearedtoken.response.updated
Event payload example:
{
"type": "request.received",
"token": "550e8400-e29b-41d4-a716-446655440000",
"request": {
"id": "650e8400-e29b-41d4-a716-446655440000",
"method": "POST",
"createdAt": "2025-01-15T10:30:00.000Z"
}
}Stream events for a single token. Delivers the same event types as /api/events, filtered by token ID.
Authenticate a user when auth is enabled. Sets the auth_token HTTP-only cookie.
{
"username": "admin",
"password": "secret"
}Response: { "ok": true, "message": "Login successful" }
Clear the authentication cookie. Response: { "ok": true, "message": "Logout successful" }
Report authentication status.
{
"authenticated": true,
"required": true
}Common status codes:
200 OK— Successful request201 Created— Resource created400 Bad Request— Invalid request parameters or body401 Unauthorized— Authentication required or failed404 Not Found— Resource not found405 Method Not Allowed— Unsupported HTTP method500 Internal Server Error— Unhandled error
Error responses follow this shape:
{
"statusCode": 404,
"message": "Token not found"
}