How to Use a Proxy for Only One Application
Route one browser, scraper or CLI tool through a proxy while the rest of the machine stays direct: app settings, launch flags, env vars, proxychains, Proxifier.
To use a proxy for only one application, you have to stop thinking about "the proxy setting" and start asking how that one program opens its sockets. A system-wide proxy is easy to switch on, but it drags your mail client, your updater and your video calls through the same exit IP. This guide ranks five per-app methods from the gentlest to the most forceful, gives a working SparkProxy example for each, and ends every one with a leak check you can run in under a minute.
Pick the Lightest Method That Reaches Your App
Every method below works by putting the proxy address somewhere that only one program reads. The further down the list you go, the less the app has to cooperate, and the more ways the setup can fail quietly. Start at the top and only move down when the app gives you no hook at that level.
| Method | Scope | App must cooperate? | Can carry user:pass? | Platforms |
|---|---|---|---|---|
| 1. App's own setting | That app, or one session object in code | Yes, needs a proxy field or API | Usually yes | All |
| 2. Launch flag | One launched instance | Yes, must parse the flag | Chromium and Electron: no | All |
| 3. Per-process env vars | One process and its children | Yes, must read the variables | Yes, in the URL | All |
| 4. proxychains-ng | One process tree, hooked at libc | No, but must be dynamically linked | Yes, in the config | Linux, BSD, macOS |
| 5. Proxifier rules | Matched executables, system driver | No | Yes, in the profile | Windows, macOS |
Two rules hold across the whole table. First, a method that the app ignores fails open: the app connects direct and nothing tells you. Second, credentials are the most common blocker at levels 1 to 3, because several popular runtimes cannot send a proxy password from a flag. Plan the auth choice before you pick the method.
The SparkProxy Endpoint, Ports and Auth Choice
All examples use the SparkProxy gateway at gateway.sparkproxy.io. We confirmed on 2026-10-30 that the name resolves and that all three ports accept TCP connections.
| Port | Protocol | Rotation | Good fit for one app that... |
|---|---|---|---|
| 11000 | HTTP/HTTPS | Random Rotation, IP changes between requests | fetches independent pages, checks prices, runs a crawler |
| 11002 | HTTP/HTTPS | Sticky Sessions, same IP for 5 minutes | logs in, keeps a cart, pages through results |
| 13000 | SOCKS5 | as configured on your account | needs non-HTTP TCP, or a tool that only speaks SOCKS |
If you're unsure which rotation mode your app needs, our explainer on sticky session proxies walks through the login and cart cases.
SparkProxy accepts both IP whitelist and user:pass authentication. For per-app routing, the IP whitelist removes a whole class of failures: Chrome, Electron apps and Java's HTTPS client can all reach the gateway, then stall on a 407 because they won't send credentials from the place you configured them. Whitelisting your machine's public IP in the dashboard means the gateway never asks. The trade-off is that every program on a whitelisted machine could use the gateway if pointed at it, which is fine on your own workstation and worth thinking about on a shared server. How proxy authentication works covers both schemes in depth.
Cost doesn't change with the method. SparkProxy plans are packs with unlimited bandwidth, starting from $75/month (USD, read from the live pricing page on 2026-10-09), so routing a heavy browser through the gateway costs the same as routing a tiny CLI tool. What you're buying is the plan and its concurrency, not gigabytes. A 24-hour free trial with no credit card is enough to run every leak check in this guide.
Scraping at scale? Skip the blocks.
Fast, unblockable datacentre proxies with unlimited bandwidth.
Method 1: The App's Own Proxy Setting
If the program has a proxy field, use it. It's the only method where the app knows it's being proxied and can handle auth prompts, retries and DNS itself.
Desktop apps often bury the field under Network or Connection settings. Firefox is the useful exception among browsers: it keeps its own proxy configuration per profile instead of borrowing the operating system's. Create a separate profile with firefox -P and give only that profile the proxy; your everyday Firefox stays direct. The full walkthrough, including the "Proxy DNS when using SOCKS v5" box, is in our Chrome and Firefox proxy guide.
For your own code, "the app's setting" means configuring the HTTP client object rather than the process. That keeps the proxy inside one session, so a second client in the same script can stay direct:
import os
import requests
PROXY = "http://{u}:{p}@gateway.sparkproxy.io:11002".format(
u=os.environ["SPARK_USER"], p=os.environ["SPARK_PASS"]
)
proxied = requests.Session()
proxied.proxies = {"http": PROXY, "https": PROXY}
direct = requests.Session()
direct.trust_env = False # ignore any HTTP(S)_PROXY inherited from the shell
print("via proxy:", proxied.get("https://api.ipify.org", timeout=20).text)
print("direct: ", direct.get("https://api.ipify.org", timeout=20).text)
Setting trust_env = False on the direct session matters. Requests reads http_proxy, https_proxy, no_proxy and all_proxy (and their uppercase forms) by default, according to its advanced-usage docs, so a variable left in your shell can silently proxy the session you meant to keep clean.
Browser automation follows the same idea. Playwright accepts a proxy at launch or per browser context, and its network docs state you can "optionally specify username and password for HTTP(S) proxy". That's the cleanest way to give one automated Chromium instance an authenticated proxy without touching the rest of the machine:
import os
from playwright.sync_api import sync_playwright
with sync_playwright() as pw:
browser = pw.chromium.launch()
proxied = browser.new_context(proxy={
"server": "http://gateway.sparkproxy.io:11000",
"username": os.environ["SPARK_USER"],
"password": os.environ["SPARK_PASS"],
})
page = proxied.new_page()
page.goto("https://api.ipify.org")
print("context exit IP:", page.inner_text("body"))
browser.close()
Command-line tools usually have a one-shot flag that applies to a single invocation and writes nothing to disk. Prefer these over editing a global config file:
# one git command through the proxy, global config untouched
git -c http.proxy=http://USER:PASS@gateway.sparkproxy.io:11000 fetch origin
# one pip install
pip install --proxy http://USER:PASS@gateway.sparkproxy.io:11000 requests
# one curl call over SOCKS5, with DNS resolved at the proxy (socks5h)
curl -x socks5h://USER:PASS@gateway.sparkproxy.io:13000 https://api.ipify.org
Leak check for method 1: print the exit IP from inside the app (the snippets above already do), then run the same request from a second, unproxied client and confirm the two IPs differ. Our Windows and macOS setup guides list where persistent per-tool config files live if you later want them permanent; here, one-shot flags keep the blast radius to a single command.
Method 2: Launch Flags for Browsers, Electron and Java
Some programs have no settings screen for a proxy but read one from the command line when they start. The flag lives only as long as that process.
Chrome and Edge take --proxy-server. Pair it with a separate --user-data-dir; without one, an already-running Chrome tends to open the new window itself and the flag doesn't apply. Our browser guide has the per-OS commands, so here are only the two facts from the Chromium proxy documentation that decide whether per-app routing works:
- Chrome "will not use any credentials embedded in the proxy settings", and "No authentication methods are supported for SOCKSv5 in Chrome". With SparkProxy, that means IP whitelist for the SOCKS5 port, or the HTTP port plus an auth prompt.
- If the proxy is unreachable, requests "fail with
ERR_PROXY_CONNECTION_FAILED", unless you append,direct://to the flag. Don't append it. Failing closed is exactly what you want when the point is that this one browser never shows your real IP.
Electron apps are Chromium underneath, and many accept the same switch. Electron's own command-line reference says --proxy-server "overrides the system setting" and that "The proxy URL does not support username and password authentication". VS Code documents the flag directly:
# VS Code through the rotating port; everything else on the machine stays direct
code --proxy-server="http://gateway.sparkproxy.io:11000" --proxy-bypass-list="localhost;127.0.0.1"
VS Code's network docs add that "SOCKS5 proxy authentication support isn't implemented yet", the same limit as Chrome. Whitelist your IP before you launch.
Java programs read proxy system properties passed with -D. Oracle's networking-properties reference lists http.proxyHost, https.proxyHost and socksProxyHost, each with a matching port property:
java -Dhttps.proxyHost=gateway.sparkproxy.io -Dhttps.proxyPort=11000 \
-Dhttp.proxyHost=gateway.sparkproxy.io -Dhttp.proxyPort=11000 \
-jar crawler.jar
Here is the trap almost nobody mentions. The JDK's default net.properties contains jdk.http.auth.tunneling.disabledSchemes=Basic, which disables Basic auth when tunnelling HTTPS through an HTTP proxy with CONNECT. A user:pass proxy will therefore reject every HTTPS request from a stock JVM, and the source throws a message of the form Unable to tunnel through proxy. Proxy returns "HTTP/1.1 407 Proxy Authentication Required". You can re-enable Basic with -Djdk.http.auth.tunneling.disabledSchemes="" and an Authenticator in code, but if you can't modify the jar, IP whitelisting is the fix that needs no code.
Leak check for method 2: launch with the flag pointed at a dead port such as http://127.0.0.1:9. If the app still loads pages, it is ignoring the flag (or another instance handled the launch). Then switch to the real gateway and compare exit IPs.
Method 3: Environment Variables Scoped to One Process
HTTP_PROXY, HTTPS_PROXY, ALL_PROXY and NO_PROXY are a convention, not a standard, and each runtime picks which of them it honours. The per-app trick is scope: set them for one command and its children, never for the whole user or machine.
# Linux/macOS: the variables exist only for this one process
HTTPS_PROXY=http://USER:PASS@gateway.sparkproxy.io:11002 \
HTTP_PROXY=http://USER:PASS@gateway.sparkproxy.io:11002 \
python scraper.py
# PowerShell 7.4+: override variables for one child process only
Start-Process python -ArgumentList 'scraper.py' -NoNewWindow -Wait -Environment @{
HTTPS_PROXY = 'http://USER:PASS@gateway.sparkproxy.io:11002'
HTTP_PROXY = 'http://USER:PASS@gateway.sparkproxy.io:11002'
}
:: cmd.exe: the variable dies with this inner shell (no space before &&)
cmd /c "set HTTPS_PROXY=http://USER:PASS@gateway.sparkproxy.io:11002&& python scraper.py"
Microsoft's Start-Process reference notes the -Environment parameter "was added in PowerShell 7.4". On Windows PowerShell 5.1, use the cmd /c form. If you need machine-wide variables on Windows instead, that's a different job, and the Windows datacenter proxy setup guide covers it.
Which programs actually read these? This is where per-app routing most often leaks:
| Runtime | Reads HTTPS_PROXY by default? | Source or measurement |
|---|---|---|
| curl | Yes; `http_proxy` lowercase only | curl man page, "Environment" section |
| Python requests and urllib | Yes, lower and upper case | Requests advanced docs; urllib measured below |
| Go `net/http` default transport | Yes, except requests to `localhost` | Go `ProxyFromEnvironment` docs |
| Node.js `fetch` and `https` | **No**, unless `NODE_USE_ENV_PROXY=1` or `--use-env-proxy` | Node 24 CLI docs; measured below |
| Java | No, uses `-D` properties | Oracle networking properties |
| Chrome, Electron | No, uses flags or system settings | Chromium proxy docs |
We measured the two riskiest rows on Windows Server 2022 (build 20348) on 2026-10-30, using HTTPS_PROXY=http://127.0.0.1:9, a port with nothing listening. A client that honours the variable must fail; a client that ignores it connects direct.
- Python 3.14.2
urllib.requestfailed with[WinError 10061] No connection could be made because the target machine actively refused it. It honoured the variable. - curl 8.15.0 failed with
curl: (7) Failed to connect to 127.0.0.1 port 9 after 2000 ms: Could not connect to server. It honoured it too. - Node.js v24.22.0
fetchreturned our server's real public IP. It ignored the variable completely. - The same Node call with
NODE_USE_ENV_PROXY=1failed withECONNREFUSED, as it should.
That Node result is the single most common silent leak in "I set HTTPS_PROXY for my scraper" setups. The Node 24 docs list NODE_USE_ENV_PROXY as added in v24.0.0 and --use-env-proxy in v24.5.0, both at Stability 1.1 (Active Development), so on older Node versions you need an agent library instead.
# Node 24: opt in, or the variables are decoration
NODE_USE_ENV_PROXY=1 HTTPS_PROXY=http://USER:PASS@gateway.sparkproxy.io:11000 node crawl.mjs
Leak check for method 3: run the dead-port test above against your actual program before you trust it. One command, and it tells you whether the variable is real for that runtime.
Method 4: proxychains-ng on Linux and macOS
When the program has no proxy setting, ignores flags and doesn't read environment variables, proxychains-ng forces it. Its README describes it as hooking "network-related libc functions in DYNAMICALLY LINKED programs via a preloaded DLL (dlsym(), LD_PRELOAD)". The project is active: the latest tagged release is v4.17 (published 2024-01-21) and the most recent commit on the default branch is dated 2026-10-06, both read from GitHub on 2026-10-30.
The config's [ProxyList] comment says "only numeric ipv4 addresses are valid" (the changelog allows hostnames only "under specific circumstances"), so resolve the gateway first and write the IP into a per-app config file:
GW_IP=$(getent hosts gateway.sparkproxy.io | awk '{print $1; exit}')
mkdir -p ~/.proxychains
cat > ~/.proxychains/spark.conf <<EOF
strict_chain
proxy_dns
tcp_read_time_out 15000
tcp_connect_time_out 8000
[ProxyList]
socks5 $GW_IP 13000 USER PASS
EOF
# only this process tree goes through the proxy
proxychains4 -f ~/.proxychains/spark.conf ./legacy-tool --fetch-all
On macOS, swap getent hosts for dig +short gateway.sparkproxy.io | head -1. Keep strict_chain with a single proxy: if the gateway is unreachable the connection fails instead of falling through. proxy_dns sends name lookups through the proxy so the app doesn't query your local resolver for every target.
Know the limits before you blame the proxy. All of these come from the proxychains-ng README:
- TCP only. "It supports TCP only (no UDP/ICMP etc)." Anything the app sends over UDP, including QUIC, goes direct or fails.
- Dynamic binaries only. Statically linked programs, which includes most Go binaries, never load the preload library. For those, graftcp traces socket syscalls with
ptrace(2)instead and its README says "statically linked binaries such as most Go programs can be traced as well". It's Linux-only; v0.8.3 was released 2026-07-15 and the last commit is dated 2026-08-25. - macOS SIP. System Integrity Protection "prevents hooking of system apps". The README's workaround is to copy the system binary into your home directory and run that copy; a Homebrew-installed tool outside
/usr/binalso works, and neither requires disabling SIP. - Scripting languages. The README warns that a glibc behaviour stops
dlopen()ed modules from being hooked, which "mainly affects scripting languages such as perl or python". For Python, use method 1 or 3; they're more reliable anyway.
Leak check for method 4: run proxychains4 -f ~/.proxychains/spark.conf curl -s https://api.ipify.org. With quiet mode off, proxychains prints a line starting [proxychains] Strict chain that ends in OK or timeout, and curl prints the exit IP. Then run your real tool and watch for the same chain lines on each connection. A tool that prints no chain lines at all isn't being hooked.
Method 5: Rule-Based Routing With Proxifier on Windows
Windows has no built-in per-application proxy, and LD_PRELOAD has no direct Windows equivalent. A rule-based proxifier fills that gap by intercepting connections at the network layer and matching them to rules. Proxifier is the long-standing commercial option: the vendor's download page lists Windows v4.14 (2025-04-23) and macOS v3.15 (2025-09-18, macOS 12 to 26). The buy page prices the Standard, Portable and Mac editions at $39.95 USD each, as a perpetual license with free minor updates and a 31-day trial (read 2026-10-30).
The setup that keeps everything else direct takes four steps:
- Add the proxy. Profile, Proxy Servers, Add:
gateway.sparkproxy.io, port13000, SOCKS Version 5, with your username and password. Proxifier sends SOCKS5 auth itself, so unlike Chrome you don't need a whitelist here. - Leave the Default rule on Direct. The docs call Default the rule Proxifier "uses when no other rule matches the connection". Direct here is what keeps the machine unproxied.
- Add one rule above it. Applications:
scraper.exe; "My Tool.exe", Action: the SparkProxy proxy. The rules page says application lists accept wildcards, and "It is possible to specify a process ID using a notation such as pid=1234", handy for one instance of a program that runs several times. Rules are scanned "from top to bottom, so the order of rules is important". - Keep the Localhost rule enabled. The docs warn that some applications, "such as Firefox, may depend on loopback connections".
Then fix DNS, because the default isn't what you want for a no-leak setup. Proxifier's Name Resolution page says the default "Detect DNS settings automatically" mode only enables "Resolve hostnames through proxy" when system DNS is unavailable. On a normal network, your proxied app's lookups still go to your local resolver. Turn automatic mode off and enable proxy resolution for strict isolation, accepting the documented cost that the app sees placeholder IPs "such as 127.8.." and IP-based rules stop matching.
Two more settings from the changelog are worth switching on. Version 4.11 added "Proxifier can now log and block UDP connections" and a master "DNS and IP Leak Prevention Mode" (Profile, Advanced). Blocking UDP for the proxied app means a browser's QUIC attempts fail, and browsers normally fall back to TCP, which the SOCKS5 rule can carry.
If you also run a VPN client or a local relay, read the "Connection Loop Problem" page first. A proxifier that captures the relay's own outbound traffic will loop until the machine runs out of connections; the fix is a Direct rule for the relay executable above everything else.
Leak check for method 5: open the Proxifier log, start your app and confirm each connection line names your app and the SparkProxy proxy. Then open a browser that isn't in the rule and check its IP: it should still be your own.
One Leak Check That Works for Every Method
Exit-IP checks catch the obvious failure. They don't catch DNS or UDP sneaking out beside a working TCP proxy. Run all three tests, in this order, every time you change a method:
# 1. Fail-closed test: point the app at a dead proxy. Success here = the app ignores your setting.
HTTPS_PROXY=http://127.0.0.1:9 your-app --one-request
# 2. Exit IP from inside the app vs. from an unproxied shell
curl -s https://api.ipify.org # your real IP
curl -s -x http://USER:PASS@gateway.sparkproxy.io:11000 https://api.ipify.org # a proxy IP
# 3. Rotation sanity: three calls on 11000 should usually differ, three on 11002 should match
for i in 1 2 3; do curl -s -x http://USER:PASS@gateway.sparkproxy.io:11002 https://api.ipify.org; echo; done
For a browser, add two checks the shell can't do: load a DNS leak test page to see which resolvers answer, and check WebRTC, since it uses UDP and can bypass an HTTP proxy. Our DNS leak testing guide and the explainer on WebRTC leaks cover both. On macOS, the Mac proxy configuration guide shows how to confirm no system-wide proxy is set, which matters here: if one is, your "per-app" test is measuring the system setting, not your method.
For Scrapers: Skip Local Routing With the Scraping API
If the one application is a scraper you wrote, there's a way to avoid machine-level routing entirely: the scraper calls the SparkProxy Scraping API as an ordinary HTTPS endpoint, and the API fetches the target through SparkProxy's pool. Nothing on your machine needs a proxy setting, so nothing can leak, and the only outbound connection is to scrape.sparkproxy.io.
import base64
import os
import requests
API = "https://scrape.sparkproxy.io/api/v1"
HEADERS = {"X-API-Key": os.environ["SPARK_API_KEY"]}
def fetch(target, country=None):
params = {
"url": target,
"render_js": "false", # plain fetch, 1 credit (render_js defaults to true, 5 credits)
"proxy_type": "premium",
"json_response": "true", # the only way to get the envelope
}
if country:
params["country_code"] = country # +5 credits
r = requests.get(API, headers=HEADERS, params=params, timeout=60)
r.raise_for_status()
env = r.json()
html = base64.b64decode(env["body"]).decode("utf-8", "replace") # body is base64
return env["status_code"], env["credits_used"], html
status, credits, html = fetch("https://www.sparkproxy.io/")
print(status, credits, len(html))
Two rules from the API reference keep this correct. format only converts content (html, md, mdx, json, screenshot, pdf); it is not the envelope. json_response=true is the only source of job_id, status_code, duration_ms, credits_used and body, and that body is base64, so decode it before matching anything. Don't stack extract_rules with json_response, since extract_rules replaces the response with an extracted object. New accounts get 1,000 free credits, which is enough to validate a scraper before you decide between the API and raw proxies.
Error Strings and the Method Each One Points To
Every string below was either produced against the SparkProxy gateway on 2026-10-30 or quoted from the tool's own documentation or source.
| What you see | What it means | Fix |
|---|---|---|
| `curl: (56) CONNECT tunnel failed, response 407` | Gateway reached, no valid credentials and IP not whitelisted | Add user:pass to the proxy URL, or whitelist your IP |
| `HTTP/1.1 407 Proxy Authentication Required` (in `curl -v`) | Same 407, seen raw | Same |
| `curl: (97) cannot complete SOCKS5 connection to api.ipify.org. (2)` | SOCKS5 port reached, request refused: no auth sent and IP not whitelisted | Use `socks5h://USER:PASS@...:13000`, or whitelist |
| `ERR_PROXY_CONNECTION_FAILED` (Chrome) | Proxy unreachable; Chrome failed closed | Check host and port; don't add `,direct://` |
| `Unable to tunnel through proxy. Proxy returns "HTTP/1.1 407 ..."` (Java) | JVM won't send Basic auth over CONNECT by default | Whitelist your IP, or re-enable Basic plus an Authenticator |
| `proxychains: can't load process '...'.` | proxychains couldn't start the target | Check the path; the binary must exist and be executable |
| `[proxychains] Strict chain ... timeout` | Proxy in `[ProxyList]` didn't answer | Re-resolve the gateway IP; check the port is 13000 for socks5 |
| App works but shows your real IP | The method isn't reaching this app | Run the dead-port test; move one method down the ladder |
The last row is the one a proxy can't fix by itself. A 407 or a SOCKS refusal is an auth problem you solve in the dashboard. An app that ignores your setting is a routing problem, and the answer is a more forceful method, not a different proxy.
Frequently asked questions
FAQ
Use the app's own proxy field if it has one, launch flags for Chrome, Edge or Electron apps, or cmd /c "set HTTPS_PROXY=...&& app.exe" for tools that read environment variables. For programs with no hook at all, a rule-based tool like Proxifier routes only the executables you list and leaves the Default rule on Direct.
Yes. Launch Chrome with --proxy-server and a separate --user-data-dir, which applies only to that instance. Chrome won't send credentials from the flag and supports no SOCKS5 auth, so whitelist your IP or use the HTTP port and answer the auth prompt.
Node's built-in fetch and https modules don't read proxy variables by default. In our test, Node v24.22.0 returned the real IP with HTTPS_PROXY set; adding NODE_USE_ENV_PROXY=1 (Node 24 and later) made it use the proxy.
No. proxychains-ng hooks libc through LD_PRELOAD, so it only works on dynamically linked programs, carries TCP only, can't hook macOS system binaries under SIP, and is unreliable with Python and Perl modules. Statically linked Go binaries need a ptrace-based tool such as graftcp instead.
It can. HTTP and SOCKS proxies carry TCP, so UDP traffic such as WebRTC and QUIC may go direct, and some tools resolve DNS locally unless told otherwise. Use socks5h:// in curl and requests, proxy_dns in proxychains, proxy name resolution plus UDP blocking in Proxifier, then run a DNS and WebRTC leak test.
Use port 11000 (Random Rotation) for apps that make independent requests, such as crawlers and price checks. Use port 11002 (Sticky Sessions, same IP for 5 minutes) for anything that logs in or holds a session, and port 13000 when the app needs SOCKS5.
Get 20% off your first month
Premium datacentre proxies with unlimited bandwidth. Use the code at checkout.
Save up to 15% more on quarterly, half-yearly and yearly plans
Related articles
Sticky Session IP Changing Mid-Flow? Causes and Fixes
Sticky session IP changing mid-login or checkout? Check leaks, session ID format, the 5-minute window, parallel connections and exit churn in order.
httpx proxies Argument Removed? Fix ProxyError and 407
The httpx proxies argument is gone in 0.28. Fix the TypeError, then ProxyError 407, SOCKS ImportError and RemoteProtocolError, with verbatim errors and code.
npm config set proxy: Install Packages Behind a Proxy
npm config set proxy and https-proxy, the right .npmrc file, yarn and pnpm equivalents, noproxy for private registries, and keeping passwords out of your repo.
