Proxy formats explained: which string each tool wants
Proxy format explained: HOST:PORT:USER:PASS, user:pass@host:port and full URLs, which tool wants which, a tested converter, and fixes for special characters.
A proxy format is the order in which four values are written as one string: host, port, username and password. The common ones are HOST:PORT, HOST:PORT:USERNAME:PASSWORD, USERNAME:PASSWORD@HOST:PORT and the full URL http://USERNAME:PASSWORD@HOST:PORT. Code libraries want the URL. Antidetect browsers and bulk importers usually want the colon-separated line.
The values never change between formats; only the punctuation around them does. A correct proxy pasted in the wrong shape fails exactly like a wrong one, so the shape is worth ten minutes. This post lists the formats you will meet, says which tools accept which, and gives you a small converter in Python and JavaScript so you can stop reformatting lists by hand.
Every example uses HOST, PORT, USERNAME and PASSWORD as placeholders. Your own values are on each service's page in the dashboard; there is no shared gateway address to copy from a blog post.
The five proxy formats you will meet
| Format | Example | Where you see it |
|---|---|---|
| Host and port | HOST:PORT |
Browser and system settings, allowlisted connections |
| Colon line, host first | HOST:PORT:USERNAME:PASSWORD |
Provider list exports, antidetect browser imports |
| Colon line, user first | USERNAME:PASSWORD:HOST:PORT |
Some other providers' exports and older tools |
| Login before the host | USERNAME:PASSWORD@HOST:PORT |
curl, some bots and config files |
| Full URL | http://USERNAME:PASSWORD@HOST:PORT |
Python, Node.js, Go, PHP, environment variables |
The full URL is the one to keep as your master copy. It carries the scheme, it is a standard, and every other format can be built from it without guessing.
What the http:// at the front means
The scheme says how your program talks to the proxy, not how it talks to the website. http:// means a plain HTTP proxy, and it still reaches https:// sites: your client asks the proxy to open a CONNECT tunnel and the encryption runs through it end to end. Writing https:// in front of the proxy asks for an encrypted connection to the proxy itself, which is a different thing. When we pointed requests at a plain HTTP proxy with an https:// URL, it said so in as many words:
ProxyError('Unable to connect to proxy. Your proxy appears to only use HTTP and not HTTPS, try changing your proxy URL to be HTTP. ...')
http:// is available on every product. For SOCKS5, write socks5h:// instead: residential ports answer both, and ISP and datacenter proxies switch protocol in the dashboard.
Which tools want which proxy string format
We fed each format to curl 8.5, requests 2.34 and undici 8.11 (the fetch library for Node.js) against a local proxy that asks for a login. Here is what each accepted.
curl is the most forgiving. -x "http://USERNAME:PASSWORD@HOST:PORT" works, and so does -x USERNAME:PASSWORD@HOST:PORT without a scheme, since curl assumes http://. You can also keep the login out of the URL with -U:
curl -x HOST:PORT -U "USERNAME:PASSWORD" https://api.ipify.org
The colon line is refused before anything is sent: curl: (5) Unsupported proxy syntax in 'HOST:PORT:USERNAME:PASSWORD': Port number was not a decimal number between 0 and 65535. The cURL guide has the rest of curl's proxy options.
Python requests wants the full URL whenever there is a login. USERNAME:PASSWORD@HOST:PORT without http:// fails with InvalidProxyURL: Please check proxy URL. It is malformed and could be missing the host., and the colon line fails with InvalidURL: Failed to parse. A bare HOST:PORT is accepted, because requests adds the scheme for you.
Node.js is the strictest. undici's ProxyAgent rejects HOST:PORT with TypeError: Invalid URL, and rejects USERNAME:PASSWORD@HOST:PORT with Invalid URL protocol: the URL must start with `http:` or `https:`. Give it the full URL every time, as the Node.js guide does.
Browsers take the host and port in one pair of fields and ask for the username and password in a pop-up when the proxy first asks. Chrome and Edge hand the whole job to the operating system. The browser guide covers Firefox's own panel and the FoxyProxy extension, which has proper login fields. If Chrome refuses to load anything after you change these settings, Chrome proxy errors maps each error code to its cause.
Antidetect browsers offer both: separate Host, Port, Username and Password fields, and a paste or import box that reads one proxy per line as HOST:PORT:USERNAME:PASSWORD. The GoLogin, AdsPower and BitBrowser guides show where each one hides that box.
Proxifier has separate fields for everything. Choose HTTPS as the protocol, which is Proxifier's name for an HTTP proxy that opens CONNECT tunnels; the Proxifier guide walks through it.
The ip:port:user:pass format from the dashboard
The residential page in the dashboard exports proxies in HOST:PORT:USERNAME:PASSWORD order, the same order GoLogin and BitBrowser import. ISP and datacenter services list each static IP with its own port and login, and those four values drop into any of the formats above.
On residential, choose Randomize IP or Sticky IP in the dashboard before you copy anything. With Sticky IP the session option travels at the end of the password, so the password you copy is longer than the plain one. Copy the whole string as shown. A sticky password cut short at a symbol gets a 407, and rotating vs sticky proxies explains when you want that mode at all.
Special characters: when user:pass@host:port breaks
Inside a URL, some characters have jobs: @ ends the login, : separates username from password and host from port, and /, #, ? and % mean something too. A password containing any of them has to be percent-encoded in the URL formats. We tested with the password p@ss:w/rd#1%41:
- Pasted raw into a URL, curl stopped with
Unsupported proxy syntax, requests withInvalidURL, and undici withInvalid URL. - Encoded as
p%40ss%3Aw%2Frd%231%2541, all three connected. - With curl's
-U, the@,:,/and#could stay raw, but%41was still decoded toA, so a literal%must be written%25there too.
The colon line has the opposite rule: it is never encoded, because tools split it on colons rather than parsing it as a URL. That makes the colon line fragile in its own way. A password with a colon in it only splits correctly when the tool knows the order and treats everything after the third colon as the password. Fixing a 407 goes through the other ways a login gets mangled on the way to the proxy.
IPv6 proxies need brackets
An IPv6 address is full of colons, so in a URL it goes in square brackets: http://[2001:db8::10]:PORT, with the login before it as usual. Without brackets, curl fails with the same "Port number was not a decimal number" error and requests with Failed to parse. With brackets, curl, requests and undici all connected to our test proxy on [::1]. In a colon line, keep the brackets too, [2001:db8::10]:PORT:USERNAME:PASSWORD, or no tool can tell where the address ends.
A proxy format converter in Python
This reads any of the five formats and writes the one you need. Save it as proxyfmt.py:
import re
from urllib.parse import quote, unquote
HOST_RE = r"(?P<host>\[[^\]]+\]|[^:@\[\]]+)"
PORT_RE = r"(?P<port>[^:@/]+)"
URL_FORM = rf"^(?:(?P<scheme>[a-z0-9]+):\/\/)?(?:(?P<user>[^:@]*):(?P<password>.*)@)?{HOST_RE}:{PORT_RE}/?$"
COLON_FORMS = {
"host_first": rf"^{HOST_RE}:{PORT_RE}:(?P<user>[^:]*):(?P<password>.*)$",
"user_first": rf"^(?P<user>[^:]*):(?P<password>.*):{HOST_RE}:{PORT_RE}$",
}
def parse_proxy(line, order="host_first"):
line = line.strip()
match = re.match(URL_FORM, line)
decode = unquote
if not match:
match = re.match(COLON_FORMS[order], line)
decode = str
if not match:
raise ValueError(f"not a proxy line I recognise: {line!r}")
p = match.groupdict()
return {
"scheme": p.get("scheme") or "http",
"host": p["host"],
"port": p["port"],
"user": decode(p["user"]) if p["user"] is not None else None,
"password": decode(p["password"]) if p["password"] is not None else None,
}
def to_url(p):
auth = ""
if p["user"] is not None:
auth = quote(p["user"], safe="") + ":" + quote(p["password"], safe="") + "@"
return p["scheme"] + "://" + auth + p["host"] + ":" + p["port"]
def to_colon(p):
parts = [p["host"], p["port"], p["user"], p["password"]]
return ":".join(part for part in parts if part is not None)
if __name__ == "__main__":
for line in [
"HOST:PORT",
"HOST:PORT:USERNAME:PASSWORD",
"USERNAME:PASSWORD@HOST:PORT",
"http://USERNAME:PASSWORD@HOST:PORT",
]:
print(to_url(parse_proxy(line)))
print(to_colon(parse_proxy("USERNAME:PASSWORD:HOST:PORT", order="user_first")))
The two colon lines look identical from the outside, so parse_proxy cannot guess between them. It assumes host first, the dashboard's order, and takes order="user_first" for lists from elsewhere. URL forms are decoded on the way in and encoded on the way out, so a password full of symbols survives a round trip. To use a saved export with requests:
import requests
from proxyfmt import parse_proxy, to_url
with open("proxies.txt") as f:
proxies = [to_url(parse_proxy(line)) for line in f if line.strip()]
for proxy in proxies:
r = requests.get("https://api.ipify.org", proxies={"http": proxy, "https": proxy}, timeout=30)
print(r.status_code, r.text)
The same converter in JavaScript
proxyfmt.mjs, for Node.js 22 or newer:
const HOST_RE = String.raw`(?<host>\[[^\]]+\]|[^:@\[\]]+)`;
const PORT_RE = String.raw`(?<port>[^:@/]+)`;
const URL_FORM = new RegExp(String.raw`^(?:(?<scheme>[a-z0-9]+):\/\/)?(?:(?<user>[^:@]*):(?<password>.*)@)?${HOST_RE}:${PORT_RE}\/?$`);
const COLON_FORMS = {
hostFirst: new RegExp(String.raw`^${HOST_RE}:${PORT_RE}:(?<user>[^:]*):(?<password>.*)$`),
userFirst: new RegExp(String.raw`^(?<user>[^:]*):(?<password>.*):${HOST_RE}:${PORT_RE}$`),
};
export function parseProxy(line, order = 'hostFirst') {
line = line.trim();
let match = line.match(URL_FORM);
let decode = decodeURIComponent;
if (!match) {
match = line.match(COLON_FORMS[order]);
decode = (s) => s;
}
if (!match) throw new Error(`not a proxy line I recognise: ${line}`);
const { scheme, host, port, user, password } = match.groups;
return {
scheme: scheme ?? 'http',
host,
port,
user: user === undefined ? undefined : decode(user),
password: password === undefined ? undefined : decode(password),
};
}
export function toUrl(p) {
const auth = p.user === undefined ? '' : encodeURIComponent(p.user) + ':' + encodeURIComponent(p.password) + '@';
return p.scheme + '://' + auth + p.host + ':' + p.port;
}
export function toColon(p) {
return [p.host, p.port, p.user, p.password].filter((part) => part !== undefined).join(':');
}
And with undici (npm i undici):
import { fetch, ProxyAgent } from 'undici';
import { parseProxy, toUrl } from './proxyfmt.mjs';
const proxy = toUrl(parseProxy('HOST:PORT:USERNAME:PASSWORD'));
const response = await fetch('https://api.ipify.org', { dispatcher: new ProxyAgent(proxy) });
console.log(response.status, await response.text());
We ran both versions against local test proxies with plain passwords, the symbol-heavy password above, IPv6 addresses in brackets and user-first lines. Every line connected, and converting to a URL and back returned the same four values.
When HOST:PORT alone is enough
If you add the IP address you connect from to the service's IP allowlist in the dashboard, the proxy lets that address in without a login. From then on HOST:PORT, or http://HOST:PORT for Node.js, is the whole proxy string, and nobody has to store the password in a config file. That suits a server with a fixed public address. It does not suit a laptop that moves between networks, because the allowlist only knows the addresses you gave it.
Quick answers
What is the ip:port:user:pass format? The colon line with the host first: HOST:PORT:USERNAME:PASSWORD. It is what the dashboard's residential export uses and what most antidetect browsers import.
Is user:pass@host:port the same as the URL format? Nearly. It is the URL without http:// at the front. curl accepts it as is; Python requests and Node.js need the scheme added.
Should the proxy URL start with http or https? http://, including for https websites. The tunnel carries the site's encryption.
Why does my proxy list work in one tool and not another? The field order or the encoding differs. Convert the list with the snippet above and check whether the password has symbols in it.
Next step
Copy one line from your service page, run it through the converter, and send a single request to https://api.ipify.org. If the address that comes back is not yours, the format is right. How to use a proxy covers that first request in more detail, and if a list still refuses to load, paste the error in Discord with the password masked.