# 托管代理

托管代理让只接受代理设置的应用经由 URnetwork 发送流量：curl、脚本、爬虫，或者 Shadowrocket 这样的手机应用。这份指南说明在 ur.io 上哪里能找到代理的 URL，以及如何把它填进常见的客户端。

一条适用范围说明：托管代理没有密封会话。你的客户端设备运行在运营方内部，由运营方把代理的协议翻译成网络自己的协议。站在运营方和你的流量之间的，是[威胁模型](/docs/threat-model)里描述的代理不记日志的设计，而不是密封。提供者仍然不知道你是谁。如果应用本身能运行 URnetwork，那么 URnetwork 应用和 [SDK](/docs/getting-started-sdk) 是更私密的途径。

## 两种代理 {#types}

ur.io 能创建两种可供第三方客户端使用的代理：

| | HTTPS 代理 | SOCKS5 代理 |
| --- | --- | --- |
| 套餐 | 所有套餐 | UR Pro |
| ur.io 上的 URL | `https://<token>.<host>:<port>` | `socks5h://<token>:x@<host>:<port>` |
| 令牌放在哪里 | 放在主机名里，不需要用户名和密码 | 作为用户名；从不检查密码 |
| 客户端必须支持 | HTTPS 代理，即与代理之间使用 TLS | 带用户名和密码的 SOCKS5 |
| 承载内容 | 网页流量：HTTP，以及经 CONNECT 的 HTTPS | TCP 连接，以及经 UDP associate 的 UDP |

SOCKS5 代理不支持 BIND，并会丢弃超过 2 KiB 的 UDP 数据报。ur.io 还能在 UR Pro 上创建 WireGuard 配置文件。把下载的 `.conf` 文件导入 WireGuard 应用即可；本指南其余部分讲 HTTPS 和 SOCKS5。

## 获取代理的 URL {#create}

1. 在 [ur.io](https://ur.io) 登录，打开**代理**（Proxies）（[ur.io/app/proxies](https://ur.io/app/proxies)）。
2. 选择**创建代理**（Create proxy）。
3. 选择代理类型：**HTTPS**、**SOCKS5** 或 **WireGuard**。SOCKS5 和 WireGuard 带有 **Pro** 标记。在免费档上，创建按钮会先提供 Pro。
4. 选择位置。搜索国家、地区或城市，或者保留**最佳可用**（Best available），它使用网络上的任意提供者。每个位置都会显示它有多少提供者。
5. 选择 **Create HTTPS proxy** 或 **Create SOCKS5 proxy**。新代理会连同它的 URL 一起出现。

列表中的每个 HTTPS 和 SOCKS5 代理都有**复制URL**（Copy URL），它会复制带令牌的完整 URL；还有 **×**，用于删除这个代理。

## 读懂 URL {#url}

```text
https://<token>.<host>:<port>
socks5h://<token>:x@<host>:<port>
```

- `<token>` 是代理的令牌，一长串字母和数字。它是代理唯一的凭据，字母大小写无关紧要。
- 在 HTTPS URL 里，令牌是主机名的第一段。把 `<token>.<host>` 原样填作代理主机，与 ur.io 显示的完全一致：主机名里的令牌就是你的登录凭据。
- 在 SOCKS5 URL 里，令牌位于 `socks5h://` 和 `:x@` 之间。`<host>` 在 `@` 之后，`<port>` 在最后一个 `:` 之后。
- 其中的 `x` 是占位密码。SOCKS5 代理从不检查密码，所以填什么都可以；但有些客户端只有在同时设置了密码时才会发送用户名，Python 的 `requests` 就是其中之一，所以 ur.io 的 URL 带上了这个 `x`。如果客户端要求分别填写用户名和密码，用户名填令牌，密码随便填。
- 保留 `socks5h` 里的 `h`。它让代理去解析主机名，于是域名查询经由提供者发出，而不是从你的机器发出。如果用不带 h 的 `socks5`，curl 会先在你的机器上解析域名。

> **把 URL 当作密码对待。** 任何拿到它的人都能使用这个代理，而代理的流量消耗的是你账户的数据。如果 URL 泄露了，在 ur.io 上删除那个代理，代理就不再接受它的令牌。然后再创建一个新的。

## 用 curl 检查是否可用 {#curl}

```sh
# HTTPS proxy, on every plan
curl -x 'https://<token>.<host>:<port>' https://api.bringyour.com/my-ip-info

# SOCKS5 proxy, on UR Pro
curl -x 'socks5h://<token>:x@<host>:<port>' https://api.bringyour.com/my-ip-info
```

`/my-ip-info` 会返回你的请求来自哪个地址，以及这个地址位于何处。经过代理时，那就是你所选位置中某个提供者的地址。HTTPS 代理需要 curl 7.52 或更新版本。

## 浏览器 {#browsers}

给浏览器加代理最简单的办法是 URnetwork 扩展，它会创建并管理自己的托管代理。见[浏览器入门](/docs/getting-started-browser)。

如果要改用你在 ur.io 上创建的代理，请用它的 HTTPS URL。Chrome 根本无法发送 SOCKS5 用户名。操作系统的代理设置和 Firefox 的手动代理设置通常都无法描述 HTTPS 代理。

**Chrome、Edge 及其他 Chromium 系浏览器。** 在命令行上带着代理启动浏览器。单独的配置文件目录能让这个参数生效，即使你平时用的浏览器窗口还开着：

```sh
# macOS
open -na "Google Chrome" --args --user-data-dir="$HOME/urnetwork-proxy-profile" --proxy-server="https://<token>.<host>:<port>"

# Linux
google-chrome --user-data-dir="$HOME/urnetwork-proxy-profile" --proxy-server="https://<token>.<host>:<port>"

# Windows, in Command Prompt (Edge: msedge instead of chrome)
start chrome --user-data-dir="%USERPROFILE%\urnetwork-proxy-profile" --proxy-server="https://<token>.<host>:<port>"
```

**Firefox。** Firefox 的手动设置里没有 SOCKS 的用户名栏，也没有 HTTPS 代理类型，所以改用一个只有一行的代理自动配置（PAC）文件。把下面的内容存为 `urnetwork.pac`：

```js
function FindProxyForURL(url, host) {
  return "HTTPS <token>.<host>:<port>";
}
```

然后打开**设置 → 常规 → 网络设置 → 设置…**（Settings → General → Network Settings → Settings…），选择**自动代理配置 URL**（Automatic proxy configuration URL），填入该文件的地址，例如 `file:///Users/you/urnetwork.pac` 或 `file:///C:/Users/you/urnetwork.pac`，再选择**确定**（OK）。要停用时，在同一个对话框里选择**不使用代理**（No proxy）。

## 手机应用：Shadowrocket 等 {#shadowrocket}

手机上的代理应用，比如 iOS 上的 Shadowrocket，用几个字段来设置一台 SOCKS5 服务器。在 ur.io 上创建一个 SOCKS5 代理（UR Pro）并复制它的 URL。在应用里添加一台服务器，按 URL 填写：

| 字段 | 值 |
| --- | --- |
| 类型（Type） | SOCKS5 |
| 地址、服务器或主机（Address） | `<host>`：`@` 与最后一个 `:` 之间的部分 |
| 端口（Port） | `<port>`：最后一个 `:` 之后的数字 |
| 用户或用户名（User） | `<token>`：`socks5h://` 与 `:x@` 之间的部分 |
| 密码（Password） | 任意值，例如 `x` |

保存服务器，选中它，然后打开连接。如果要覆盖整部手机，[iOS](/docs/getting-started-ios) 或 [Android](/docs/getting-started-android) 上的 URnetwork 应用不需要代理，而且是更私密的途径。

## Python {#python}

`requests` 两种代理都能用。SOCKS5 需要 `socks` 附加组件（`pip install "requests[socks]"`），而且它的 URL 需要一个占位密码：

```python
import requests

proxy = "https://<token>.<host>:<port>"          # HTTPS proxy, every plan
# proxy = "socks5h://<token>:x@<host>:<port>"    # SOCKS5 proxy, UR Pro

r = requests.get(
    "https://api.bringyour.com/my-ip-info",
    proxies={"http": proxy, "https": proxy},
    timeout=30,
)
print(r.json()["info"]["ip"])
```

`httpx` 接受同样的 URL；SOCKS5 需要 `pip install "httpx[socks]"`。`aiohttp` 接受 HTTPS 代理：

```python
import asyncio

import aiohttp
import httpx

with httpx.Client(proxy="https://<token>.<host>:<port>") as client:
    print(client.get("https://api.bringyour.com/my-ip-info").json())


async def main():
    async with aiohttp.ClientSession() as session:
        async with session.get("https://api.bringyour.com/my-ip-info", proxy="https://<token>.<host>:<port>") as r:
            print(await r.json())

asyncio.run(main())
```

Scrapy 的默认下载处理器无法经 HTTPS 代理抓取 `https://` 页面：它会停下并报错 "HTTPS proxies for HTTPS destinations are not supported"。请改用上面的某个库来抓取这些页面。

## Node.js {#node}

对于 `fetch`，把 HTTPS 代理交给 undici 的 `ProxyAgent`：

```js
import { fetch, ProxyAgent } from "undici";

const dispatcher = new ProxyAgent("https://<token>.<host>:<port>");
const res = await fetch("https://api.bringyour.com/my-ip-info", { dispatcher });
console.log(await res.json());
```

对于 `node:https` 以及接受 agent 的库，使用 `https-proxy-agent`；SOCKS5 则用 `socks-proxy-agent`：

```js
import https from "node:https";
import { HttpsProxyAgent } from "https-proxy-agent";
// import { SocksProxyAgent } from "socks-proxy-agent";

const agent = new HttpsProxyAgent("https://<token>.<host>:<port>");
// const agent = new SocksProxyAgent("socks5h://<token>:x@<host>:<port>");

https.get("https://api.bringyour.com/my-ip-info", { agent }, (res) => res.pipe(process.stdout));
```

## 其他程序：proxychains {#proxychains}

在 Linux 上，proxychains-ng 能让一个程序的连接经由 SOCKS5 代理（UR Pro）发出，即使这个程序本身没有代理设置。它靠预加载一个库来工作，所以管不到静态链接的程序，其中包括大多数 Go 程序。它的第一个代理必须是 IP 地址，而不能是主机名，所以先查出地址：

```sh
dig +short <host>
```

把这个地址写进配置文件 `urnetwork.conf`：

```text
strict_chain
proxy_dns
[ProxyList]
socks5 <ip> <port> <token> x
```

然后通过它运行程序：

```sh
proxychains4 -f urnetwork.conf curl https://api.bringyour.com/my-ip-info
```

`proxy_dns` 让 proxychains 经由代理解析域名。如果之后连接不再可用，重新查一次地址。

## 限制 {#limits}

| | 免费档 | UR Pro |
| --- | --- | --- |
| 活跃代理 | 5 | 1,000 |
| 每分钟新建代理 | 5 | 20 |

浏览器扩展创建的代理也计算在内。限制只阻止新建代理：你已有的代理照常工作，删除一个就能腾出位置再建一个。代理会一直保留，直到你删除它；闲置约 30 天未用的代理会被自动删除。

## 独立出口 IP {#unique-exit}

托管代理通常会把连接分散到多个提供者上，所以你的两个代理可能从同一个地址出网。开启独立出口 IP（UR Pro）后，每个代理只经由一个提供者出网，而这个提供者的地址不会被你的其他代理使用。只要该提供者保持健康，代理就一直使用这个出口。

- **开启方式：** 在代理页面创建代理时，勾选**独立出口 IP**。把**代理数量**设为最多 20，可以一次创建多个。在 API 里，在 `proxy_config` 中加入 `"unique_exit": true`。
- **限制：** 独立出口代理和其他代理计入同样的限制：1,000 个活跃代理，每分钟新建 20 个。
- **你能看到什么：** 每个代理会显示它的出口所在的位置（城市、地区和国家）以及开始使用的时间。出口的 IP 地址不会显示。想看 IP，可以通过代理访问一个 IP 查询地址，例如用上面的 curl 命令。
- **提供者出故障时：** 代理会自动换一个新的独立出口，期间显示**正在更换出口…**。每小时的自动更换次数有限制；超过限制后，代理会等待并稍后再试。在 API 里，`"unique_exit_replace": "manual"` 会让代理改为等你处理：它会显示**出口已失效**，直到你手动更换。
- **更换出口：** 给代理换一个新的独立出口。每个代理的出口每分钟可更换一次，每天最多 60 次。如果该位置没有其他空闲的独立出口，代理会保留当前的出口。
- **没有可用的独立出口：** 当该位置所有健康的出口都已被你的其他代理占用时，ur.io 会提示你，并且不会创建代理；这次尝试不计入你的限制。请换一个位置，或者选**最佳可用**。

## 故障排查 {#troubleshooting}

**ur.io 拒绝创建代理。** 它会显示原因（消息为英文）。在免费档上：

- "Your plan's limit of 5 active proxies is reached. Remove a proxy to create another, or upgrade to UR Pro for more."（你的套餐已达到 5 个活跃代理的上限。）删除不再使用的代理，或者升级。
- "Your plan allows 5 new proxies a minute. Try again in a minute, or upgrade to UR Pro for more."（你的套餐每分钟只允许新建 5 个代理。）等一分钟再试。

在 UR Pro 上，同样的消息会给出 Pro 的限制，并且不提升级。

**SOCKS5 代理拒绝登录。** curl 会报下面其中一条，其他客户端各有各的措辞：

- `User was rejected by the SOCKS5 server (1 1).` 令牌错误或不完整，或者代理已被删除。从 ur.io 重新复制 URL。
- `No authentication method was acceptable.` 客户端没有发送用户名。把用户名设为令牌，并填一个占位密码，比如 `x`。

**HTTPS 代理拒绝连接。** curl 会报 `CONNECT tunnel failed, response N`，其中 N 是：

- 407：代理没有找到有效的令牌。从 ur.io 重新复制 URL，并把完整的 `<token>.<host>` 填作代理主机。
- 502：代理已不存在，例如你删除了它；或者无法连到目标。
- 403：调用方的地址不在代理的 IP 锁定范围内。只有通过 API 创建并设置了 IP 锁定的代理才有这种锁定。

如果客户端收到空回复，或者连接立刻被关闭，它多半是在用明文 HTTP 和代理通信。请在支持 HTTPS 代理的客户端里使用 `https://` URL。

**页面很慢或超时。** 试试**最佳可用**，或者提供者更多的位置。代理的流量消耗你账户的数据，所以数据用完的账户无法建立新连接。

**出口 IP 会变。** 和应用一样，托管代理会把它的连接分散到所在位置的多个提供者上。网站看到的地址可能每次连接都不一样。如果想让每个代理固定一个出口 IP，请在 UR Pro 上用[独立出口 IP](#unique-exit) 创建它。

**代理位置不对。** 最佳可用可能从任何国家出网。创建代理时选好国家、地区或城市。ur.io 无法移动你已有的代理：在你想要的位置新建一个，然后删除旧的。

## 用 API 自动化 {#api}

代理页面调用的是[运营方 API](/docs/api)：带 `proxy_config` 的 `POST /network/auth-client`，其中 `initial_device_state.location` 选择位置。返回的 `proxy_config_result` 里有 `https_proxy_url`、`socks_proxy_url` 和 `auth_token`。API 返回的 `socks_proxy_url` 不含用户名：像 ur.io 那样，把 `auth_token` 加作用户名。由 IP 地址或子网组成的 `lock_ip_list` 可以限制谁能使用代理。上面的限制同样适用于 API。

如需[独立出口 IP](#unique-exit)，在 `proxy_config` 中加入 `unique_exit`：

```json
{
  "proxy_config": {
    "unique_exit": true,
    "initial_device_state": {
      "location": { "connect_location_id": { "best_available": true } }
    }
  }
}
```

加入 `"unique_exit_replace": "manual"` 后，代理的提供者出故障时会保持在原出口上，直到你手动更换；默认值为 `"auto"`。

返回的 `proxy_config_result.exit` 给出出口的 `exit_id`、`country_code`、`region`、`city`、`exit_since`、`state`（`assigned`、`replacing` 或 `no_supply`）、有原因时的 `state_reason`，以及 `replace_mode`。`GET /network/proxies` 会在每个代理的 `exit` 中报告同样的信息，与它的 `proxy_client` 并列。带 `no_unique_supply` 的拒绝表示该位置已没有你的代理尚未使用的出口。用代理的 `client_id` 调用 `POST /network/proxy/rotate-exit`，可以把它换到一个新的独立出口，返回新的 `exit`；超过更换频率时会以 `rate_limited` 拒绝，没有其他空闲出口时以 `no_unique_supply` 拒绝。调用它需要你网络的登录凭证或 API 密钥。
