Introduction
Proxelar is a scriptable local traffic workbench written in Rust. It sits between a client and an upstream service so you can inspect, intercept, replay, and modify HTTP, HTTPS, and WebSocket traffic.
It is aimed at development and debugging workflows: API inspection, local service mocking, request/response rewriting, WebSocket debugging, and repeatable traffic transforms without changing the application under test.
What can it do?
- Inspect traffic — see every request and response in real time, including headers and bodies
- Intercept HTTPS — automatic CA certificate generation and per-host certificate minting
- Modify traffic with Lua scripts — write
on_requestandon_responsehooks to transform, block, or mock traffic at runtime - Forward and reverse proxy — use as a system proxy (forward) or put it in front of your service (reverse)
- Three interfaces — interactive TUI, plain terminal output, or web GUI
- Inspect WebSockets — capture WebSocket connections and browse individual frames
What is it not?
Proxelar is not trying to replace a mature security suite. If you need scanning, collaborative testing, a large addon ecosystem, or advanced transparent capture today, use a tool built for that workflow. Proxelar is deliberately smaller: a local, scriptable proxy that is easy to install, run, and automate.
Architecture
Proxelar is built as a three-crate Rust workspace:
proxelar-cli— the CLI binary with three interface modesproxyapi— the core proxy engine, usable as a standalone libraryproxyapi_models— shared request/response data types
The proxy engine is built on hyper 1.x, rustls 0.23, and tokio. HTTPS interception uses OpenSSL for certificate generation and rustls for TLS termination. Lua scripting is powered by mlua with a vendored Lua 5.4.
Installation
Homebrew (macOS / Linux)
brew install proxelar
From crates.io
cargo install proxelar
This builds and installs the proxelar binary. Lua 5.4 and OpenSSL are vendored and compiled from source, so no system dependencies are required beyond a Rust toolchain.
From source
git clone https://github.com/emanuele-em/proxelar.git
cd proxelar
cargo build --release
The binary is at target/release/proxelar.
Without Lua scripting
If you don’t need scripting and want a smaller build:
cargo install proxelar --no-default-features
Requirements
- Rust 1.94 or later
- Works on Linux, macOS, and Windows
Quick Start
1. Start the proxy
proxelar
This starts Proxelar in forward proxy mode with the interactive TUI on 127.0.0.1:8080.
2. Install the CA certificate
Configure your system or browser proxy to 127.0.0.1:8080, then visit http://proxel.ar through the proxy. The page provides direct certificate downloads and platform-specific installation instructions.
Alternatively, manually install ~/.proxelar/proxelar-ca.pem. See CA Certificate for all platforms.
3. Browse through the proxy
All HTTP and HTTPS traffic now flows through Proxelar and appears in the TUI. Press ? for the full keybinding reference, or see Interfaces for details on the TUI, terminal, and web GUI modes.
4. Try a Lua script
Create a file called script.lua:
function on_request(request)
request.headers["X-Proxied-By"] = "proxelar"
return request
end
Run Proxelar with the script:
proxelar --script script.lua
Every request passing through the proxy now has the X-Proxied-By header injected.
CA Certificate
Proxelar intercepts HTTPS traffic by generating a local Certificate Authority (CA) and minting per-host leaf certificates on the fly. For this to work, your system must trust the Proxelar CA.
Automatic generation
On first run, Proxelar generates a 4096-bit RSA CA certificate and private key in ~/.proxelar/:
~/.proxelar/proxelar-ca.pem— CA certificate~/.proxelar/proxelar-ca.key— CA private key (mode 0600)
If these files already exist, they are reused.
Certificate download server
The easiest way to install the CA is through the built-in download server. With the proxy running, visit:
http://proxel.ar
This page provides:
- Direct download links for PEM and DER formats
- Platform-specific installation instructions for macOS, Linux, Windows, iOS, and Android
Manual installation
macOS
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain \
~/.proxelar/proxelar-ca.pem
Linux (Debian/Ubuntu)
sudo cp ~/.proxelar/proxelar-ca.pem /usr/local/share/ca-certificates/proxelar.crt
sudo update-ca-certificates
Linux (Fedora/RHEL)
sudo cp ~/.proxelar/proxelar-ca.pem /etc/pki/ca-trust/source/anchors/proxelar.pem
sudo update-ca-trust
Windows
certutil -addstore -f "ROOT" %USERPROFILE%\.proxelar\proxelar-ca.pem
Firefox
Firefox uses its own certificate store. Go to Settings > Privacy & Security > Certificates > View Certificates > Import, and select ~/.proxelar/proxelar-ca.pem.
Custom CA directory
Use --ca-dir to store the CA files in a different location:
proxelar --ca-dir /path/to/certs
Removing the CA
When you are done, remove the Proxelar CA from every trust store where you installed it. The generated files live in ~/.proxelar/ by default, but deleting those files does not remove trust from your OS, browser, or mobile device.
See CA trust and uninstall for platform-specific uninstall notes and limitations such as certificate pinning.
Per-host certificate caching
Leaf certificates are cached in memory (up to 1,000 hosts). Repeated connections to the same host reuse the cached certificate instead of generating a new one.
Inspect browser and curl traffic
This guide gets a basic HTTP and HTTPS capture working with the default forward proxy mode.
Start Proxelar
proxelar
The proxy listens on 127.0.0.1:8080 and opens the TUI.
Test with curl
Plain HTTP works without a certificate:
curl -x http://127.0.0.1:8080 http://httpbin.org/get
For HTTPS, curl must trust the generated Proxelar CA:
curl --proxy http://127.0.0.1:8080 \
--cacert ~/.proxelar/proxelar-ca.pem \
https://httpbin.org/get
The request and response should appear in the TUI. Press Enter to open details, Tab to switch request/response tabs, and / to filter.
Configure a browser
Set both HTTP and HTTPS proxy to:
127.0.0.1:8080
Then browse to:
http://proxel.ar
Download and trust the Proxelar CA using the instructions shown on that page. After the CA is trusted, HTTPS pages should appear in Proxelar.
Firefox uses its own certificate store unless configured to use the system store. Import ~/.proxelar/proxelar-ca.pem in Firefox settings if HTTPS traffic still shows certificate warnings.
Troubleshooting
- If HTTP works but HTTPS fails, the client does not trust the Proxelar CA.
- If nothing appears, confirm the client is actually using
127.0.0.1:8080as both HTTP and HTTPS proxy. - If an app uses certificate pinning, Proxelar cannot decrypt it without changing the app or test configuration.
- On Android 7+, user-installed CAs are trusted only by apps that opt in through network security configuration.
Mock or modify a local API
Reverse proxy mode puts Proxelar in front of a service without configuring the client as an explicit proxy. This is useful for local frontend development, API testing, and scripted response changes.
Start a reverse proxy
If your app normally calls a backend on http://localhost:3000, run:
proxelar -m reverse --target http://localhost:3000
Clients connect to:
http://127.0.0.1:8080
Proxelar forwards requests to http://localhost:3000 while preserving the path and query string.
Mock one endpoint
Create mock_user.lua:
function on_request(request)
if request.method == "GET" and string.find(request.url, "/api/user/me") then
return {
status = 200,
headers = { ["Content-Type"] = "application/json" },
body = '{"id":1,"name":"Local Test User"}',
}
end
end
Run:
proxelar -m reverse --target http://localhost:3000 --script mock_user.lua
Requests to /api/user/me are answered by the script. Other requests pass through to the target.
Inject development headers
function on_request(request)
request.headers["Authorization"] = "Bearer local-dev-token"
request.headers["X-Forwarded-By"] = "proxelar"
return request
end
Simulate a failure
function on_request(request)
if string.find(request.url, "/api/payments") then
return {
status = 503,
headers = { ["Content-Type"] = "application/json" },
body = '{"error":"payments unavailable in local test"}',
}
end
end
Notes
- Reverse proxy mode does not require browser or OS proxy configuration.
- Use
-i guiif you prefer the web UI while developing. - Use
--upstream-trust default+ca:/path/to/ca.pemif the upstream service uses a private HTTPS CA. - For HTTPS clients connecting to Proxelar itself, use forward proxy mode today; reverse proxy TLS termination for inbound clients is not the main supported workflow.
Lua recipes
Lua scripts define on_request and/or on_response hooks. Return a modified table to continue, return nil to pass through unchanged, or return a response table from on_request to short-circuit the upstream request.
Add a request header
function on_request(request)
request.headers["X-Proxied-By"] = "proxelar"
return request
end
Remove cookies
function on_request(request)
request.headers["cookie"] = nil
return request
end
Add CORS response headers
function on_response(request, response)
response.headers["Access-Control-Allow-Origin"] = "*"
response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
response.headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization"
return response
end
Block a domain
local blocked = { "ads%.example%.com", "tracker%.example%.com" }
function on_request(request)
for _, pattern in ipairs(blocked) do
if string.find(request.url, pattern) then
return {
status = 403,
headers = { ["Content-Type"] = "text/plain" },
body = "Blocked by Proxelar",
}
end
end
end
Modify a JSON response
function on_response(request, response)
local ct = response.headers["content-type"] or ""
if not string.find(ct, "application/json") then return end
if string.sub(response.body, 1, 1) == "{" then
response.body = '{"proxied":true,' .. string.sub(response.body, 2)
end
return response
end
Use the checked-in examples
The repository includes complete scripts in examples/scripts/:
add_header.luaauth_inject.luablock_domain.luafilter_by_method.luainject_cors.lualog_traffic.luamock_api.luamodify_json_response.luaredirect.luarequest_body_modify.luarewrite_html.luastrip_cookies.lua
See the Lua API reference for all fields and return values.
CA trust and uninstall
Proxelar decrypts HTTPS by generating a local Certificate Authority and minting per-host certificates. Clients must trust that CA before HTTPS interception works.
Generated files
By default, Proxelar stores the CA files in:
~/.proxelar/proxelar-ca.pem
~/.proxelar/proxelar-ca.key
The private key stays on your machine. Anyone with the key can mint certificates trusted by clients where you installed the CA, so treat it as sensitive.
Install through the built-in page
Start Proxelar, configure your browser or device to use 127.0.0.1:8080, then visit:
http://proxel.ar
The page provides PEM/DER downloads and platform notes.
Uninstall notes
Remove trust from every place where you installed the CA:
- macOS: open Keychain Access, find the Proxelar certificate, and delete it from the trusted keychain.
- Linux: remove the certificate from
/usr/local/share/ca-certificates/or/etc/pki/ca-trust/source/anchors/, then run the platform trust update command. - Windows: open Certificate Manager or run
certmgr.msc, find the Proxelar root under trusted root authorities, and delete it. - Firefox: remove it from Settings > Privacy & Security > Certificates > View Certificates.
- iOS/Android: remove the installed profile or user CA from system settings.
After trust is removed, deleting ~/.proxelar/ removes Proxelar’s local copy of the certificate and key.
Limitations
- Certificate-pinned apps usually reject Proxelar’s generated certificates.
- Android 7+ apps trust user-installed CAs only if the app opts in.
- Some corporate-managed devices block custom CA installation.
- If you bind Proxelar to a network interface, other devices can reach the proxy. Only do this on trusted networks and with a clear reason.
Forward Proxy
Forward proxy is the default mode. Clients send their traffic to Proxelar, which forwards it to the destination server. This is the standard setup for inspecting browser or application traffic.
Usage
proxelar
Configure your client (browser, curl, application) to use 127.0.0.1:8080 as the HTTP/HTTPS proxy.
How it works
- The client sends a request to the proxy
- For HTTPS, the client sends a
CONNECTrequest. Proxelar upgrades the connection and detects the protocol:- TLS ClientHello — generates a leaf certificate for the target host, terminates TLS, and inspects the decrypted traffic
- Plain HTTP (e.g.,
GETprefix) — serves the stream directly - Unknown protocol — tunnels the raw TCP connection without inspection
- For plain HTTP, the request is forwarded directly
- Lua
on_request/on_responsehooks run at each step (if a script is loaded)
Examples
# Start forward proxy on default port
proxelar
# Custom port and bind address
proxelar -p 9090 -b 0.0.0.0
# With terminal output instead of TUI
proxelar -i terminal
# With a Lua script
proxelar --script block_ads.lua
# Test with curl
curl -x http://127.0.0.1:8080 http://httpbin.org/get
curl -x http://127.0.0.1:8080 https://httpbin.org/get
# Trust an extra private CA when Proxelar connects to upstream HTTPS servers
proxelar --upstream-trust default+ca:/path/to/ca.pem
For upstream HTTPS, Proxelar uses bundled Mozilla/WebPKI roots by default. --upstream-trust ca-only:/path/to/ca.pem trusts only a supplied CA file, and --upstream-trust insecure disables upstream certificate and hostname verification for controlled debugging. insecure makes upstream HTTPS traffic vulnerable to MITM.
Reverse Proxy
In reverse proxy mode, Proxelar sits in front of a backend service. Clients connect to Proxelar directly (without proxy configuration), and all requests are forwarded to the specified target.
This is useful for debugging local APIs, injecting headers, mocking endpoints, or testing how your frontend handles modified responses.
Usage
proxelar -m reverse --target http://localhost:3000
Clients connect to http://127.0.0.1:8080 and Proxelar forwards everything to http://localhost:3000.
How it works
- The client sends a request to
127.0.0.1:8080 - Proxelar rewrites the URI to the target (preserving path and query)
- The
Hostheader is updated to match the target - Lua
on_request/on_responsehooks run (if a script is loaded) - The response is returned to the client
Examples
# Reverse proxy to a local service
proxelar -m reverse --target http://localhost:3000
# Custom port (clients connect to 4000, forwarded to 3000)
proxelar -m reverse --target http://localhost:3000 -p 4000
# With a Lua script that injects auth headers
proxelar -m reverse --target http://localhost:3000 --script auth_dev.lua
# With web GUI
proxelar -m reverse --target http://localhost:3000 -i gui
# HTTPS upstream with a private CA
proxelar -m reverse --target https://localhost:3000 --upstream-trust default+ca:/path/to/ca.pem
For upstream HTTPS, Proxelar uses bundled Mozilla/WebPKI roots by default. Use --upstream-trust default+ca:/path/to/ca.pem to add a private CA, --upstream-trust ca-only:/path/to/ca.pem to trust only that CA, or --upstream-trust insecure for temporary debugging without certificate or hostname verification. insecure makes upstream HTTPS traffic vulnerable to MITM.
Common use cases with scripting
Inject authentication
function on_request(request)
request.headers["Authorization"] = "Bearer dev-token-12345"
return request
end
Add security headers
function on_response(request, response)
response.headers["Strict-Transport-Security"] = "max-age=31536000"
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "DENY"
return response
end
Simulate errors
function on_request(request)
if string.find(request.url, "/api/payments") then
return {
status = 500,
headers = { ["Content-Type"] = "application/json" },
body = '{"error": "Internal Server Error"}',
}
end
end
Intercept & Modify Traffic
Intercept mode pauses requests mid-flight so you can inspect, edit, and decide what to do before they reach the server.
How it works
When intercept is on, every request is held until you act on it. Nothing is forwarded automatically. When intercept is off, traffic flows through normally (still captured and displayed).
TUI
Toggle intercept
Press i to turn intercept on or off. The status bar shows a red INTERCEPT badge when active.
Act on a request
When a request arrives it appears as a ⏸ row. Navigate to it with j/k, then:
| Key | Action |
|---|---|
f | Forward the request (as-is or with your edits) |
d | Drop — returns a 504 to the client |
e | Open the inline editor |
Edit inline
Press e to open the editor. The full raw HTTP request is shown and fully editable — method, URI, headers, and body.
POST /api/login HTTP/1.1
host: example.com
content-type: application/json
{"user":"alice","pass":"secret"}
- Arrow keys / Home / End — move the cursor
- Enter — insert a new line
- Backspace / Delete — delete characters
- Esc — finish editing (request stays held, ready to forward)
f— forward (with your edits applied)d— drop- Esc (again, when not typing) — discard your edits
Binary bodies — if the original body is not valid UTF-8 the editor shows a ⚠ warning. The content is displayed lossily; edits may corrupt binary data.
Web GUI
Click the ⏸ Intercept: OFF button in the toolbar to enable intercept. The button turns red and shows a pending-request count.
Pending requests appear in the table with an amber left border. Click a row to open the editor panel:
- Edit the method, URI, headers, and body directly
- Click Forward to send (with any edits you made)
- Click Drop (504) to block the request
- Press Ctrl+Enter as a keyboard shortcut for Forward
- Press Esc or × to close the panel without acting (request stays pending)
Turning intercept off
Press i (TUI) or click the intercept button (web) again. All pending requests are forwarded immediately so clients do not hang.
Lua Scripting
Proxelar supports Lua scripts that hook into the request/response lifecycle. You can modify headers, rewrite URLs, block requests, mock API responses, transform bodies, and more — all without recompiling or changing your application.
Running a script
proxelar --script my_script.lua
The script is loaded once at startup. It applies to all traffic flowing through the proxy, in both forward and reverse modes.
Writing a script
A script defines one or both of these global functions:
function on_request(request)
-- Called before forwarding the request to the upstream server.
-- Modify and return the request, return a response to short-circuit,
-- or return nil to pass through unchanged.
end
function on_response(request, response)
-- Called before returning the response to the client.
-- Modify and return the response, or return nil to pass through unchanged.
end
Both functions are optional. If a function is not defined, traffic passes through unchanged.
Request hook
on_request receives a request table and can return one of three things:
- The request table — forward it (modified or not)
- A response table (with a
statusfield) — short-circuit and return that response directly, without contacting the upstream server nil(or no return) — pass through unchanged
function on_request(request)
-- Pass through logging only
if string.find(request.url, "blocked%.com") then
return { status = 403, headers = {}, body = "Blocked" } -- short-circuit
end
request.headers["X-Custom"] = "value"
return request -- forward modified request
end
Response hook
on_response receives both the request (for context) and the response. It can modify and return the response, or return nil to pass through.
function on_response(request, response)
response.headers["X-Proxy"] = "proxelar"
return response
end
Error handling
Script errors are caught, logged, and the request passes through unchanged. A buggy script can never crash the proxy. Check the log output (set RUST_LOG=debug for details) to see script errors.
Native C modules
By default the Lua VM runs in safe mode, which blocks native C modules — require of a C module fails with can't load C modules in safe mode. To use one (for example lua-protobuf), pass --allow-c-modules:
proxelar --script decode.lua --allow-c-modules
This runs the VM in unsafe mode: loaded modules execute unsandboxed native code in the proxy process, so only use it with scripts you trust. The module must target Lua 5.4 (the version proxelar embeds).
On Windows, the standard release binary statically links Lua and cannot load C modules. Use the …-cmodules release archive instead, which links a shared lua54.dll bundled alongside the executable.
Feature flag
Lua scripting is behind the scripting feature flag, enabled by default. To build without it:
cargo install proxelar --no-default-features
API Reference
Request table
The on_request function receives a table with these fields:
| Field | Type | Description |
|---|---|---|
method | string | HTTP method ("GET", "POST", "PUT", "DELETE", etc.) |
url | string | Full request URL ("https://example.com/path?q=1") |
headers | table | Request headers (see Headers below) |
body | string | Request body (may contain binary data, empty string for GET/HEAD) |
All fields are readable and writable. Modify them in place and return the table to forward the modified request.
body is always plaintext: if the message uses a supported Content-Encoding
(gzip, deflate, or br), the proxy decompresses it before calling the hook
and re-compresses your result to the same encoding on the way out, refreshing
Content-Length. Remove the Content-Encoding header to forward the body
uncompressed instead. Any other encoding is passed through untouched. See
Content encoding below.
Response table
The on_response function receives two arguments:
- request — a table with
methodandurlfields (for context) - response — a table with these fields:
| Field | Type | Description |
|---|---|---|
status | number | HTTP status code (200, 404, 500, etc.) |
headers | table | Response headers |
body | string | Response body (plaintext — see Content encoding) |
Short-circuit response
To respond immediately without contacting the upstream server, return a table with a status field from on_request:
return {
status = 403,
headers = { ["Content-Type"] = "text/plain" },
body = "Forbidden",
}
The presence of the status field is what distinguishes a response from a modified request.
Headers
Headers are Lua tables mapping lowercase header names to values.
Single-value headers are plain strings:
request.headers["content-type"] -- "application/json"
request.headers["authorization"] -- "Bearer token123"
Multi-value headers (like Set-Cookie) are arrays:
response.headers["set-cookie"] -- {"session=abc", "lang=en"}
When setting headers, both forms are accepted:
-- Single value (most common)
request.headers["x-custom"] = "value"
-- Multiple values
response.headers["set-cookie"] = {"a=1", "b=2"}
-- Remove a header
request.headers["cookie"] = nil
Content encoding
Scripts work on decompressed bodies. When a request or response carries a
Content-Encoding the proxy understands, the body is decoded before your hook
runs and re-encoded to the same scheme afterward, with Content-Length updated
to match.
Content-Encoding | Behavior |
|---|---|
gzip / deflate / br | Decoded for the hook, re-encoded on output |
absent / identity | Passed through as-is |
anything else (e.g. zstd) | Passed through compressed, untouched |
To change the wire encoding, edit the Content-Encoding header in your hook:
-- Forward the response uncompressed
response.headers["content-encoding"] = nil
response.body = "now plaintext on the wire"
If re-encoding fails, the proxy strips Content-Encoding and sends the body
uncompressed rather than corrupting it. Bodies larger than
--body-capture-limit stream through unchanged and are never decoded.
Return values
on_request
| Return | Effect |
|---|---|
| Request table | Forward the (modified) request to upstream |
Response table (has status) | Short-circuit — return this response directly |
nil (or no return) | Pass through unchanged |
on_response
| Return | Effect |
|---|---|
| Response table | Return the (modified) response to the client |
nil (or no return) | Pass through unchanged |
Available Lua standard libraries
Scripts run in a standard Lua 5.4 environment with access to:
string— pattern matching, formatting, manipulationtable— array/table operationsmath— mathematical functionsos.date(),os.time(),os.clock()— time functionsprint()— output to proxy stdouttostring(),tonumber(),type()— type conversion
Script Examples
All examples below are complete, working scripts. They are also available in the examples/scripts/ directory.
Add headers to requests
function on_request(request)
request.headers["X-Forwarded-By"] = "proxelar"
request.headers["X-Request-Time"] = os.date("%Y-%m-%dT%H:%M:%S")
return request
end
Block domains
local blocked = {
"ads%.example%.com",
"tracker%.example%.com",
"analytics%.bad%.com",
}
function on_request(request)
for _, pattern in ipairs(blocked) do
if string.find(request.url, pattern) then
return {
status = 403,
headers = { ["Content-Type"] = "text/plain" },
body = "Blocked by Proxelar: " .. request.url,
}
end
end
end
Mock API endpoints
function on_request(request)
if request.method == "GET" and string.find(request.url, "/api/user/me") then
return {
status = 200,
headers = { ["Content-Type"] = "application/json" },
body = '{"id": 1, "name": "Test User", "email": "test@example.com"}',
}
end
end
Redirect requests to a different host
function on_request(request)
request.url = string.gsub(request.url, "old%-api%.example%.com", "new-api.example.com")
return request
end
Inject CORS headers
function on_response(request, response)
response.headers["Access-Control-Allow-Origin"] = "*"
response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
response.headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization"
return response
end
Log traffic to stdout
function on_request(request)
print(string.format("[REQ] %s %s", request.method, request.url))
end
function on_response(request, response)
local ct = response.headers["content-type"] or "unknown"
local size = #response.body
print(string.format("[RES] %s %s -> %d (%s, %d bytes)",
request.method, request.url, response.status, ct, size))
end
Run with proxelar -i terminal -q --script log.lua to see only the script’s output, without the proxy’s own per-request lines.
Inject authentication
local TOKEN = "Bearer my-dev-token-12345"
function on_request(request)
if string.find(request.url, "api%.example%.com") then
request.headers["Authorization"] = TOKEN
end
return request
end
Modify JSON response bodies
function on_response(request, response)
local ct = response.headers["content-type"] or ""
if not string.find(ct, "application/json") then return end
if string.sub(response.body, 1, 1) == "{" then
response.body = '{"proxied":true,' .. string.sub(response.body, 2)
end
return response
end
Inject a banner into HTML pages
function on_response(request, response)
local ct = response.headers["content-type"] or ""
if not string.find(ct, "text/html") then return end
local banner = '<div style="position:fixed;top:0;left:0;right:0;'
.. 'background:#ff6b35;color:white;text-align:center;'
.. 'padding:4px;z-index:99999;font-size:12px;">'
.. 'Proxied by Proxelar</div>'
response.body = string.gsub(response.body, "<body>", "<body>" .. banner, 1)
return response
end
Only allow GET and HEAD
function on_request(request)
if request.method ~= "GET" and request.method ~= "HEAD" then
return {
status = 405,
headers = {
["Content-Type"] = "text/plain",
["Allow"] = "GET, HEAD",
},
body = "Method " .. request.method .. " not allowed by proxy policy",
}
end
end
Strip tracking cookies
local tracking_cookies = { "fbp", "_ga", "_gid", "fr", "datr" }
function on_request(request)
local cookie = request.headers["cookie"]
if not cookie then return end
local parts = {}
for pair in string.gmatch(cookie, "([^;]+)") do
pair = string.match(pair, "^%s*(.-)%s*$")
local name = string.match(pair, "^([^=]+)")
local dominated = false
for _, tc in ipairs(tracking_cookies) do
if name == tc then dominated = true; break end
end
if not dominated then table.insert(parts, pair) end
end
if #parts > 0 then
request.headers["cookie"] = table.concat(parts, "; ")
else
request.headers["cookie"] = nil
end
return request
end
Modify POST request bodies
function on_request(request)
if request.method ~= "POST" then return end
local ct = request.headers["content-type"] or ""
if string.find(ct, "application/json") and string.sub(request.body, 1, 1) == "{" then
request.body = '{"injected_by":"proxelar",' .. string.sub(request.body, 2)
end
return request
end
CLI Reference
proxelar [OPTIONS]
Options
| Flag | Short | Default | Description |
|---|---|---|---|
--interface | -i | tui | Interface mode: terminal, tui, or gui |
--mode | -m | forward | Proxy mode: forward or reverse |
--port | -p | 8080 | Port to listen on |
--addr | -b | 127.0.0.1 | Bind address |
--target | -t | — | Upstream target URI (required for reverse mode) |
--script | -s | — | Path to a Lua script for request/response hooks |
--allow-c-modules | off | Let scripts load native Lua C modules (e.g. lua-protobuf); runs the VM in unsafe mode | |
--quiet | -q | Suppress per-request output (only used with -i terminal) | |
--gui-port | 8081 | Web GUI port (only used with -i gui) | |
--ca-dir | ~/.proxelar | Directory for CA certificate and key files | |
--body-capture-limit | free | Maximum body bytes buffered for capture/editing; use free, unlimited, or none for unlimited | |
--upstream-trust | default | Upstream TLS trust policy: default, default+ca:/path/ca.pem, ca-only:/path/ca.pem, or insecure |
--upstream-trust insecure disables upstream certificate and hostname verification. Use it only for controlled debugging; it makes upstream HTTPS traffic vulnerable to MITM.
Environment variables
| Variable | Description |
|---|---|
RUST_LOG | Controls log verbosity. Examples: debug, proxyapi=trace, warn |
Examples
# Default: forward proxy with TUI
proxelar
# Terminal output on custom port
proxelar -i terminal -p 9090
# Web GUI accessible from the network
proxelar -i gui -b 0.0.0.0
# Reverse proxy with script
proxelar -m reverse --target http://localhost:3000 --script auth.lua
# Forward proxy with logging script
proxelar --script log_traffic.lua
# Show only the script's print() output, no per-request lines
proxelar -i terminal -q --script log_traffic.lua
# Capture only the first 1 MiB of large bodies while streaming traffic through
proxelar --body-capture-limit 1048576
# Trust a private upstream CA in addition to the default Mozilla roots
proxelar --upstream-trust default+ca:/path/to/ca.pem
# Trust only a private upstream CA
proxelar --upstream-trust ca-only:/path/to/ca.pem
Interfaces
Proxelar provides three interface modes, all showing the same live traffic data.
TUI (default)
proxelar
# or
proxelar -i tui
An interactive terminal interface built with ratatui. Shows a table of all captured requests and WebSocket connections with nine columns: time, protocol, method, host, path, status, content-type, size, and duration.
Key bindings
| Key | Action |
|---|---|
j / k / ↑ / ↓ | Navigate requests |
Enter | Open detail panel; press again to focus it for scrolling |
j / k (focused) | Scroll detail content |
Tab | Switch between Request and Response (or Frames) tabs |
/ | Enter filter mode |
Esc | Close detail panel or clear filter |
g / G | Jump to first / last request |
r | Replay selected request |
c | Clear all captured requests |
? | Show keybinding help |
q / Ctrl+C | Quit |
The detail panel shows the full request or response including headers and body. For WebSocket connections the Frames tab lists every captured frame with its direction (↑ client→server, ↓ server→client), opcode, size, and payload preview.
Filtering
Press / to enter filter mode. Plain text searches across method and URL. Use column:value to scope the search to a single column:
| Syntax | Matches |
|---|---|
time:14: | rows captured after 14:00 |
proto:https | rows using HTTPS or WSS |
method:POST | rows whose method contains POST |
host:github | rows whose host contains github |
path:/api | rows whose path contains /api |
status:404 | rows whose status contains 404 |
type:json | rows whose content-type contains json |
size:1.5 | rows whose formatted size contains 1.5 |
duration:slow | rows whose formatted duration contains slow |
Column names are case-insensitive. Press Enter to apply, Esc to cancel.
Terminal
proxelar -i terminal
Prints each request/response as a colored line to stdout. Useful for quick inspection or when piping output to other tools.
Output includes timestamp, HTTP method (color-coded), URL, status code, and response size.
Pass --quiet (-q) to suppress the per-request lines; errors still go to stderr. This is useful with a Lua script that produces its own output via print():
proxelar -i terminal -q --script log_traffic.lua
Web GUI
proxelar -i gui
Opens a web interface at http://127.0.0.1:8081 (configurable with --gui-port). Built with axum and WebSocket for real-time streaming.
Features:
- Interactive request table with live updates — nine columns: Time, Proto, Method, Host, Path, Status, Type, Size, Duration
- WebSocket inspection — connections appear as live/closed rows; click to browse frames
- Unified
column:valuesearch bar — same syntax as the TUI filter (e.g.status:404,type:json,proto:https) - Click a row to view full request/response detail
- Intercept mode — pause requests, edit method/URI/headers/body, then forward or drop
- JSON pretty-printing in the detail view
- Light and dark mode (follows system preference)
To make the web GUI accessible from other machines:
proxelar -i gui -b 0.0.0.0
The current web GUI is designed for local use. Its WebSocket connection is token-protected and accepts localhost origins, so remote browser access should be done through a local tunnel such as SSH port forwarding until remote GUI access is explicitly hardened and documented.
Known limitations
Proxelar is usable today for local traffic inspection, scripting, intercept, replay, and WebSocket inspection. These are the main gaps to understand before choosing it for a workflow.
Persistence and export
Captured flows currently live in memory. Proxelar does not yet save sessions, reload sessions, export HAR files, export curl commands, or write raw request/response pairs.
Use Proxelar when you need live inspection and local transformation. Use a tool with mature export support if saved artifacts are central to your workflow.
Body decoding and editing
Bodies are captured as bytes, and large bodies can be capped with --body-capture-limit. Rich content decoding is limited today:
- gzip, br, zstd, and deflate decoding are not yet first-class content views
- binary body editing is intentionally cautious
- content-aware editors for JSON, forms, protobuf, multipart, and images are roadmap items
Capture modes
Proxelar supports explicit forward proxy mode and reverse proxy mode. It does not yet support transparent/local capture, WireGuard-style capture, SOCKS5 mode, DNS inspection, or upstream proxy chaining.
HTTPS and mobile apps
HTTPS interception requires trusting the Proxelar CA. Certificate-pinned clients will reject the generated certificates. Android 7+ apps trust user-installed CAs only if the app explicitly opts in.
Remote web GUI
The web GUI is designed for local use. It uses a runtime token and currently accepts localhost origins for its WebSocket connection. Use SSH port forwarding or another local tunnel if you need to view it from another machine.
Security-suite features
Proxelar is not a scanner, crawler, collaborative testing platform, or vulnerability management tool. For those workflows, tools such as Burp Suite, Caido, or mitmproxy may be a better fit.
Comparison with other tools
This page is intentionally practical, not promotional. Proxelar overlaps with several proxy tools, but it is not the best choice for every workflow.
Summary
Use Proxelar when you want a local, scriptable, Rust-native traffic workbench with a TUI, web GUI, Lua hooks, request intercept, replay, and WebSocket frame inspection.
Choose another tool when you need mature export formats, transparent capture, a large addon ecosystem, polished desktop UX, or professional security testing workflows.
mitmproxy
mitmproxy is the category default for many developers and security testers. It has mature HTTP tooling, a large addon ecosystem, strong flow persistence/export workflows, transparent/local capture modes, and broad documentation.
Proxelar is smaller. Its strengths are a Rust-native implementation, a single CLI with TUI/web/terminal modes, Lua scripting, and a focused development-debugging workflow. It is not yet a mitmproxy replacement for advanced capture modes, saved flow formats, or deep content views.
Choose mitmproxy if you need the most mature general-purpose MITM proxy today. Choose Proxelar if you value a compact Rust-native tool with Lua transforms and are comfortable with a younger feature set.
proxyfor
proxyfor is the closest Rust CLI neighbor: it provides forward/reverse proxy modes, TUI/WebUI, filtering, CA install help, export formats, and portable binaries.
Proxelar currently emphasizes interactive intercept/edit, replay, Lua request/response hooks, WebSocket frame inspection, and an embeddable proxyapi core. proxyfor currently has stronger export-oriented ergonomics.
Choose proxyfor if export and a simpler capture workflow are the main requirement. Choose Proxelar if traffic transformation and scripting are central.
Burp Suite and Caido
Burp Suite and Caido are security testing platforms. They are built for manual web security testing, scanning, collaboration, history management, and security-oriented workflows.
Proxelar is not a security suite. It can help inspect and modify traffic, but it does not provide scanners, project collaboration, vulnerability workflows, or the same depth of manual testing tools.
Choose Burp or Caido for professional web security testing. Choose Proxelar for local development debugging and scriptable traffic transforms.
Charles, Proxyman, and HTTP Toolkit
These tools focus on polished desktop inspection workflows. They are often easier for GUI-first app debugging, especially when users want a desktop product rather than a terminal tool.
Proxelar is CLI-first and open source. Its interface is practical rather than desktop-polished, and its strongest workflows are scriptability, terminal use, and local proxy automation.
Choose a desktop proxy when UI polish and app onboarding matter most. Choose Proxelar when you want a terminal-friendly tool you can script and run in development environments.