Stickiness gets a little more interesting once an upstream provider is involved. There are now two sticky-session systems in the path: Nodetonet's, which lives on our edge, and the upstream's, which lives on their gateway. Both do the same job — pin a stream of requests to a single exit IP for a while — but each has its own TTL, its own keying scheme, and its own definition of "rotate". If you do not line them up, you will see surprising behaviour: an IP that seems to drift mid-session, a fresh upstream dial on every request, or a session that never rotates when you want it to. This guide explains exactly what happens at each layer, how to configure them cleanly together, and what to check when something feels off.
If you are new to sticky sessions in general, start with sticky sessions without an upstream first, then come back here. If you have not yet set up an upstream at all, see upstream residential forwarding.
The two layers, in order
Every request through a tunnel that has an upstream travels through two sticky-session decisions, one after the other:
client
→ Nodetonet edge (layer 1 — picks which backend)
→ upstream gateway (layer 2 — picks which exit IP)
→ exit IP
→ target site
Layer 1 — Nodetonet's sticky map. The edge reads the X-Session header (or the session-tagged username, such as user-session-abc). It derives a stable key from that value and routes every request carrying the same key to the same backend connection. Without an upstream, that backend is a paired mobile device. With an upstream configured, the backend is the upstream gateway connection associated with that key.
Layer 2 — the upstream's sticky map. The upstream gateway reads its own session indicator — almost always a modifier embedded in the proxy username — and pins that session to a particular exit IP for its own TTL. Nodetonet passes the username through without modification, so any upstream username modifier you configured flows to the upstream unchanged.
Understanding this two-hop model is the key to everything else in this guide. Each layer is independently configurable, and the two TTLs are independent timers.
The clean case — let the upstream do all the work
If your only goal is "same exit IP for the next N minutes" and you trust the upstream's TTL to cover that window, the simplest approach is to pin the session entirely at the upstream layer and not use Nodetonet sticky at all:
# Example upstream string baked into the proxy config
gate.example-upstream.com:7000:user-acct-country-de-session-abc123:pass
Every request you send through the tunnel reaches the upstream with that same username. The upstream sees the same session token, gives you the same exit IP for its default TTL, and Nodetonet simply forwards bytes without any sticky bookkeeping. To rotate, update the session-abc123 part in the upstream string and save. The very next request gets a fresh exit.
This is the right approach for scripts and tools that control their own session state — scrape jobs that need a stable IP per run, or automation where a single operator manages the session lifecycle explicitly.
The dynamic case — per-client session via Nodetonet
What if you want multiple independent clients — each identified by its own session header — to be routed to different upstream exits simultaneously, without saving a separate proxy for each one? That is where both layers cooperate.
You send an X-Session: foo header on HTTP/HTTPS, or embed the session in the SOCKS5 auth username as uXXX-session-foo. Nodetonet's edge derives a stable key from that value and forwards to the upstream with a modified username that includes a per-session identifier mapped from your key. A different X-Session value on your side produces a different session token on the upstream side, which produces a different exit IP.
The mapping is deterministic and sticky: the same X-Session: foo always maps to the same upstream session token, so you reliably get the same exit IP for the lifetime of that mapping. Our default mapping TTL is 10 minutes — after that, the entry is dropped and the next request with the same X-Session starts fresh.
| Session approach | Who manages the session token | Best for | Rotation mechanism |
|---|---|---|---|
| Upstream-only (baked string) | You, statically in the proxy config | Single-threaded jobs, operator-controlled rotation | Edit upstream string, save proxy |
| Nodetonet X-Session (dynamic) | Your client, per request or per flow | Multi-client, per-flow stickiness without N proxies | Change X-Session value, or send X-Rotate: 1 |
| No session header | Neither — pure rotating | High-volume crawling, max IP diversity | Automatic per-request (or per upstream draw) |
TTL mismatch — what actually happens
The two TTLs are independent. Here is what to expect in each scenario:
- Nodetonet TTL expires first (default 10 min) but the upstream's is longer. The mapping on our side is dropped. If you send the same
X-Sessionagain, we create a new mapping. Because the upstream session token we derive is deterministic from your key, the upstream may still see the same token and give you the original exit. In practice: your IP often survives the Nodetonet TTL expiry if the upstream is long-lived. - Upstream TTL expires first. Even though your
X-Sessionvalue never changed, the upstream silently rotates to a new exit IP, because the upstream session it held internally expired. You get a different IP without doing anything explicit. - Both expire together (or you send
X-Rotate: 1). Both layers drop state simultaneously and the next request gets a completely fresh upstream draw and a fresh exit. This is the cleanest rotation.
The two TTLs do not have to match — but knowing which is shorter tells you who controls your rotation cadence. If you want predictable rotation, set an upstream session TTL that is shorter than ours (many upstream providers accept a TTL hint in the username).
When stickiness "breaks" — common gotchas
A few patterns that produce unexpected IP changes or unwanted stickiness:
- HTTP connection pools. Some clients reuse the same TCP connection across many requests. The sticky decision is made at connect time, so all requests on that connection share the same exit regardless of what session header you set mid-stream. Close and reopen the connection to switch sessions cleanly.
- Sending
X-Rotate: 1on the wrong request. This header drops both layers' state for that session and forces an immediate upstream re-dial. If you send it accidentally (e.g., from a retry loop), you will lose your pinned IP unexpectedly. - Pool exhaustion on the upstream side. If the upstream's pool for your requested region is depleted, your "sticky" session silently rotates because the original exit is no longer reachable. This is a condition at the upstream, not Nodetonet. You will see it as an IP change despite a stable session header. Pair with what-is-my-ip checks to detect it.
- Upstream credential errors mid-session. If the upstream returns a 407 for any reason (expired sub-account, rate limit), our edge drops the connection, which may reset the session mapping. See debugging upstream auth failures for how to isolate these.
- SOCKS5 session embedding. On SOCKS5, the session must be in the auth username (
uXXX-session-foo) because there are no HTTP headers. If your client caches the resolved SOCKS5 connection, the session is also cached at connect time.
Inspecting the actual exit IP
The fastest sanity check: hit a known IP-reflection endpoint several times with the same session header and compare results.
for i in 1 2 3; do
curl -sx http://u8x2:p7q1@sub42.nodetonet.com:48888 -H "X-Session: my-test-session" https://api.ipify.org && echo
done
If the IP stays constant across all three calls, both layers are stickily aligned. If it changes between calls 1 and 2, check whether the upstream TTL elapsed. If it changes between calls 2 and 3 but not 1-2, suspect pool exhaustion or a mid-stream connection pool reuse issue. You can also use our built-in proxy checker to inspect the resolved exit after each rotation.
Response latency is another signal: a noticeably slower first call after a gap usually means the upstream just dialled a new exit, indicating session expiry rather than continuity.
Rotation without an upstream: device-backed stickiness
Everything above assumes you have an upstream configured. If you are running Nodetonet in native mode — where the backend is a paired Android device rather than a third-party gateway — sticky sessions work the same way at layer 1, but layer 2 is the device itself (specifically its carrier-assigned IP). There is no second TTL to manage; the IP is stable for as long as the device holds its mobile assignment. See mobile proxies and when to use rotating mobile proxies for details on that mode.
Per-client controls that interact with sessions
If you expose your tunnel to end-customers using per-client credentials (user/pass auth, IP allowlist, per-client auth), note that sticky sessions are scoped to the upstream connection, not the client credential. Two different client credentials can produce the same upstream session key if they send identical X-Session values. This is usually fine, but if you need per-client isolation at the exit-IP level, use distinct session prefixes per client or separate proxies. For pools rather than individual proxies, token groups give you round-robin device selection with failover across a named pool.
Get started
Ready to try it? Create a free Nodetonet account, configure an upstream under your proxy settings, and run the curl loop above to confirm your two-layer sticky setup is working. For a full walkthrough of rotating vs sticky across all modes, see when to use rotating mobile proxies.