Hello everyone.
Proxy configurations are fundamentally static. You define rules, assign policies, and the engine follows them. But what if you need dynamic behavior — inspecting an HTTP request body, modifying headers based on real-time conditions, or deciding routing based on the Wi-Fi SSID you are currently connected to?
Today we are introducing Chute’s JavaScript scripting engine, which brings programmable logic to every layer of the proxy stack.
Why JavaScript?
We could have invented a domain-specific language. Many proxy tools do. But a DSL means users have to learn yet another syntax, yet another standard library, and yet another set of limitations. JavaScript needs no introduction — every developer already knows it. By embedding a JavaScript runtime, we give users the full power of a general-purpose language for their proxy logic.
The engine is built on QuickJS, a small, embeddable JavaScript engine that compiles to a few hundred kilobytes. QuickJS supports the ES2020 specification, including modern features like arrow functions, template literals, async/await, and modules. It runs in a sandboxed environment with no access to the file system or network — it can only see the data we explicitly pass in.
What You Can Script
We expose seven script hooks, each triggered at a different point in the proxy pipeline:
http-request
Runs when a request is about to be forwarded. Your script receives the full request object — URL, method, headers, and body — and can modify any of them before the request leaves your device:
1 | if ($request.url.includes('analytics')) { |
http-response
Runs when a response is received but before it is delivered to the application. Your script can inspect the response body, modify headers, or replace the entire response:
1 | const body = JSON.parse($response.body); |
rule
Runs during rule evaluation. Your script receives the connection metadata — domain, source IP, destination port — and returns a routing decision. This allows routing based on logic that cannot be expressed in static rules.
dns
Runs during DNS resolution. Your script can override the normal DNS result, inject custom mappings, or reject certain domains without performing a real DNS lookup.
generic
Runs at configurable intervals, like a cron job. Useful for periodic tasks such as fetching updated blocklists or rotating credentials.
cron
A simpler, cron-like timer trigger. Define a schedule expression and the script runs on that cadence.
dns-ttl
Runs when a DNS cache entry expires, giving your script the opportunity to refresh or expire entries based on custom logic.
A Real Example: Wi-Fi-Aware Routing
Here is a script that routes traffic differently depending on which Wi-Fi network you are on:
1 | const ssid = $network.wifi.ssid; |
Place this in the rule script hook, and your routing adapts automatically as you move between networks.
Script API
The full API provides access to:
$request— URL, method, headers, body$response— status, headers, body$network— current Wi-Fi SSID, interface information$persistentStore— key-value storage that survives restarts$notification— post notifications to the user
The API is designed to match the Surge scripting convention where possible, so scripts written for other platforms should be portable with minimal changes.
Performance
JavaScript in a proxy path sounds expensive, but QuickJS is remarkably fast. For http-request and http-response scripts, the overhead is typically under 0.2ms per invocation. Scripts are pre-compiled at load time, and the runtime reuses a warmed-up engine instance across invocations.
We also added bodyBytes support — scripts can now access raw response bytes without encoding conversion — and automatic unwrapping of {response: ...} objects in http-response scripts, matching community conventions.
Give it a try. Write a script, drop it in your configuration, and watch your proxy come alive. If you build something useful, share it — we would love to see what the community creates.
Thanks.
Chute Devs