Skip to main content

Profile Syntax Reference

Audience: AI assistants. Complete, self-contained syntax tables for generating Surfboard profiles. Distilled from the parser source code (surfboard-core/profile/); if a construct is not listed here, it is not supported.

Maintainers: keep this page in sync with both the source code and the per-topic pages under Profile Format.

File Structure

  • Surge-style INI. Comments: #, ;, // (whole-line or trailing).
  • Key/value separator: =. Parameter separator: ,. Values may be wrapped in single or double quotes (required when a value contains ,, ; or spaces).
  • Recognized sections (exact spelling, others silently ignored): [General], [Host], [Proxy], [Proxy Group], [Rule], [Panel], [WireGuard <name>].
  • Optional first line (managed subscription header):
#!MANAGED-CONFIG https://example.com/profile.conf interval=86400 strict=false
ParameterDefaultNote
interval-Auto-update interval in seconds.
strict-Parsed but ignored.

Canonical doc: Managed Config

[General]

All keys optional.

KeyValueDefaultNote
ipv6true/falsetrueEnable IPv6 support.
dns-serverComma-separated list-Items: system, IPv4, IPv4:port, bare IPv6, [IPv6]:port.
doh-serverComma-separated URLs-e.g. https://1.1.1.1/dns-query.
http-listenIP:port-Port MUST be 1025-65535.
socks5-listenIP:port-Port MUST be 1025-65535.
skip-proxyComma-separated list-Items: IP, IP/prefix, localhost, wildcard (*.local), plain domain. Bypasses proxying.
proxy-test-urlURLhttp://www.gstatic.com/generate_204MUST be http://.
internet-test-urlURLhttp://www.gstatic.cn/generate_204MUST be http://.
test-timeoutInteger seconds5Connectivity test timeout.
always-real-ipComma-separated domains/wildcards-Stored verbatim; bypasses fake-IP.
udp-policy-not-supported-behaviourDIRECT/REJECTREJECTFallback for UDP routed to a proxy without UDP relay.
[General]
dns-server = system, 223.5.5.5, [2001:db8::1]:53
doh-server = https://1.1.1.1/dns-query
skip-proxy = 192.168.0.0/16, localhost, *.local, example.com
proxy-test-url = http://www.gstatic.com/generate_204
test-timeout = 5
udp-policy-not-supported-behaviour = DIRECT
ipv6 = true

Canonical docs: General

[Proxy]

Line format:

{name} = {protocol}, {server}, {port}, [positional args...,] key=value, ...

Protocol keywords: direct, reject, reject-tinygif, http, https, socks5, socks5-tls, shadowsocks (aliases: ss, custom), vmess, trojan, hysteria2, tuic-v5, anytls, snell, wireguard.

Common Parameters

Valid on all protocols except direct/reject/wireguard (which support only block-quic).

ParameterValueDefaultNote
udp-relaytrue/falsePer protocol (see tables)Enables UDP forwarding.
underlying-proxyProxy or group name-Chain: connect through another proxy. Reference must exist; cycles rejected.
block-quicoff/on/autoauto (off for direct)Per-node QUIC (UDP/443) blocking.

TLS parameters, valid on TLS-capable protocols (https, socks5-tls, vmess+tls, trojan, hysteria2, tuic-v5, anytls):

ParameterValueNote
skip-cert-verifytrue/falseSkip certificate validation.
sniDomain, or offsni=off disables SNI entirely. Invalid values discarded.
server-cert-fingerprint-sha25664-char hex, comma-separated listCertificate pinning.

direct / reject

LocalDirect = direct, block-quic=off
Block = reject
BlockGif = reject-tinygif

Built-in policy names usable anywhere without definition: DIRECT, REJECT, REJECT-TINYGIF, REJECT-DROP, REJECT-NO-DROP. (REJECT-DROP/REJECT-NO-DROP exist only as built-in names, not definable types.)

http / https / socks5 / socks5-tls

httpshttp + tls=true; socks5-tlssocks5 + tls=true. Username/password are positional (args 4 and 5), not key=value.

ParameterMandatoryDefaultNote
server, porttrue-
username, passwordfalse-Positional, may be quoted.
tlsfalsefalseUpgrades http/socks5 to the TLS variant.
udp-relayfalsefalseMeaningful for socks5 only.
TLS paramsfalse-Only on TLS variants.
ProxyHTTP = http, 192.0.2.1, 8080, username, password
ProxyHTTPS = https, 192.0.2.1, 443, username, password, sni=example.com
ProxySOCKS5 = socks5, 192.0.2.1, 1080, udp-relay=true

shadowsocks (ss, custom)

ParameterMandatoryDefaultNote
server, porttrue-
encrypt-methodtrue-encrypt-method= key or positional arg 4. See method list below.
passwordtrue-password= key or positional arg 5. SS2022: base64, 16 bytes (aes-128) / 32 bytes (aes-256).
udp-relayfalsefalse
obfsfalse-tls or http.
obfs-hostfalsecloudfront.netOnly when obfs set.
obfs-urifalse/Only when obfs set.

Encrypt methods: aes-128-gcm, aes-192-gcm, aes-256-gcm, chacha20-ietf-poly1305, xchacha20-ietf-poly1305, 2022-blake3-aes-128-gcm, 2022-blake3-aes-256-gcm, none. Legacy stream ciphers (rc4, rc4-md5, aes-*-cfb, aes-*-ctr, bf-cfb, camellia-*-cfb, salsa20, chacha20, chacha20-ietf) are accepted in debug builds only — never generate them.

ProxySS = ss, 192.0.2.1, 8000, encrypt-method=chacha20-ietf-poly1305, password=<PASSWORD>, udp-relay=true, obfs=tls, obfs-host=example.com
ProxySS2022 = ss, 192.0.2.1, 8000, encrypt-method=2022-blake3-aes-256-gcm, password=<BASE64-32-BYTES>

vmess

ParameterMandatoryDefaultNote
server, porttrue-
usernametrue-MUST be a valid UUID.
tlsfalsefalse
wsfalsefalseWebSocket transport.
ws-pathfalse/
ws-headersfalse-Format: `Key:Value
vmess-aeadfalsetrue
encrypt-methodfalseaes-128-gcmaes-128-gcm or chacha20-ietf-poly1305.
udp-relayfalsefalse
TLS paramsfalse-When tls=true and sni empty, SNI falls back to Host in ws-headers.
ProxyVMess = vmess, 192.0.2.1, 443, username=<UUID>, ws=true, tls=true, ws-path=/v2, ws-headers=Host:cdn.example.com

trojan

ParameterMandatoryDefaultNote
server, porttrue-
passwordtrue-Non-empty.
ws, ws-path, ws-headersfalsefalse, /, -Same as vmess.
udp-relayfalsefalse
TLS paramsfalse-Trojan is always TLS.
ProxyTrojan = trojan, 192.0.2.1, 443, password=<PASSWORD>, sni=example.com, ws=true, ws-path=/ws

hysteria2

ParameterMandatoryDefaultNote
server, porttrue-
passwordtrue-
download-bandwidthfalse-Integer, Mbps.
port-hoppingfalse-Semicolon-separated ranges, quoted: "1234;5000-6000".
port-hopping-intervalfalse-Seconds.
salamander-passwordfalse-Salamander obfuscation.
gecko-passwordfalse-Gecko obfuscation. Takes precedence over salamander-password if both are set.
udp-relayfalsetrue
TLS paramsfalse-
ProxyHysteria2 = hysteria2, 192.0.2.1, 443, password=<PASSWORD>, port-hopping="1234;5000-6000", port-hopping-interval=30, sni=example.com

tuic-v5

ParameterMandatoryDefaultNote
server, porttrue-
uuidtrue-MUST be a valid UUID.
passwordtrue-
alpnfalse-e.g. h3.
port-hopping, port-hopping-intervalfalse-Same format as hysteria2.
udp-relayfalsetrue
TLS paramsfalse-
ProxyTuic = tuic-v5, 192.0.2.1, 443, uuid=<UUID>, password=<PASSWORD>, alpn=h3

anytls

ParameterMandatoryDefaultNote
server, porttrue-
passwordtrue-password= key or positional arg 4.
reusefalsetrueSession reuse.
udp-relayfalsetrue
TLS paramsfalse-
ProxyAnyTLS = anytls, 192.0.2.1, 443, password=<PASSWORD>, sni=example.com

snell

ParameterMandatoryDefaultNote
server, porttrue-
psktrue-psk= key or positional arg 4.
versionfalse11-4; values >4 clamped to 4.
obfs, obfs-host, obfs-urifalse-, cloudfront.net, -
udp-relayfalsefalseEffective only with version>=3; forced off on v1/v2.
ProxySnell = snell, 192.0.2.1, 443, psk=<PSK>, version=4, obfs=http, obfs-host=example.com, udp-relay=true

wireguard

[Proxy] line only references a [WireGuard <name>] section:

ProxyWG = wireguard, section-name = HomeServer

[WireGuard HomeServer]
private-key = <BASE64>
self-ip = 192.0.2.2
dns-server = 1.1.1.1
mtu = 1280
peer = (public-key = <BASE64>, preshared-key = <BASE64>, allowed-ips = "0.0.0.0/0, ::/0", endpoint = example.com:51820, keepalive = 25)
KeyMandatoryDefaultNote
private-keytrue-Base64.
self-iptrue-IPv4.
self-ip-v6false-IPv6.
dns-serverfalse-Comma-separated IPs.
mtufalse1280
peertrue-Parenthesized map. public-key, allowed-ips, endpoint mandatory; preshared-key, keepalive optional. endpoint is host:port; IPv6 must be bracketed, e.g. [2001:db8::1]:51820.

WireGuard always supports UDP (udp-relay implied true).

Canonical docs: Proxy · shadowsocks · vmess · trojan · hysteria2 · tuic-v5 · anytls · snell · http · socks5 · wireguard · direct · reject

[Proxy Group]

Line format:

{name} = {type}, member1, member2, ..., key=value, ...

Types: select, url-test (alias: smart), fallback, load-balance, subnet, ssid. Members are proxy names, group names, or built-in policies.

Common Parameters

ParameterValueDefaultNote
policy-pathURL-Remote proxy list (parsed as [Proxy] lines); members merged into the group.
policy-regex-filterRegex.*Filters members from policy-path/include-all-proxies/include-other-group.
update-intervalSeconds86400policy-path refresh interval.
hiddentrue/falsefalseHide from UI.
no-alerttrue/falsefalse
include-all-proxiestrue/falsefalseInclude all [Proxy] entries (regex-filtered).
include-other-groupQuoted, comma-separated group names-Merge members of other groups (recursive).

evaluate-before-use and icon-url are parsed but silently ignored.

Type-Specific Parameters

TypeParametersDefaults
selectNone.-
url-test / smarturl (MUST be http://), interval (s), tolerance (ms), timeout (s)interval=600, tolerance=100, timeout=5
fallbackurl (MUST be http://), interval (s), timeout (s)interval=600, timeout=5
load-balancepersistent=true/falsefalse
subnet / ssidMUST contain default = <policy>; other entries are condition = policy where condition is SSID:<name>, BSSID:<mac>, ROUTER:<ip>, TYPE:WIFI/WIRED/CELLULAR, MCCMNC:<mcc-mnc>-
SelectGroup = select, NodeA, NodeB, DIRECT, REJECT
AutoGroup = url-test, NodeA, NodeB, url=http://www.gstatic.com/generate_204, interval=600, tolerance=100, timeout=5
SubGroup = select, policy-path=https://example.com/nodes.txt, update-interval=86400, policy-regex-filter=HK
AllProxies = select, include-all-proxies=true
LBGroup = load-balance, NodeA, NodeB, persistent=true
SubnetGroup = subnet, default = NodeA, TYPE:WIFI = DIRECT, SSID:MyHome = NodeB

A Global Proxy select group (all non-hidden proxies, excluding DIRECT/REJECT) is auto-generated; never define it yourself.

SSID/BSSID-based groups require location permission on the device.

Canonical docs: Proxy Group · select · url-test · fallback · load-balance · subnet/ssid

[Rule]

Line format: TYPE,value,policy[,attributes...]. Evaluated top-down, first match wins. policy must be a defined proxy/group or a built-in policy name.

TypeSyntaxNote
DOMAINDOMAIN,www.example.com,ProxyExact match.
DOMAIN-SUFFIXDOMAIN-SUFFIX,example.com,ProxyMatches the domain and all subdomains.
DOMAIN-KEYWORDDOMAIN-KEYWORD,example,ProxySubstring match.
DOMAIN-WILDCARDDOMAIN-WILDCARD,*.example.com,Proxy*/? wildcards.
DOMAIN-SETDOMAIN-SET,https://example.com/list.txt,ProxyRemote URL only. File: one domain per line; leading . = suffix match, otherwise exact.
IP-CIDR / IP-CIDR6IP-CIDR,192.0.2.0/24,DIRECT,no-resolveCIDR.
GEOIPGEOIP,CN,DIRECT,no-resolveTwo-letter country code.
PROCESS-NAMEPROCESS-NAME,com.example.app,ProxyAndroid package name; wildcards supported.
USER-AGENTUSER-AGENT,ExampleApp*,DIRECTWildcard match on HTTP User-Agent.
SUBNETSUBNET,TYPE:WIFI,DIRECTSubtypes: SSID:, BSSID:, ROUTER:, TYPE:WIFI/WIRED/CELLULAR, MCCMNC:. Requires location permission for SSID/BSSID.
DEST-PORTDEST-PORT,443,DIRECT1-65535.
SRC-IPSRC-IP,192.0.2.10,DIRECTExact source IP.
IN-PORTIN-PORT,6152,DIRECT1-65535.
PROTOCOLPROTOCOL,QUIC,REJECTHTTP (TCP:80), HTTPS (TCP:443), TCP, UDP, QUIC (UDP:443).
RULE-SETRULE-SET,https://example.com/rules.conf,ProxyRemote URL only. RULE-SET,SYSTEM/RULE-SET,LAN NOT supported. File lines are rules without the policy column (policy inherited). Nested RULE-SET forbidden.
AND / OR / NOTAND,((RULE1),(RULE2)),ProxyAND/OR take ≥2 sub-rules, NOT exactly 1. Sub-rules have no policy. Max nesting depth 16. No RULE-SET/DOMAIN-SET inside. Any invalid sub-rule drops the whole rule.
FINALFINAL,ProxyCatch-all. MUST be the last rule.

Rule attributes (4th column onward), only on DOMAIN/DOMAIN-SUFFIX/DOMAIN-KEYWORD/DOMAIN-WILDCARD/PROCESS-NAME/USER-AGENT:

AttributeEffect
force-remote-dnsResolve the domain via the proxy instead of locally.
enhanced-modeEnable enhanced matching mode.

no-resolve on IP-based rules is accepted (compatibility) and conventionally means "do not trigger DNS resolution to evaluate this rule".

[Rule]
DOMAIN,www.example.com,Proxy
DOMAIN-SUFFIX,ads.example.com,REJECT
DOMAIN-KEYWORD,tracker,REJECT
PROCESS-NAME,com.example.app,Proxy,force-remote-dns
IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
SUBNET,TYPE:WIFI,DIRECT
PROTOCOL,QUIC,REJECT
AND,((DOMAIN-SUFFIX,example.com),(DEST-PORT,443)),Proxy
GEOIP,CN,DIRECT
RULE-SET,https://example.com/reject-rules.conf,REJECT
FINAL,Proxy

Canonical docs: Rule · domain · ip · process · ruleset · domainset · logical · subnet · misc · user-agent · final

[Host]

Custom DNS resolution. Format: key = value[, value...].

Key formValue formExample
Domain or * wildcardOne or more IPs (v4/v6 mixed)example.com = 192.0.2.1, 2001:db8::1
Domain or wildcardSingle domain (alias)foo.example.com = bar.example.com
Domain or wildcardserver:<IP> / server:<IP>:<port>bar.example.com = server:1.1.1.1
Domain or wildcardserver:system / server:syslibbaz.example.com = server:system
Domain or wildcardserver:https://<doh-url>doh.example.com = server:https://1.1.1.1/dns-query
DOMAIN-SET:<url>Any value form aboveDOMAIN-SET:https://example.com/list.txt = 192.0.2.1
RULE-SET:<url>Any value form aboveDomain rules in the set match for DNS.

Canonical docs: Host

[Panel]

{name} = title="...", content="...\n...", style=good|info|alert|error

title and content mandatory; style optional. \n produces a line break. icon is parsed but ignored.

Canonical doc: Panel

Single-proxy share links the app accepts (convert them into [Proxy] lines when the user pastes one):

SchemeNotes
ss://SIP002 (base64url userinfo) and legacy whole-base64 forms. plugin=obfs-local;obfs=tls;obfs-host=...;obfs-uri=... maps to obfs parameters.
vmess://v2rayN base64 JSON (ps/add/port/id/tls/sni/net/path/host). Only net=ws (or plain TCP) supported.
trojan://Query: sni, type=ws, path, host, allowInsecure.
anytls://Query: sni, insecure=1. Default port 443.
hysteria2://, hy2://Query: sni, insecure=1, `obfs=salamander

Subscription URL schemes: http(s)://, surfboard://, surge://, surge3://. Deep links of the form surfboard:///install-config?url=<encoded> wrap a subscription URL.