diff --git a/frontend/src/lib/xray/outbound-form-adapter.ts b/frontend/src/lib/xray/outbound-form-adapter.ts
index 86c69697..5ecc41b6 100644
--- a/frontend/src/lib/xray/outbound-form-adapter.ts
+++ b/frontend/src/lib/xray/outbound-form-adapter.ts
@@ -265,6 +265,10 @@ function freedomFromWire(raw: Raw): FreedomOutboundFormSettings {
return (allowed.includes(s) ? s : '') as FreedomOutboundFormSettings['domainStrategy'];
})(),
redirect: asString(raw.redirect),
+ proxyProtocol: ((): FreedomOutboundFormSettings['proxyProtocol'] => {
+ const n = asNumber(raw.proxyProtocol, 0);
+ return (n === 1 || n === 2) ? n : 0;
+ })(),
fragment: wireHasFragment
? {
packets: asString(fragment.packets, '1-3'),
@@ -489,6 +493,7 @@ function freedomToWire(s: FreedomOutboundFormSettings) {
return {
domainStrategy: s.domainStrategy || undefined,
redirect: s.redirect || undefined,
+ proxyProtocol: s.proxyProtocol || undefined,
fragment: fragmentEnabled ? Object.fromEntries(fragmentEntries) : undefined,
noises: s.noises.length > 0 ? s.noises : undefined,
finalRules: s.finalRules.length > 0
diff --git a/frontend/src/pages/xray/OutboundFormModal.tsx b/frontend/src/pages/xray/OutboundFormModal.tsx
index 8bd90bc6..29f2f2bc 100644
--- a/frontend/src/pages/xray/OutboundFormModal.tsx
+++ b/frontend/src/pages/xray/OutboundFormModal.tsx
@@ -664,6 +664,15 @@ export default function OutboundFormModal({
+
+
+
{() => {
diff --git a/frontend/src/schemas/forms/outbound-form.ts b/frontend/src/schemas/forms/outbound-form.ts
index 96ca63bf..eb73e423 100644
--- a/frontend/src/schemas/forms/outbound-form.ts
+++ b/frontend/src/schemas/forms/outbound-form.ts
@@ -166,6 +166,7 @@ export type FreedomFinalRuleForm = z.infer;
export const FreedomOutboundFormSettingsSchema = z.object({
domainStrategy: z.union([OutboundDomainStrategySchema, z.literal('')]).default(''),
redirect: z.string().default(''),
+ proxyProtocol: z.number().int().min(0).max(2).default(0),
fragment: FreedomFragmentSchema.default({
packets: '1-3',
length: '',
diff --git a/frontend/src/schemas/protocols/outbound/freedom.ts b/frontend/src/schemas/protocols/outbound/freedom.ts
index c09d00be..ff7d997d 100644
--- a/frontend/src/schemas/protocols/outbound/freedom.ts
+++ b/frontend/src/schemas/protocols/outbound/freedom.ts
@@ -52,6 +52,7 @@ export type FreedomFinalRule = z.infer;
export const FreedomOutboundSettingsSchema = z.object({
domainStrategy: OutboundDomainStrategySchema.optional(),
redirect: z.string().optional(),
+ proxyProtocol: z.number().optional(),
fragment: FreedomFragmentSchema.optional(),
noises: z.array(FreedomNoiseSchema).optional(),
finalRules: z.array(FreedomFinalRuleSchema).optional(),
diff --git a/frontend/src/test/outbound-form-adapter.test.ts b/frontend/src/test/outbound-form-adapter.test.ts
index 3fdf9186..f4b3b0e0 100644
--- a/frontend/src/test/outbound-form-adapter.test.ts
+++ b/frontend/src/test/outbound-form-adapter.test.ts
@@ -235,16 +235,26 @@ describe('outbound-form-adapter: round-trip', () => {
settings: {
domainStrategy: 'UseIPv4',
redirect: '1.1.1.1',
+ proxyProtocol: 2,
fragment: { packets: 'tlshello', length: '100-200' },
},
}));
expect(filled.settings).toMatchObject({
domainStrategy: 'UseIPv4',
redirect: '1.1.1.1',
+ proxyProtocol: 2,
fragment: { packets: 'tlshello', length: '100-200' },
});
});
+ it('freedom omits proxyProtocol when disabled (0)', () => {
+ const round = formValuesToWirePayload(rawOutboundToFormValues({
+ protocol: 'freedom',
+ settings: { proxyProtocol: 0 },
+ }));
+ expect((round.settings as { proxyProtocol?: number }).proxyProtocol).toBeUndefined();
+ });
+
it('mux is only emitted when enabled AND protocol/network/flow allow it', () => {
// Disabled mux: omitted
const disabled = formValuesToWirePayload(rawOutboundToFormValues({
diff --git a/web/translation/ar-EG.json b/web/translation/ar-EG.json
index 1586d691..bb24350d 100644
--- a/web/translation/ar-EG.json
+++ b/web/translation/ar-EG.json
@@ -1209,6 +1209,7 @@
"interface": "الواجهة",
"ipv6Only": "IPv6 فقط",
"acceptProxyProtocol": "قبول proxy protocol",
+ "proxyProtocol": "Proxy protocol",
"tcpUserTimeoutMs": "TCP user timeout (ms)",
"tcpKeepAliveIdleS": "TCP keep-alive idle (ثانية)"
},
diff --git a/web/translation/en-US.json b/web/translation/en-US.json
index e30b75a9..222e5988 100644
--- a/web/translation/en-US.json
+++ b/web/translation/en-US.json
@@ -1209,6 +1209,7 @@
"interface": "Interface",
"ipv6Only": "IPv6 only",
"acceptProxyProtocol": "Accept proxy protocol",
+ "proxyProtocol": "Proxy protocol",
"tcpUserTimeoutMs": "TCP user timeout (ms)",
"tcpKeepAliveIdleS": "TCP keep-alive idle (s)"
},
diff --git a/web/translation/es-ES.json b/web/translation/es-ES.json
index b9c9b5a8..2ec91be7 100644
--- a/web/translation/es-ES.json
+++ b/web/translation/es-ES.json
@@ -1209,6 +1209,7 @@
"interface": "Interfaz",
"ipv6Only": "Solo IPv6",
"acceptProxyProtocol": "Aceptar proxy protocol",
+ "proxyProtocol": "Proxy protocol",
"tcpUserTimeoutMs": "TCP user timeout (ms)",
"tcpKeepAliveIdleS": "TCP keep-alive idle (s)"
},
diff --git a/web/translation/fa-IR.json b/web/translation/fa-IR.json
index fa239780..5e468248 100644
--- a/web/translation/fa-IR.json
+++ b/web/translation/fa-IR.json
@@ -1209,6 +1209,7 @@
"interface": "رابط",
"ipv6Only": "فقط IPv6",
"acceptProxyProtocol": "پذیرش Proxy Protocol",
+ "proxyProtocol": "Proxy Protocol",
"tcpUserTimeoutMs": "TCP user timeout (ms)",
"tcpKeepAliveIdleS": "TCP keep-alive idle (s)"
},
diff --git a/web/translation/id-ID.json b/web/translation/id-ID.json
index 9bde0098..84322de3 100644
--- a/web/translation/id-ID.json
+++ b/web/translation/id-ID.json
@@ -1209,6 +1209,7 @@
"interface": "Interface",
"ipv6Only": "Hanya IPv6",
"acceptProxyProtocol": "Terima proxy protocol",
+ "proxyProtocol": "Proxy protocol",
"tcpUserTimeoutMs": "TCP user timeout (ms)",
"tcpKeepAliveIdleS": "TCP keep-alive idle (d)"
},
diff --git a/web/translation/ja-JP.json b/web/translation/ja-JP.json
index f5a1e276..cd201e10 100644
--- a/web/translation/ja-JP.json
+++ b/web/translation/ja-JP.json
@@ -1209,6 +1209,7 @@
"interface": "インターフェース",
"ipv6Only": "IPv6 のみ",
"acceptProxyProtocol": "proxy protocol を受け入れる",
+ "proxyProtocol": "Proxy protocol",
"tcpUserTimeoutMs": "TCP user timeout (ms)",
"tcpKeepAliveIdleS": "TCP keep-alive idle (秒)"
},
diff --git a/web/translation/pt-BR.json b/web/translation/pt-BR.json
index e32d14af..2cf99c59 100644
--- a/web/translation/pt-BR.json
+++ b/web/translation/pt-BR.json
@@ -1209,6 +1209,7 @@
"interface": "Interface",
"ipv6Only": "Apenas IPv6",
"acceptProxyProtocol": "Aceitar proxy protocol",
+ "proxyProtocol": "Proxy protocol",
"tcpUserTimeoutMs": "TCP user timeout (ms)",
"tcpKeepAliveIdleS": "TCP keep-alive idle (s)"
},
diff --git a/web/translation/ru-RU.json b/web/translation/ru-RU.json
index 72e611f1..53efabdc 100644
--- a/web/translation/ru-RU.json
+++ b/web/translation/ru-RU.json
@@ -1209,6 +1209,7 @@
"interface": "Интерфейс",
"ipv6Only": "Только IPv6",
"acceptProxyProtocol": "Принимать proxy protocol",
+ "proxyProtocol": "Proxy protocol",
"tcpUserTimeoutMs": "TCP user timeout (мс)",
"tcpKeepAliveIdleS": "TCP keep-alive idle (с)"
},
diff --git a/web/translation/tr-TR.json b/web/translation/tr-TR.json
index e0a945c7..ab11e88c 100644
--- a/web/translation/tr-TR.json
+++ b/web/translation/tr-TR.json
@@ -1209,6 +1209,7 @@
"interface": "Arabirim",
"ipv6Only": "Yalnızca IPv6",
"acceptProxyProtocol": "Proxy protocol kabul et",
+ "proxyProtocol": "Proxy protocol",
"tcpUserTimeoutMs": "TCP user timeout (ms)",
"tcpKeepAliveIdleS": "TCP keep-alive idle (s)"
},
diff --git a/web/translation/uk-UA.json b/web/translation/uk-UA.json
index a37ed985..e934d571 100644
--- a/web/translation/uk-UA.json
+++ b/web/translation/uk-UA.json
@@ -1209,6 +1209,7 @@
"interface": "Інтерфейс",
"ipv6Only": "Лише IPv6",
"acceptProxyProtocol": "Приймати proxy protocol",
+ "proxyProtocol": "Proxy protocol",
"tcpUserTimeoutMs": "TCP user timeout (мс)",
"tcpKeepAliveIdleS": "TCP keep-alive idle (с)"
},
diff --git a/web/translation/vi-VN.json b/web/translation/vi-VN.json
index 0b8a2607..15bedc93 100644
--- a/web/translation/vi-VN.json
+++ b/web/translation/vi-VN.json
@@ -1209,6 +1209,7 @@
"interface": "Giao diện",
"ipv6Only": "Chỉ IPv6",
"acceptProxyProtocol": "Chấp nhận proxy protocol",
+ "proxyProtocol": "Proxy protocol",
"tcpUserTimeoutMs": "TCP user timeout (ms)",
"tcpKeepAliveIdleS": "TCP keep-alive idle (s)"
},
diff --git a/web/translation/zh-CN.json b/web/translation/zh-CN.json
index 60296692..f5953722 100644
--- a/web/translation/zh-CN.json
+++ b/web/translation/zh-CN.json
@@ -1209,6 +1209,7 @@
"interface": "接口",
"ipv6Only": "仅 IPv6",
"acceptProxyProtocol": "接受 proxy protocol",
+ "proxyProtocol": "Proxy protocol",
"tcpUserTimeoutMs": "TCP user timeout (ms)",
"tcpKeepAliveIdleS": "TCP keep-alive idle (s)"
},
diff --git a/web/translation/zh-TW.json b/web/translation/zh-TW.json
index e65bda7a..a874fed6 100644
--- a/web/translation/zh-TW.json
+++ b/web/translation/zh-TW.json
@@ -1209,6 +1209,7 @@
"interface": "介面",
"ipv6Only": "僅 IPv6",
"acceptProxyProtocol": "接受 proxy protocol",
+ "proxyProtocol": "Proxy protocol",
"tcpUserTimeoutMs": "TCP user timeout (ms)",
"tcpKeepAliveIdleS": "TCP keep-alive idle (s)"
},