Overview

har-capture-proxy provides a REST API that you control from your test code. The workflow is always the same regardless of language or framework:

  1. Start the har-capture-proxy binary (or ensure it is running)
  2. Create a proxy session via POST /proxy
  3. Configure your browser to use the proxy
  4. Optionally enable HAR capture or add mutation rules
  5. Run your tests
  6. Retrieve HAR data or tear down the session

Prerequisites

Tip

For CI environments, you can pre-generate the CA cert on one machine and distribute it, or add it to the trust store in your Docker image or CI setup script.

Step-by-Step Integration

1. Start the binary

Launch har-capture-proxy with the REST API on a known port:

./har-capture-proxy --port 8080

2. Create a proxy session

Use the REST API to spin up a new proxy listener:

curl -X POST http://localhost:8080/proxy
# Response: {"port": 8081}

3. Enable HAR capture

curl -X PUT "http://localhost:8080/proxy/8081/har?captureHeaders=true&captureContent=true"

4. Configure the browser

Point your browser's HTTP/HTTPS proxy to localhost:8081 (the port returned in step 2).

5. Add mutation rules (optional)

curl -X POST http://localhost:8080/proxy/8081/rules \
  -H "Content-Type: application/json" \
  -d '[{"url_pattern": ".*api\\.example\\.com.*", "json_mutations": [{"selector": "$.user.name", "action": "set", "value": "Test User"}]}]'

6. Run your tests, then retrieve HAR

curl http://localhost:8080/proxy/8081/har -o capture.har

7. Tear down

curl -X DELETE http://localhost:8080/proxy/8081

Ruby (Selenium WebDriver)

This example uses Ruby's built-in Net::HTTP to manage the proxy and Selenium WebDriver for Chrome.

require 'selenium-webdriver'
require 'net/http'
require 'json'
require 'uri'

PROXY_API = "http://localhost:8080"

# 1. Create a proxy session
uri = URI("#{PROXY_API}/proxy")
response = Net::HTTP.post(uri, "")
proxy_port = JSON.parse(response.body)["port"]
puts "Proxy started on port #{proxy_port}"

# 2. Enable HAR capture
uri = URI("#{PROXY_API}/proxy/#{proxy_port}/har?captureHeaders=true&captureContent=true")
Net::HTTP.start(uri.host, uri.port) do |http|
  http.put(uri, "")
end

# 3. Add mutation rules (optional)
rules_uri = URI("#{PROXY_API}/proxy/#{proxy_port}/rules")
rules = [
  {
    url_pattern: ".*api\\.example\\.com.*",
    json_mutations: [
      { selector: "$.user.email", action: "set", value: "test@example.com" }
    ]
  }
]
req = Net::HTTP::Post.new(rules_uri)
req["Content-Type"] = "application/json"
req.body = rules.to_json
Net::HTTP.start(rules_uri.host, rules_uri.port) { |http| http.request(req) }

# 4. Configure Chrome to use the proxy
options = Selenium::WebDriver::Chrome::Options.new
options.add_argument("--proxy-server=http://localhost:#{proxy_port}")
options.add_argument("--ignore-certificate-errors")

driver = Selenium::WebDriver.for(:chrome, options: options)

begin
  # 5. Run your test
  driver.get("https://example.com")
  puts "Page title: #{driver.title}"

  # 6. Retrieve HAR
  har_uri = URI("#{PROXY_API}/proxy/#{proxy_port}/har")
  har_response = Net::HTTP.get(har_uri)
  File.write("capture.har", har_response)
  puts "HAR saved to capture.har"
ensure
  # 7. Cleanup
  driver.quit
  uri = URI("#{PROXY_API}/proxy/#{proxy_port}")
  Net::HTTP.start(uri.host, uri.port) do |http|
    http.delete(uri)
  end
end
Ruby string interpolation

The #{variable} syntax in the code above is Ruby string interpolation, not template placeholders. These expressions are evaluated at runtime by Ruby.


JavaScript (Playwright)

Playwright has built-in proxy support. Use fetch (Node 18+) or any HTTP client to control the proxy API.

const { chromium } = require('playwright');

const PROXY_API = 'http://localhost:8080';

(async () => {
  // 1. Create a proxy session
  const createRes = await fetch(`${PROXY_API}/proxy`, { method: 'POST' });
  const { port: proxyPort } = await createRes.json();
  console.log(`Proxy started on port ${proxyPort}`);

  // 2. Enable HAR capture
  await fetch(
    `${PROXY_API}/proxy/${proxyPort}/har?captureHeaders=true&captureContent=true`,
    { method: 'PUT' }
  );

  // 3. Add mutation rules (optional)
  await fetch(`${PROXY_API}/proxy/${proxyPort}/rules`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify([{
      url_pattern: '.*api\\.example\\.com.*',
      json_mutations: [
        { selector: '$.user.name', action: 'set', value: 'Test User' }
      ]
    }])
  });

  // 4. Launch browser with proxy
  const browser = await chromium.launch({
    proxy: {
      server: `http://localhost:${proxyPort}`
    }
  });

  const context = await browser.newContext({
    ignoreHTTPSErrors: true
  });
  const page = await context.newPage();

  // 5. Run your test
  await page.goto('https://example.com');
  console.log(`Page title: ${await page.title()}`);

  // 6. Retrieve HAR
  const harRes = await fetch(`${PROXY_API}/proxy/${proxyPort}/har`);
  const har = await harRes.text();
  const fs = require('fs');
  fs.writeFileSync('capture.har', har);
  console.log('HAR saved to capture.har');

  // 7. Cleanup
  await browser.close();
  await fetch(`${PROXY_API}/proxy/${proxyPort}`, { method: 'DELETE' });
})();

Python (Selenium WebDriver)

Uses the requests library for proxy management and Selenium for Chrome.

import requests
import json
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

PROXY_API = "http://localhost:8080"

# 1. Create a proxy session
response = requests.post(f"{PROXY_API}/proxy")
proxy_port = response.json()["port"]
print(f"Proxy started on port {proxy_port}")

# 2. Enable HAR capture
requests.put(
    f"{PROXY_API}/proxy/{proxy_port}/har",
    params={"captureHeaders": "true", "captureContent": "true"}
)

# 3. Add mutation rules (optional)
rules = [{
    "url_pattern": ".*api\\.example\\.com.*",
    "json_mutations": [
        {"selector": "$.user.name", "action": "set", "value": "Test User"}
    ]
}]
requests.post(
    f"{PROXY_API}/proxy/{proxy_port}/rules",
    json=rules
)

# 4. Configure Chrome to use the proxy
chrome_options = Options()
chrome_options.add_argument(f"--proxy-server=http://localhost:{proxy_port}")
chrome_options.add_argument("--ignore-certificate-errors")

driver = webdriver.Chrome(options=chrome_options)

try:
    # 5. Run your test
    driver.get("https://example.com")
    print(f"Page title: {driver.title}")

    # 6. Retrieve HAR
    har = requests.get(f"{PROXY_API}/proxy/{proxy_port}/har")
    with open("capture.har", "w") as f:
        f.write(har.text)
    print("HAR saved to capture.har")

finally:
    # 7. Cleanup
    driver.quit()
    requests.delete(f"{PROXY_API}/proxy/{proxy_port}")

BrowserMob Proxy Migration Checklist

If you are migrating from BrowserMob Proxy, follow this checklist to ensure a smooth transition.


CI Pipeline Tips

Start as a background service

In CI, start har-capture-proxy before your test suite runs:

# Start in background
./har-capture-proxy --port 8080 &
PROXY_PID=$!

# Wait for it to be ready
sleep 1

# Run your tests
bundle exec rspec

# Stop the proxy
kill $PROXY_PID

Docker

Run har-capture-proxy as a sidecar container or within the same container as your tests:

# Dockerfile snippet
COPY har-capture-proxy /usr/local/bin/har-capture-proxy
RUN chmod +x /usr/local/bin/har-capture-proxy

GitHub Actions

steps:
  - name: Download har-capture-proxy
    run: |
      curl -L https://github.com/jaygen/har-capture-proxy/releases/latest/download/har-capture-proxy-x86_64-unknown-linux-gnu \
        -o har-capture-proxy
      chmod +x har-capture-proxy

  - name: Start proxy
    run: |
      ./har-capture-proxy --port 8080 &
      sleep 1

  - name: Run tests
    run: npm test

Parallel test suites

Each test worker can create its own proxy session via POST /proxy. Sessions are isolated from each other. No need to run multiple proxy processes.

Tip

Use POST /proxy?port=0 to let the OS assign an available port. This avoids port conflicts in parallel test environments.

Note

Always delete proxy sessions after tests complete (DELETE /proxy/{port}) to free up resources. Leaked sessions accumulate memory and file descriptors.