๐ŸŽ‰ Premium Proxies ยท 3-Day Free TrialClaim Now โ†’
Proxy Basic

What Is an Upstream Proxy? Parent Proxy Explained

An upstream proxy is a proxy that forwards traffic to another proxy. Learn how upstream and parent proxies work, Squid and mitmproxy config, and real use cases.

S SparkProxy 2 11 min read
Share
What Is an Upstream Proxy? Parent Proxy Explained

An upstream proxy is a proxy that forwards its traffic to another proxy instead of connecting to the destination directly. If you have ever run a local Squid cache that hands requests off to a residential provider, or pointed mitmproxy through a corporate gateway, you have already used one. This guide explains what an upstream (parent) proxy is, how it differs from proxy chaining, the exact Squid and mitmproxy config, real use cases like caching plus rotation and centralized auth, and how to point an upstream at the SparkProxy gateway.

What Is an Upstream Proxy?

An upstream proxy is the next proxy on the path toward the internet. When one proxy is configured to relay everything it receives to a second proxy rather than fetching from the origin itself, that second proxy is its upstream. Squid calls it a parent proxy. Other stacks call it a cascading proxy, or plainly a proxy behind a proxy. The label changes, the mechanic does not: your first proxy never opens a socket to the target, it opens a socket to another proxy and lets that one reach the target.

The word upstream describes direction. Data flows upstream toward the origin server and downstream back toward the client, so the upstream proxy is whichever proxy sits closer to the destination. This is a role, not a product. Any forward proxy becomes an upstream the moment another proxy is pointed at it. In fact an upstream setup is just a forward proxy aimed at another forward proxy instead of at the open internet.

How an Upstream Proxy Works

Two proxies sit in line. The local proxy accepts connections from your apps, applies whatever local logic it runs (caching, header rewriting, TLS inspection, auth), then forwards the request to the upstream, which makes the real request to the target.

Upstream (parent) proxy flow

[Client apps] --> [Local proxy] --> [Upstream proxy] --> [target.com]
  browsers,        Squid or          SparkProxy           sees the
  scripts,         mitmproxy         gateway              upstream's
  services         caching, auth,    rotation,            exit IP
                   TLS handling      clean exit IP

The target sees only the upstream's exit IP, never your local proxy or your apps. That is the same masking a single forward proxy gives you, with an extra stage in front where you keep the logic you want to run yourself. A common split is a local proxy on your own network for caching and credentials, and a managed upstream for rotation and a clean exit address.

Protocol handling carries through both hops. For an HTTPS target the local proxy opens a CONNECT tunnel to the upstream, the upstream opens its own CONNECT to the destination, and TLS is negotiated end to end between your client and the target. Neither proxy can read an HTTPS body unless it performs TLS interception with a trusted root certificate. If you need a refresher on how HTTP, HTTPS, and SOCKS5 behave through a proxy, see understanding proxy protocols.

Free trial

Scraping at scale? Skip the blocks.

Fast, unblockable datacentre proxies with unlimited bandwidth.

Upstream Proxy vs Proxy Chaining

People treat these as the same thing. They overlap but describe different ideas. "Upstream" (or parent) is a role in a configuration: it names the next proxy a given proxy forwards to. "Proxy chaining" is a practice: deliberately stacking two or more proxies in sequence, usually for layered anonymity so no single hop sees both your identity and your target.

The relationship is nested. In proxy chaining, each hop's upstream is simply the next hop in the chain, so a chain is a series of upstream relationships. The difference is intent. You reach for the word upstream when you configure one proxy to hand off to a parent for a functional reason: caching, centralized auth, protocol or TLS handling, or a managed exit. You reach for chaining when the goal is spreading trust across several operators.

DimensionUpstream (parent) proxyProxy chaining
What it namesA role: one proxy forwards to a designated parentA practice: stack two or more proxies in sequence
Typical hopsOne local proxy plus one upstreamTwo or more, often three
Primary goalFunction: caching, auth, TLS handling, managed exitAnonymity: split trust so no hop sees the full path
Who configures itThe local proxy operator (Squid, mitmproxy)The client, per app or per request (ProxyChains)
Exit IPWhatever the upstream provides, often rotatingOne fixed exit IP, the last hop
Typical toolsSquid `cache_peer`, mitmproxy `--mode upstream`ProxyChains, Proxifier, Tor

If your goal is layered anonymity rather than a functional hand-off, read what is proxy chaining for the tradeoffs, because more hops multiply latency and failure without adding much once you pass two or three.

Configure an Upstream Proxy in Squid

Squid is the reference implementation. You declare an upstream with the cache_peer directive and force Squid to always use it with never_direct. Here Squid listens on port 3128 and forwards every request to the SparkProxy gateway as its parent:

# /etc/squid/squid.conf
http_port 3128

# Forward everything to the SparkProxy gateway as the parent (upstream)
cache_peer gateway.sparkproxy.io parent 8080 0 no-query default login=USERNAME:PASSWORD

# Never bypass the parent and connect direct
never_direct allow all

# Allow your LAN to use this proxy
acl localnet src 10.0.0.0/8 192.168.0.0/16
http_access allow localnet
http_access deny all

Reading the cache_peer line left to right: gateway.sparkproxy.io is the upstream host, parent is the peer type, 8080 is its HTTP port, 0 is the ICP port (unused here), no-query disables ICP probes, default marks it as the last-resort parent, and login=USERNAME:PASSWORD sends a Proxy-Authorization header to the upstream. Swap the host and port for the values in your dashboard. Ports are not fixed by any standard, so check them rather than assuming; proxy ports explained covers why 8080 and 3128 are common but not guaranteed.

Here is the detail most guides skip: the peer type matters. Squid accepts parent and sibling, and they behave very differently on a cache miss.

Peer typeOn a cache missFetches a miss from origin?Use it for
`parent`Forwards the request up the chainYes, through the parentA true upstream or egress proxy
`sibling`Only serves objects it already holdsNo, it cannot fetch a miss for youSharing a cache between peers at the same level

A sibling is not an upstream. Point at a sibling expecting it to reach the internet for you and every cache miss fails, because a sibling only answers with what it has already cached. For an egress upstream you always use parent. That single word is the difference between a working setup and a wall of TCP_MISS/504 errors.

Configure an Upstream Proxy in mitmproxy

When you want to inspect or rewrite traffic locally before it leaves, mitmproxy (10.x and 11.x) runs as the local proxy and forwards to an upstream in its dedicated upstream mode. This is the classic debugging setup: watch and modify requests on your machine, then relay them through a real egress proxy.

mitmdump --mode upstream:http://gateway.sparkproxy.io:8080 \
         --upstream-auth USERNAME:PASSWORD \
         --listen-port 8081

Point your app at http://127.0.0.1:8081 and every request flows through mitmproxy and then out through the upstream. --mode upstream:URL sets the parent, and --upstream-auth USER:PASS attaches the credentials the upstream expects. Use mitmweb for the browser UI or mitmproxy for the terminal UI if you want to watch flows live instead of running headless with mitmdump. For how those credentials are transmitted and why the format differs by protocol, see how proxy authentication works.

Upstream Proxy Use Cases

Running a local proxy in front of a provider gateway is extra machinery, so it should earn its place. Three cases repay the effort.

Caching in front of rotation. A local Squid caches repeated fetches and forwards only true misses to a rotating residential upstream. You cut bandwidth and latency on assets you request over and over, while live fetches still exit from fresh IPs. A corporate proxy forwarding to the internet is the same pattern at company scale: one caching layer, one egress upstream.

Centralized authentication. Store the provider credentials in exactly one place, the local proxy's cache_peer login= or mitmproxy's --upstream-auth. Your apps point at the local proxy with no secrets embedded, and when you rotate a password or swap providers you change one line instead of redeploying every service that scrapes.

Local inspection and transformation. mitmproxy as the local hop lets you log, rewrite headers, mock responses, or debug a scraper before traffic reaches the egress upstream. You get full visibility on your own machine while the upstream still supplies the exit IP and rotation.

Point an Upstream at the SparkProxy Gateway

In each config above, the SparkProxy gateway is the upstream. Your local proxy holds the credentials and any local logic, and it forwards to the gateway, which handles rotation, geo-targeting, and a clean exit IP. After starting Squid, confirm traffic actually goes through the local proxy and out the upstream:

curl -x http://127.0.0.1:3128 -s https://example.com -o /dev/null -w "%{http_code}\n"

If you would rather skip the local proxy entirely and let one endpoint handle the exit, rotation, rendering, and anti-detection, the SparkProxy Scraping API does that in a single request. Authenticate with the X-API-Key header and pass the target as the url parameter, documented at /docs/scraping-api:

import requests

resp = requests.get(
    "https://scrape.sparkproxy.io/api/v1",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={
        "url": "https://example.com",
        "render_js": "true",
        "premium_proxy": "true",   # residential exit
        "country_code": "US",      # geo-targeted exit
    },
    timeout=60,
)
print(resp.status_code)

If you still need a hop under your own control, for example a corporate egress that must sit in the path, the own_proxy parameter routes the fetch through a proxy you supply. That inserts your own upstream hop without you managing chain health or DNS routing by hand:

params = {
    "url": "https://example.com",
    "own_proxy": "http://user:pass@egress.sparkproxy.io:8080",
}

You keep the one real benefit of a controlled upstream, a hop you own, and let the API manage the exit.

Frequently asked questions

FAQ

An upstream proxy is a proxy that another proxy forwards its traffic to, instead of connecting to the destination directly. It sits closer to the internet on the path, so it is also called a parent proxy or a cascading proxy. The target sees the upstream's exit IP, never the local proxy or the client behind it.

Upstream (or parent) names a role: the next proxy a given proxy forwards to. Proxy chaining is the practice of stacking two or more proxies in sequence, usually for layered anonymity. A chain is really a series of upstream relationships, but you use the word upstream when the hand-off is functional (caching, auth, a managed exit) rather than for spreading trust across operators.

In Squid, a parent is a cache_peer that fetches requests up the chain on your behalf, including on a cache miss, so it acts as your upstream or egress proxy. It differs from a sibling, which only returns objects it already has cached and will not fetch a miss for you. For an egress upstream you always declare the peer as parent.

Run mitmproxy in upstream mode: mitmdump --mode upstream:http://HOST:PORT, adding --upstream-auth USER:PASS if the upstream needs credentials. mitmproxy then accepts connections locally, lets you inspect or rewrite them, and forwards everything to that upstream proxy. Point your application at mitmproxy's listen port.

Yes, and centralizing that auth is a common reason to run one. Squid sends credentials to the upstream with the login=USERNAME:PASSWORD option on cache_peer, and mitmproxy uses --upstream-auth USERNAME:PASSWORD. Your apps hold no secrets and point at the local proxy, so rotating the credential is a one-line change.

Yes. Cascading proxy, parent proxy, and proxy behind a proxy are all informal names for the same setup where one proxy forwards to another. They describe the upstream relationship from different angles, but the traffic path and the masking behavior are identical.

Limited-time ยท 50% off

Get 50% off your first purchase

Premium datacentre proxies with unlimited bandwidth. Use the code at checkout.

Offer ends soon โ€” claim it before it's gone

Claim Discount

About the Author

This guide was written by the SparkProxy Technical Team. SparkProxy operates residential, datacenter, and mobile proxy networks across 190+ countries, plus a managed Scraping API for teams that would rather send one request than run their own proxy infrastructure. We publish practical, engineer-to-engineer explainers on proxy architecture, authentication, and reliable large-scale data collection, grounded in how these systems behave in production. Start at sparkproxy.io.

Keep reading

Related articles

What Is a P2P Proxy Network?

What Is a P2P Proxy Network?

A P2P proxy network routes traffic through real consumer devices that opt in to share bandwidth. See how it differs from datacenter and ISP proxies, and vet it.

SparkProxyยทProxy Basic
What Are WebRTC Leaks and How to Prevent Them

What Are WebRTC Leaks and How to Prevent Them

A WebRTC leak reveals your real IP through STUN and ICE candidates even behind a proxy or VPN. Learn how it happens, how to test for a leak, and how to stop it.

SparkProxyยทProxy Basic
What Is a PAC File? Proxy Auto-Config Explained

What Is a PAC File? Proxy Auto-Config Explained

A PAC file is a JavaScript FindProxyForURL function that tells a browser which proxy to use per URL. Learn PAC syntax, helper functions, WPAD, and security.

SparkProxyยทProxy Basic