Skip to content

Freedom — Xray-core

Freedom is Xray's bypass outbound — it sends traffic to the real destination without any proxy protocol in the middle. It also carries two anti-DPI features: TCP/TLS fragmentation and arbitrary noise-packet injection.

Outbound

settings for an outbound of "protocol": "freedom":

FieldTypeDefaultAllowed valuesDescription
targetStrategystringasisasis | useip | useipv4 | useipv6 | useipv4v6 | useipv6v4 | forceip | forceipv4 | forceipv6 | forceipv4v6 | forceipv6v4How the destination address is resolved before dial-out. `asis` passes through whatever the routing engine handed in; `useip*` resolves to IP only if needed; `forceip*` always resolves and replaces. The `*v4v6` / `*v6v4` suffixes control which family is tried first.
domainStrategystring(inherits targetStrategy)<same values>Legacy alias for `targetStrategy`. Read only when `targetStrategy` is empty.
redirectstring(unset)<host:port>Override the destination of every dial. Useful for forwarding to a local service regardless of routing.
userLeveluint320<uint32>Default policy level for connections passing through this outbound.
fragment*Fragment(disabled)FragmentPer-segment TCP fragmentation for evading length-based DPI.
noise*Noise(removed)(use noises)Removed field. Use the `noises` array. Setting `noise` triggers an explicit error at config build.
noises[]*Noise(unset)[Noise]List of noise packets sent ahead of (or between) real packets to throw off pattern matching.
proxyProtocoluint3200 | 1 | 2Prepend a PROXY-protocol header on outgoing connections. 0 disables; 1 / 2 set v1 (text) or v2 (binary).
ipsBlocked*StringList(removed)(use finalRules)Removed field. Migrated to `finalRules`; setting it only logs a warning and has no effect.
finalRules[]*FreedomFinalRuleConfig(unset)[FreedomFinalRuleConfig]Per-connection allow/block rules evaluated at dial time — a lightweight firewall on the outbound itself, matched by network / port / IP.

Source: infra/conf/freedom.go:19-30 · pinned at v26.6.1 (94ffd50)

fragment

FieldTypeDefaultAllowed valuesDescription
packetsstring(all)tlshello | <from-to> | Which packets to fragment. `tlshello` fragments only the TLS ClientHello (subtler, useful for SNI obfuscation). A range like `"1-3"` fragments packets at that offset range. Empty fragments every packet — most aggressive.
length*Int32Range(required){from, to}Range of bytes per fragment. Min must be > 0.
interval*Int32Range(required){from, to}Range of milliseconds to delay between fragments.
maxSplit*Int32Range(unbounded){from, to}Range of total splits per packet. Optional cap.

Source: infra/conf/freedom.go:32-37 · pinned at v26.6.1 (94ffd50)

noises[]

FieldTypeDefaultAllowed valuesDescription
typestring(required)rand | str | hex | base64Packet payload form. `rand` generates random bytes whose length comes from `packet` (a range string). `str`/`hex`/`base64` interpret `packet` literally.
packetstring(required)<bytes or range>Payload content. Format depends on `type`.
delay*Int32Range(unset){from, to}Range of milliseconds to wait before sending this noise packet.
applyTostringipip | all | ipv4 | ipv6Restrict noise injection to a single IP family. `ip` and `all` apply to both.

Source: infra/conf/freedom.go:39-44 · pinned at v26.6.1 (94ffd50)

finalRules[]

A small allow/block firewall the freedom outbound applies as it dials out. Rules are evaluated in order; the first match decides. This replaces the removed ipsBlocked field.

FieldTypeDefaultAllowed valuesDescription
actionstring(required)allow | blockWhat to do with a matching connection. `block` refuses it; `allow` lets it through — list `allow` rules first to carve exceptions out of a broader `block`.
network*NetworkList(any)tcp | udp | tcp,udpMatch only these networks.
port*PortList(any)<port / range list>Match only these destination ports.
ip*StringList(any)<IP / CIDR / geoip:>Match only these destination IPs. Accepts `geoip:` categories.
blockDelay*Int32Range(immediate){from, to}For a `block` action, wait a random delay in this millisecond range before dropping the connection — stalls a prober instead of failing fast.

Source: infra/conf/freedom.go:46-52 · pinned at v26.6.1 (94ffd50)

Examples

Plain freedom outbound (passthrough):

json
{
  "outbounds": [
    { "tag": "direct", "protocol": "freedom", "settings": {} }
  ]
}

Force IPv4-then-IPv6 resolution:

json
{
  "outbounds": [
    {
      "tag": "direct-v4",
      "protocol": "freedom",
      "settings": { "targetStrategy": "forceipv4v6" }
    }
  ]
}

TLS-hello fragmentation (good first try for SNI obfuscation):

json
{
  "outbounds": [
    {
      "tag": "frag",
      "protocol": "freedom",
      "settings": {
        "fragment": {
          "packets": "tlshello",
          "length":   { "from": 10, "to": 20 },
          "interval": { "from": 10, "to": 20 }
        }
      }
    }
  ]
}

Noise injection (4 random bytes ahead of every IPv4 connection, delayed 0-1 ms):

json
{
  "outbounds": [
    {
      "tag": "noisy",
      "protocol": "freedom",
      "settings": {
        "noises": [
          {
            "type": "rand",
            "packet": "4-4",
            "delay": { "from": 0, "to": 1 },
            "applyTo": "ipv4"
          }
        ]
      }
    }
  ]
}

Block QUIC (UDP/443) outright, and stall connections to private ranges to slow down probing:

json
{
  "outbounds": [
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {
        "finalRules": [
          { "action": "block", "network": "udp", "port": "443" },
          { "action": "block", "ip": ["geoip:private"], "blockDelay": { "from": 1000, "to": 3000 } }
        ]
      }
    }
  ]
}

Forward everything to a local service (testing helper):

json
{
  "outbounds": [
    {
      "tag": "redir",
      "protocol": "freedom",
      "settings": { "redirect": "127.0.0.1:8080" }
    }
  ]
}

Notes

  • targetStrategy is the modern field; domainStrategy is read only as a fallback when targetStrategy is empty (infra/conf/freedom.go:46-48). New configs should set only targetStrategy.
  • Unknown strategy strings produce a startup error rather than a silent fallback (infra/conf/freedom.go:72-73).
  • noise (singular) is removed. The error message at startup tells you to migrate to noises = [ { ... } ] (infra/conf/freedom.go:145-147).
  • ipsBlocked is removed and folded into finalRules. Replace it with a { "action": "block", "ip": [...] } rule; the old field now only logs a warning and is otherwise ignored.
  • finalRules actions are allow / block only (unknown actions error at startup). ip accepts geoip: categories; blockDelay applies solely to block rules.
  • fragment.packets: "tlshello" is the recommended starting point — fragmenting only the TLS ClientHello breaks SNI-based DPI but leaves the rest of the connection untouched (the byte-overhead is one-time).
  • fragment.length.from and fragment.interval are required when fragment is present (infra/conf/freedom.go:102-118). The maxSplit range is optional.
  • proxyProtocol outside 1..2 is silently ignored — only those two values produce header output (infra/conf/freedom.go:162-164).

Cross-core notes

  • sing-box calls this Direct and the outbound has almost no options of its own — destination overrides were removed in 1.13; use route actions for that behavior. See Direct — sing-box.
  • mihomo has a similarly minimal Direct type; the built-in DIRECT proxy is implicit in every config. See Direct — mihomo.

Source: infra/conf/freedom.go:19-52 · v26.6.1 (94ffd50)

Core Tutorial by Argsment