Zitto

Rotating vs Sticky Proxy Sessions: When to Rotate, When to Hold the IP

By Theo Marsh · Infrastructure & Proxies Writer · 2026-10-27 · 8 min read

Quick answer: Rotate when each request is independent (one-off price checks, SERP snapshots across cities). Go sticky when requests belong to one flow (a login, a checkout test, a multi-page journey). If the flow lasts longer than your provider's sticky limit, use a static ISP IP.

Disclosure: Zitto sells proxies. These patterns work with any provider.

The rule in one line

One logical user journey = one IP. Everything else follows from that.

How session control usually works

Most providers encode options in the proxy username:

user-<account>-country-de-city-berlin-session-a1b2c3-sesstime-10:<password>@gate.example:7000
  • Same session- value → same exit IP until it expires
  • New value → new IP
  • No session value → rotates on every request

When to rotate

  • Independent GETs across many locations
  • Spreading load from a high-volume monitoring job so you stay within each site's rate limits
  • Geo sampling ("what does this page show in 50 cities?")

When to stick

  • Login → navigate → act flows on your own staging app
  • Checkout and localisation QA (currency, tax, consent banners)
  • Anything with cookies, CSRF tokens or server-side sessions

When to use static ISP

  • Sessions over ~30 minutes
  • Allowlisting a fixed IP on your own firewall or a partner API
  • Dashboards you keep open all day

Comparison

Per-request rotationSticky sessionStatic ISP
IP lifetime1 requestMinutes (plan-dependent)Indefinite
Best forIndependent samplingMulti-step flowsLong sessions, allowlists
Breaks if…Flow needs stateSession expires mid-flowRarely
BillingUsually per GBUsually per GBUsually per IP

Gotchas

  • Session expiry mid-test: set the session time longer than your slowest flow, or switch to static.
  • Peer drop-off: residential exit devices can go offline, so sticky ≠ guaranteed. Handle reconnects.
  • Parallelism: 20 parallel workers sharing one session ID all hit one IP. Give each worker its own ID.
  • Polite rates: rotation isn't a licence to hammer a site. Respect robots.txt and the site's terms.

In practice: a Python pattern

import uuid, requests
def session_proxy(country="de"):
    sid = uuid.uuid4.hex[:8]
    url = f"http://user-ACC-country-{country}-session-{sid}:PASS@gate.example:7000"
    return {"http": url, "https": url}

s = requests.Session; s.proxies = session_proxy
s.get("https://staging.yourapp.example/login")  # same IP for the whole flow

Decision matrix by workload

WorkloadSession typeSession lengthWhy
SERP rank snapshot, 50 citiesRotate per request—Each query is independent
Price check, 1 page per productRotate per request—Independent pages
Category crawl, paginatedSticky5–10 minPagination cookies, consistent results
Staging login + 10-step flowSticky≥ 2× flow timeSession cookies, CSRF
Checkout/localisation QASticky10–30 minCart state, geo consistency
Ad verification on a landing pageSticky2–5 minRedirect chains must share one IP
Admin dashboard all dayStatic ISPIndefiniteStable allowlisted IP
Partner API with IP allowlistStatic ISP / DCIndefiniteFixed IP required

The worker-pool pattern

For parallel jobs, give each worker its own session and recycle it after N requests or T minutes:

import uuid, time, requests

class Worker:
    def __init__(self, country, max_reqs=50, max_age=600):
        self.country, self.max_reqs, self.max_age = country, max_reqs, max_age
        self._new
    def _new(self):
        sid = uuid.uuid4.hex[:8]
        url = f"http://user-ACC-country-{self.country}-session-{sid}:PASS@gate.example:7000"
        self.s = requests.Session; self.s.proxies = {"http": url, "https": url}
        self.n, self.t0 = 0, time.time
    def get(self, url, **kw):
        if self.n >= self.max_reqs or time.time - self.t0 > self.max_age:
            self._new
        self.n += 1
        r = self.s.get(url, timeout=30, **kw)
        if r.status_code in (407, 502, 503):
            self._new  # exit died or auth issue: fresh session
        return r

Why recycle? Residential exits go offline unpredictably. Planned rotation at safe points beats an unplanned IP change in the middle of a flow.

Handling a session that dies mid-flow

  1. Detect: connection errors, 502/503 from the gateway, or a sudden change in the exit IP (check with an echo endpoint).
  2. Decide: if the flow is idempotent, restart it on a new session. If not (e.g. checkout QA), mark the run as invalid. Never carry on half-way through on a new IP.
  3. Log: session ID, exit IP, step number. Patterns show up quickly (some cities or ASNs are less stable).

Measuring session stability yourself

Run 100 sticky sessions at your target length and ping an echo endpoint every 30 seconds:

MetricHealthy target (rule of thumb)
Sessions keeping the same IP the whole way> 90%
Median time before an unplanned change> your session length
Reconnect success> 98%

If you can't reach these, switch that workload to static ISP proxies.

Rate limiting and politeness

Rotation spreads load, but it doesn't change what's acceptable. For any target that isn't yours:

  • Read and respect robots.txt and the terms of service
  • Keep request rates modest and add jitter
  • Identify yourself in the User-Agent where appropriate
  • Prefer official APIs and data feeds
  • Cache aggressively

For your own systems, add your proxy ranges or session pattern to your WAF allowlist so QA traffic isn't blocked by your own defences.

Cost implications

Sticky and rotating sessions usually cost the same per GB on residential plans. The costs that really differ:

  • Retries from dead sessions use extra bandwidth
  • Static ISP is per IP: cheap for one long session, expensive for wide geo-coverage
  • Mobile sticky sessions may be limited by modem rotation intervals
<!-- deepened -->

Configuration recipes for common tools

curl

# Rotating (new IP per request)
curl -x "http://user-ACC-country-us:PASS@gate.example:7000" https://ipinfo.io/ip
# Sticky (same IP for the session ID)
curl -x "http://user-ACC-country-us-session-qa42:PASS@gate.example:7000" https://ipinfo.io/ip

Playwright (Node)

const browser = await chromium.launch({
  proxy: { server: "http://gate.example:7000",
           username: "user-ACC-country-de-session-run17", password: "PASS" }
});

One browser context = one session ID. Create a new context with a new ID for each test journey.

Scrapy

Set the proxy per request in request.meta["proxy"] and keep a session ID per spider "slot" so paginated crawls stay consistent.

Parameter syntax varies by provider.

Choosing session length

Use this formula as a starting point:

session length = (slowest expected flow duration × 2) + 1 minute

A login → checkout test that takes up to 3 minutes gets a 7-minute session. Too short breaks flows. Too long ties a single IP to unrelated jobs if you reuse IDs by mistake.

Observability: what to log

For every request:

  • Timestamp, worker ID, session ID
  • Exit IP (from a periodic echo check), country/city requested vs observed
  • Status code, latency, bytes transferred
  • Retry count

With these you can answer the important questions: which cities are unstable, how much bandwidth goes to retries, and whether a provider's pool got worse this week.

Mobile proxies: special rules

Mobile proxy "rotation" often means the modem reconnecting to the carrier to get a new IP, which takes seconds and may be rate-limited. Patterns:

  • Rotation on demand: call an API endpoint to rotate between journeys
  • Timed rotation: e.g. every 5–15 minutes
  • Sticky by default: because carrier IPs are shared by many real users (CGNAT), a single mobile IP is less "unique" than a residential one

Design tests around rotation windows. Don't rotate in the middle of a journey.

Troubleshooting quick reference

SymptomLikely causeFix
Logged out mid-flowIP changedSticky session; longer duration
407 Proxy Auth RequiredWrong credentials/parametersCheck the username format
Wrong countryPool shortage in that geoBroaden to region or try later
Slow first byteDistant exit or overloaded peerCity targeting closer to the target; new session
Many 403s from your own WAFYour defences blocking QAAllowlist test sessions
<!-- deepened2 -->

Budgeting by session type

Different session types change cost in different ways:

ModelCost driverExample estimate
Rotating residential (per GB)Total bandwidth5 GB/day × 30 × price/GB
Sticky residential (per GB)Bandwidth + retries from dropped sessionsAdd 5–15% retry overhead (measure yours)
Static ISP (per IP)Number of IPs × months10 IPs × monthly price
Mobile (per day/period)Days of access, sometimes plus bandwidthZitto states from $0.60/day [CLIENT-PROVIDED: confirm bandwidth limits and terms]

To compare a per-day mobile price with per-GB offers, estimate your daily GB, then work out each provider's cost for your workload. Don't compare headline units directly. Any "cheapest" claim needs dated competitor prices for the same workload.

<!-- deepened3 -->

Bottom line

Rotate for independent samples, stick for flows, and go static for long sessions. Match the IP lifetime to the length of the journey.

FAQ

Sticky length? Plan-dependent, typically minutes. Static ISP keeps the IP indefinitely. Is per-request rotation more natural? No. Real users keep the same IP for a whole visit. How to control it? With a session ID in the username.

FAQ

How long does a sticky session last?
It depends on the provider and plan. Common options range from a few minutes to around 30 minutes or more. Static ISP proxies keep the IP indefinitely.
Does rotating every request look more natural?
No. A real user keeps the same IP throughout a visit. Rotating mid-flow breaks logins, carts and CSRF tokens on your own test sites.
How do I control the session?
Most providers put a session ID in the proxy username. Same ID = same IP until it expires.

Keep reading