Backconnect proxy: one gateway, a new exit IP
A backconnect proxy is one gateway address that hands each request or session to a different exit IP. How it works, and how to confirm it in a few requests.
- Published November 10, 2026
- 8 min read
In this article
A backconnect proxy is one gateway address you connect to. The gateway picks the exit IP that the site on the other end sees, and the list of exits stays on its side. "Proxy backconnect" is the same thing with the words swapped.
You never see that list, so what you can check is narrow: does the address that comes back change between requests, or hold? A handful of requests and an IP echo service answer it.
Your client sees one host and one port
A plain proxy list is a list of machines. Each line points at one address, and when that address is refused or goes away, swapping the line is your job.
With a backconnect proxy the line stays put. Our API docs publish the format as host:port:username:password:
premium.resifactory.net:7777:ResiFactory_a3f9c142~us~1a2b3c4d:pass
premium.resifactory.net:7777:ResiFactory_a3f9c142~us~9f8e7d6c:pass
Those are the two example lines in the Output Formats section of that page. Same host, same port. The username changes: a prefix, the country (us), a session id. None of it is an exit address.
The session id does not hold an exit by itself. Both lines above carry one, and as written, with nothing after it, they rotate. A sticky line adds a fourth field to the username, the minutes, in the order user~country~sessionid~minutes, and that field turns the hold on. A session id with no minutes still rotates. The sibling post has the detail under the minutes field turns the hold on, not the session ID.
Port 7777 is the HTTP proxy port on every pool. SOCKS5 is on 1080, on every pool except Essential. For an https:// target your client doesn't hand the gateway the request. It sends a CONNECT for a host and port, which RFC 9110 §9.3.6 defines as a request to open a tunnel and then forward bytes both ways until the tunnel closes. Over the HTTP port the destination port has to be 80 or 443, and anything else comes back as a generic 502. SOCKS5, specified in RFC 1928, is the route for other ports.
Rotating and sticky, and how to ask for each
Your request arrives with the credentials in the username and password, the gateway picks an exit, and the site sees that address. You can't ask for a particular one. You ask for a country and a mode. If you've used a list before, that's the trade: you give up choosing the machine, and you stop maintaining the list.
The mode is the part you control. The API docs call rotating a new IP on every request. sticky holds one exit IP for a set number of minutes: requests sent with the same username and password pair exit from the same IP until the session expires, and the next request after that rotates to a new IP on its own.
You ask on POST /proxies in the API:
{ "pool": "<id from GET /pools>", "country": "us", "quantity": 10, "proxyType": "sticky", "sessionDuration": 30 }
pool is the id of the pool you bought, and GET /pools lists the ids your account can use. It matters more than it looks. Each pool has its own prepaid wallet, and if you leave pool out the batch is generated against Premium, or against Mobile for the two mobile types. Buy Essential, leave it out, and you've asked an empty wallet for lines.
proxyType is rotating (the default) or sticky; on the Mobile pool the values are mobile_rotating and mobile_sticky. sessionDuration is minutes, from 1 to 1,440. Leave it out on a sticky batch and it's 60. On a rotating batch it's ignored. On Essential a session holds at most 120 minutes, whatever you send.
Ten lines come back as ten session ids. On a sticky batch each line also carries the minutes, so those are ten separate held exits. On a rotating batch they are still ten ids with a new exit on every request. Which mode a job needs is covered in rotating or sticky: a run of requests that has to come from one IP, like a login or a multi-step form, wants sticky, and a list of independent fetches wants rotating.
A gateway you can run and count
The real check is twenty requests through your line to an IP echo service, a URL that replies with the address it saw you connect from, with the answers sorted and counted. The sibling post prints that loop under count the exits. Rotating should give more than one address. How many is up to the pool, so a repeat isn't a fault, but one address with a 20 beside it is. Sticky should give one address, 20 times, for as long as the session lasts.
We ran it on 2026-11-10 on one Premium line with the country set to us. As generated, with a session id and no minutes, the line returned 20 different addresses in 20 requests. With ~30 added to the same username, 20 requests returned one address.
To see what that shape looks like without a credential, here is the smallest gateway that has the idea in it. It is a toy and not ours. It has two exits only, because loopback gives a machine two source addresses to use, 127.0.0.1 and ::1, so it needs a machine with IPv6 loopback. Username demo rotates. Username demo~hold1 keeps the exit it was first given. The toy treats any text after the first ~ as a hold key, which is not our username format.
const http = require('http');
const EXITS = ['127.0.0.1', '::1'];
let next = 0;
const held = new Map();
// Echo server: replies with the address the connection came from.
http.createServer((req, res) => {
res.end(req.socket.remoteAddress.replace('::ffff:', '') + '\n');
}).listen(18081, '::');
// Gateway: one address in, a choice of exit behind it.
http.createServer((req, res) => {
const auth = (req.headers['proxy-authorization'] || '').replace('Basic ', '');
const user = Buffer.from(auth, 'base64').toString().split(':')[0];
const [, hold] = user.split('~');
let exit;
if (hold) {
if (!held.has(hold)) held.set(hold, EXITS[next++ % EXITS.length]);
exit = held.get(hold);
} else {
exit = EXITS[next++ % EXITS.length];
}
const u = new URL(req.url);
const out = http.request({ host: exit, port: u.port, path: u.pathname, localAddress: exit }, (r) => {
res.writeHead(r.statusCode);
r.pipe(res);
});
out.on('error', (e) => { res.writeHead(502); res.end(String(e.message)); });
out.end();
}).listen(18080, '127.0.0.1', () => console.log('ready'));
Save it as toy.js and start it with node toy.js. In a second terminal, send ten requests per user through the gateway at 127.0.0.1:18080 to the echo server at 127.0.0.1:18081. The toy doesn't handle CONNECT, so this one is plain http://:
for u in demo demo~hold1; do
echo "user: $u"
for i in $(seq 1 10); do
curl -s -x "http://$u:[email protected]:18080" http://127.0.0.1:18081/
done | sort | uniq -c | sort -rn
done
Run on 2026-11-10 with Node 26, it prints:
user: demo
5 ::1
5 127.0.0.1
user: demo~hold1
10 127.0.0.1
The client used one host and one port both times. The rotating user got both exits, five each. The held user got one exit ten times. Run demo first to get these numbers: a held user takes whichever exit the counter is on when it first appears, so a fresh hold name run after this one lands on ::1 instead.
That is the shape the real check should show, with more addresses in the first case. The toy says nothing about our pool or any other.
What the count doesn't show
Addresses are all it counts. It can't say where they are, so if you asked for us, confirm that with whatever geolocation lookup you trust. It can't say whether the site you want will accept them either, and a gateway can rotate perfectly while handing you exits a given target refuses.
That second question is the one that costs money. The only number worth having is one measured on your own target at your own volume, and a quoted success rate can't stand in for it. Why is in your 99% success rate was measured on a page that lets you in.
A held session ends when its time does, and the exit after that is a different address, so a long job should log the address beside every request. The echo service is somebody else's server. Switch to another if the first one rate-limits you.
Check the batch before the count
Buy the smallest amount of residential proxies the pricing page lets you, in the pool you want to use. Call GET /pools and copy that pool's id. Generate one rotating batch and one sticky batch with that id in pool, and read four fields in each response before you use a line: pool, is_demo, proxy_type and session_duration.
is_demo is the one to look at first. If the wallet for the pool is empty, the API doesn't return an error. It returns lines with non-working credentials and is_demo: true, so you can wire up an integration before you pay, and they look like real lines until you try them. The same happens when a pool is coming_soon, with coming_soon: true beside it to tell the two apart. Wallets are per pool, so credit on Premium doesn't help a batch generated against Essential. If you see true, check pool first, then the balance for that pool.
With is_demo: false and the right pool, run the twenty-request loop on each line. If the output matches, every later failure is a question about the target and not about the gateway. If it doesn't, read proxy_type and session_duration back from the response before you touch anything else.
Start small, then measure.
Top up a wallet from $0.50 and point it at your own targets, or try the free trial first.