---
title: "Proxies & exits"
description: "Store a proxy, check it, and understand the exit rule: how one bought gateway becomes many stable addresses, why an address belongs to one profile, and what the guard does while a browser runs."
canonical: "https://scalebrowser.net/docs/proxies"
---

> ## Documentation Index
> Fetch the complete documentation index at: https://scalebrowser.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Proxies & exits

> Store a proxy, check it, and understand the exit rule: how one bought gateway becomes many stable addresses, why an address belongs to one profile, and what the guard does while a browser runs.

Where a request comes from is judged before any JavaScript on the page has run, so nothing the browser does reaches it. This page is the half of a profile's identity that lives outside the browser.

## Storing one
A proxy is a row: kind, host, port, optional credentials, a rotation setting and a label.

```bash
$ curl -X POST http://127.0.0.1:8787/v1/proxies \
    -H "Authorization: Bearer $SCALEBROWSER_BEARER_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"kind":"gateway","host":"gw.dataimpulse.com","port":823,
         "username":"…","password":"…","rotation":"rotating","label":"DE mobile"}'
```

| Field      | Values                              | Notes                                                                       |
| ---------- | ----------------------------------- | ----------------------------------------------------------------------------- |
| `kind`     | `http` · `socks5` · `gateway`       | `gateway` is a provider endpoint that hands out exits, rather than one exit.  |
| `rotation` | `sticky` · `rotating`               | What the provider does. This is your claim about them, not a measurement.     |
| `label`    | free text                           | Give one. A whole pool shares host, port and kind, so nothing else tells two rows apart. |

<Warning>

**Credentials are write-only.** No endpoint returns them, and the type that carries them cannot be serialised at all, so a proxy password cannot leave through a response by accident.  They are encrypted at rest with the daemon's master key.

A row whose credentials will not decrypt, for instance a database restored without its key, is flagged `credentials_unreadable`. It stays listable and deletable, and every path that would *use* it fails closed: an authenticated proxy is never run without its credentials, because that would send the session out over your real address.

</Warning>

## Checking one
Two calls, and the difference matters.

| Call                         | What it does                                                                                                    |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `POST /v1/proxies/:id/check` | Runs the real check through a saved proxy: reachability, exit address and country, and the fingerprint verdict. Stores the result and emits a `proxy_checked` event. |
| `POST /v1/proxies/check`     | The same check on an unsaved configuration. Stores nothing, emits nothing, and adds `latency_ms`. Test before you persist. |

An unreachable proxy is reported as `healthy: false`, not as an error. The row exists; it just does not work right now.

<Warning>

**Changing the connection identity clears the cached exit.** A `PATCH` touching host, port, kind or credentials drops the stored country and health, because the geo gate must not judge a new endpoint by the previous one's exit. Run a check afterwards. Changing only the rotation setting or the label keeps the cache.

</Warning>

## One gateway, many profiles
A residential or mobile pool is bought once and split into many stable exits by a **session tag**, a value the provider reads out of your username or password. The daemon knows the spelling for fourteen providers and writes the tag for you, so one bought account serves many profiles with a different address each.

| Provider host                          | Where the tag goes | Shape                     |
| -------------------------------------- | ------------------ | ------------------------- |
| `brd.superproxy.io`                    | username           | up to 20 alphanumerics    |
| `pr.oxylabs.io`                        | username           | up to 20 alphanumerics    |
| `residential.proxy.decodo.com`         | username           | up to 20 alphanumerics    |
| `proxy.soax.com`                       | username           | up to 32 alphanumerics    |
| `gw.netnut.net`                        | username           | 8 digits                  |
| `p.webshare.io`                        | username           | 6 digits                  |
| `rp.scrapegw.com`                      | username           | up to 20 alphanumerics    |
| `gw.dataimpulse.com`                   | username           | up to 20 alphanumerics    |
| `unmetered.residential.proxyrack.net`  | username           | up to 20 alphanumerics    |
| `pool.infatica.io`                     | username           | up to 16 alphanumerics    |
| `geo.iproyal.com`                      | password           | exactly 8 alphanumerics   |
| `rp.evomi.com`                         | password           | up to 16 alphanumerics    |
| `residential.rayobyte.com`             | password           | up to 16 alphanumerics    |
| `superproxy.zenrows.com`               | password           | up to 16 alphanumerics    |

The host is matched on its ending, so a regional hostname such as `de.gw.netnut.net` resolves too.

<Note>

**An unknown host is not an error.** The daemon leaves the credentials exactly as you entered them and simply cannot draw a new session. Guessing a spelling would be worse than not trying: a malformed username is a refused connection at most gateways, which reads like a dead proxy.

</Note>

## One address, one profile
Two of your profiles arriving from the same address at the same site is the cheapest link there is. The daemon therefore treats an exit address as **owned**.

When a profile starts and the address it draws already belongs to another profile, the daemon draws a new session tag and tries again, up to five times. If every draw is taken, the start is refused with error `4013`, which is deliberately not `4002`: this is not a proxy problem, and sending you to check your proxy would send you looking for something that is not there.

### The comparison unit
Not the raw string. For IPv4 it is the address; for IPv6 it is the `/64` prefix, because a subscriber line draws new addresses out of that prefix at will, and comparing whole IPv6 addresses would report "different" for every pair while protecting nothing.

Two different addresses inside one IPv4 `/24` count as **neighbours**. That is advisory by default and can be made blocking.

### How long ownership lasts
By the kind of address, because a mobile address is recycled in days and a datacenter address is not:

| Address kind  | Ownership lasts |
| ------------- | --------------- |
| `mobile`      | 3 days          |
| `residential` | 30 days         |
| `datacenter`  | 90 days         |
| `unknown`     | 30 days         |

`unknown` deliberately takes the middle term rather than the longest. A deployment without an address dataset classifies everything as unknown, and the longest term there would drain the pool while looking careful.

## The guard, while a browser runs
An exit can change under you: a provider rotates, a session times out, a route flaps. Two beats watch for it:

- **Keep-alive, every 30 seconds**, only where the provider documents an idle cut-off. A couple of hundred bytes that say nothing and hold the session. Oxylabs, Decodo and SOAX drop a session after 60 seconds of silence, Bright Data after five minutes, so a resting profile would otherwise lose its address between two clicks.
- **Address check, every 120 seconds.** The real question, and the only one that can cut a wire.

The verdict rides on the event stream as `exit_guard_changed`, in one of four states:

| State        | What it means                                                                                       |
| ------------ | ----------------------------------------------------------------------------------------------------- |
| `ok`         | The address is the one this profile owns.                                                            |
| `degraded`   | One or two probes in a row got no answer. Nothing is cut.                                            |
| `unverified` | Three in a row got no answer. Still nothing is cut: the wire is unverified, not proven wrong.        |
| `severed`    | The address moved onto one another profile owns. The wire is cut, and stays cut.                     |

<Note>

**A silent probe is not an address change.** If it were, one unreachable probe endpoint would sever every healthy profile behind it, and the outage would cause exactly the damage the guard exists to prevent. `degraded` and `unverified` are statements about our knowledge, and neither cuts anything.

A severed wire is not reconnected automatically either, because an automatic return would show the page a second address change mid-session.

</Note>

### What the promise actually is
A number, not the word "never". The relay opens its own upstream per browser connection, so between a provider-side change nobody announced and the next check there is a window by construction:

- **zero at start**, measured live rather than read off a stored field
- **30 seconds** around a rotation whose period two matching gaps have confirmed
- **at most 120 seconds** otherwise

The rotation shape is measured, with two probes, and never read off the row's `rotation` setting: that field is a customer's claim about a provider, and the provider is the one that decides.

## Turning the rule off
`GET` and `PUT /v1/exit-rule` read and set it for this machine, and the management app has the switch under **Settings**. Off means starts are never refused for a taken address and nothing is watched.

That is a real choice for a single-profile setup and a bad one for a fleet.

## What the daemon deliberately does not do
It does not rewrite traffic. There is no man in the middle, no header injection, no certificate of ours in the chain. A proxy is a proxy, and the coherence work happens inside the engine where a page can be told the truth instead of a rewritten version of it.

A datacenter exit therefore stays a datacenter exit. No patch in the browser reaches that, which is why sharp targets presuppose a terminating residential or mobile proxy. See [Coherence & proxies](/docs/coherence).

## Next
- [Coherence & proxies](/docs/coherence): what the exit is compared against before a launch.
- [Profiles & personas](/docs/profiles): the half of the identity that lives inside the browser.
- [Events](/docs/events): the `exit_guard_changed` and `proxy_checked` frames.
- [Errors](/docs/errors): `4002` and `4013`, and why they are different.
