Skip to content

TCP and UDP ports

A tcp service passes TCP and forwards UDP to your origins, for example for VPNs; anyconnect fronts a Cisco Secure VPN gateway. The mode is set at creation.

Endpoints

Method Path Key Purpose
PATCH /services/{fqdn} write Change a vService

PATCH /services/{fqdn}

Body: ServicePatch
Returns: 200, Service

Request: waf_and_limit
{
  "waf": {
    "enabled": true
  },
  "rate_limiting": {
    "req_per_10s": 800
  }
}
Request: replace_origins
{
  "origins": [
    {
      "name": "origin-1",
      "address": "192.0.2.10",
      "port": 443
    }
  ]
}
Answer 200: waf_and_limit
{
  "fqdn": "a.example.com",
  "tenant": "tenant-a",
  "enabled": true,
  "mode": "https",
  "ports": [
    443
  ],
  "udp_ports": [],
  "created": "2026-10-01T00:00:00+00:00",
  "created_by": "",
  "account_id": "acc-a",
  "network": {
    "vips": [
      {
        "anycast_ipv4": "203.0.113.1",
        "anycast_ipv6": "2001:db8:a::1"
      }
    ],
    "pool": null,
    "edge_hostname": "a-example-com.edge.protect.zeroservices.eu"
  },
  "network_protection": {
    "enabled": true
  },
  "backend": {
    "algorithm": "roundrobin",
    "health_check": {}
  },
  "origins": [
    {
      "name": "origin-1",
      "address": "192.0.2.10",
      "port": 443,
      "tls": true,
      "ssl_verify": true,
      "weight": 100,
      "maxconn": 100
    }
  ],
  "waf": {
    "enabled": true,
    "fail_open": true,
    "mode": "On",
    "paranoia_level": 1,
    "inbound_threshold": 5,
    "disabled_rules": []
  },
  "rate_limiting": {
    "challenge_req_per_10s": 200,
    "req_per_10s": 800,
    "conn_per_10s": 50,
    "conn_drop_per_10s": 500,
    "path_default": 1000,
    "path_aggregate_per_10s": 5000,
    "geo_tcp_reject_sess_rate": 200,
    "ja4_enabled": false,
    "ja4_req_per_10s": 100,
    "ja4_action": "challenge"
  },
  "pow": {
    "enabled": true,
    "difficulty": 18,
    "cookie_ttl": 86400,
    "always_challenge": false,
    "exempt_crawlers": true,
    "bind_ip": true,
    "botcheck_per_10s": 1000
  },
  "admission": {
    "enabled": false,
    "credit_required": 5,
    "credit_window": "60s",
    "known_ttl": "24h",
    "unknown_conn_rate": 1500,
    "table_size": "1m"
  },
  "ssl": {
    "mode": "acme",
    "acme_provider": "zerossl",
    "acme_email": null,
    "https_redirect": false,
    "cert_status": "none",
    "cert_issued": null,
    "cert_expires": null,
    "cert_last_renewal": null,
    "days_remaining": null
  },
  "aliases": [
    {
      "fqdn": "www.a.example.com",
      "ssl": {
        "mode": "acme",
        "acme_provider": "zerossl",
        "acme_email": null,
        "https_redirect": false,
        "cert_status": "none",
        "cert_issued": null,
        "cert_expires": null,
        "cert_last_renewal": null,
        "days_remaining": null
      }
    }
  ],
  "cache": {
    "enabled": true
  },
  "slow_http": {
    "enabled": true,
    "timeout_http_request": "5s",
    "timeout_client_fin": "5s"
  },
  "tcp_timeout": {
    "client": "30s",
    "server": "30s",
    "tunnel": "30s"
  },
  "udp_timeout": {
    "idle": "300s",
    "unreplied": "30s",
    "unreplied_pkts": 32,
    "src_pps": 0
  },
  "sni_policy": "allowlist",
  "proxy_protocol": false,
  "tls_termination": false,
  "priority": {
    "enabled": true,
    "session_signal": []
  },
  "response_headers": {
    "hsts": {},
    "cors": {},
    "x_frame_options": null,
    "x_content_type_options": false,
    "referrer_policy": null
  },
  "content_negotiation": {
    "enabled": false,
    "encodings": [
      "zstd",
      "gzip"
    ],
    "file_types": [
      "css",
      "js",
      "html"
    ]
  },
  "logging": {
    "enabled": false
  },
  "threat_intel": {
    "mode": "log",
    "abuse": "log",
    "tor": "log"
  },
  "redirects": [],
  "origin_host": null,
  "origin_sni": null,
  "maintenance_mode": false,
  "auto_mitigation": false,
  "defense_posture": null,
  "protection_allowlist": null,
  "edges": [],
  "protection_rules": [],
  "append_trailing_slash": false,
  "exceed_no_rtbh": false,
  "exceed_no_flowspec": false,
  "origin_kind": "dynamic",
  "alias_limit": 20,
  "serving_edges": [
    "shared-1"
  ],
  "posture_drift": [],
  "baseline": null
}
Answer 200: replace_origins
{
  "fqdn": "a.example.com",
  "tenant": "tenant-a",
  "enabled": true,
  "mode": "https",
  "ports": [
    443
  ],
  "udp_ports": [],
  "created": "2026-10-01T00:00:00+00:00",
  "created_by": "",
  "account_id": "acc-a",
  "network": {
    "vips": [
      {
        "anycast_ipv4": "203.0.113.1",
        "anycast_ipv6": "2001:db8:a::1"
      }
    ],
    "pool": null,
    "edge_hostname": "a-example-com.edge.protect.zeroservices.eu"
  },
  "network_protection": {
    "enabled": true
  },
  "backend": {
    "algorithm": "roundrobin",
    "health_check": {
      "method": "GET",
      "path": "/",
      "host": null,
      "interval": "10s",
      "rise": 2,
      "fall": 3,
      "expect": null
    }
  },
  "origins": [
    {
      "name": "origin-1",
      "address": "192.0.2.10",
      "port": 443,
      "tls": true,
      "ssl_verify": true,
      "weight": 100,
      "maxconn": 100
    }
  ],
  "waf": {
    "enabled": true,
    "fail_open": true,
    "mode": "On",
    "paranoia_level": 1,
    "inbound_threshold": 5,
    "disabled_rules": []
  },
  "rate_limiting": {
    "challenge_req_per_10s": 200,
    "req_per_10s": 800,
    "conn_per_10s": 50,
    "conn_drop_per_10s": 500,
    "path_default": 1000,
    "path_aggregate_per_10s": 5000,
    "geo_tcp_reject_sess_rate": 200,
    "ja4_enabled": false,
    "ja4_req_per_10s": 100,
    "ja4_action": "challenge"
  },
  "pow": {
    "enabled": true,
    "difficulty": 18,
    "cookie_ttl": 86400,
    "always_challenge": false,
    "exempt_crawlers": true,
    "bind_ip": true,
    "botcheck_per_10s": 1000
  },
  "admission": {
    "enabled": false,
    "credit_required": 5,
    "credit_window": "60s",
    "known_ttl": "24h",
    "unknown_conn_rate": 1500,
    "table_size": "1m"
  },
  "ssl": {
    "mode": "acme",
    "acme_provider": "zerossl",
    "acme_email": null,
    "https_redirect": false,
    "cert_status": "none",
    "cert_issued": null,
    "cert_expires": null,
    "cert_last_renewal": null,
    "days_remaining": null
  },
  "aliases": [
    {
      "fqdn": "www.a.example.com",
      "ssl": {
        "mode": "acme",
        "acme_provider": "zerossl",
        "acme_email": null,
        "https_redirect": false,
        "cert_status": "none",
        "cert_issued": null,
        "cert_expires": null,
        "cert_last_renewal": null,
        "days_remaining": null
      }
    }
  ],
  "cache": {
    "enabled": true
  },
  "slow_http": {
    "enabled": true,
    "timeout_http_request": "5s",
    "timeout_client_fin": "5s"
  },
  "tcp_timeout": {
    "client": "30s",
    "server": "30s",
    "tunnel": "30s"
  },
  "udp_timeout": {
    "idle": "300s",
    "unreplied": "30s",
    "unreplied_pkts": 32,
    "src_pps": 0
  },
  "sni_policy": "allowlist",
  "proxy_protocol": false,
  "tls_termination": false,
  "priority": {
    "enabled": true,
    "session_signal": []
  },
  "response_headers": {
    "hsts": {
      "enabled": false,
      "max_age": 31536000,
      "include_subdomains": false,
      "preload": false
    },
    "cors": {
      "enabled": false,
      "rules": []
    },
    "x_frame_options": null,
    "x_content_type_options": false,
    "referrer_policy": null
  },
  "content_negotiation": {
    "enabled": false,
    "encodings": [
      "zstd",
      "gzip"
    ],
    "file_types": [
      "css",
      "js",
      "html"
    ]
  },
  "logging": {
    "enabled": false
  },
  "threat_intel": {
    "mode": "log",
    "abuse": "log",
    "tor": "log"
  },
  "redirects": [],
  "origin_host": null,
  "origin_sni": null,
  "maintenance_mode": false,
  "auto_mitigation": false,
  "defense_posture": null,
  "protection_allowlist": null,
  "edges": [],
  "protection_rules": [],
  "append_trailing_slash": false,
  "exceed_no_rtbh": false,
  "exceed_no_flowspec": false,
  "origin_kind": "dynamic",
  "alias_limit": 20,
  "serving_edges": [
    "shared-1"
  ],
  "posture_drift": [],
  "baseline": null
}

Fields

Part of the service: send them with PATCH /services/{fqdn}.

Field Type Notes
ports list of integer or string at most 8; may be null; TCP ports the edge accepts, up to 8 entries. https: 443 and 80 by default, any further port is plain HTTP to the same port on your origins. tcp: also ranges ("40000-40009") and mappings ("40000-40009=50000": edge range to origin start)
udp_ports list of integer or string at most 8; may be null; UDP ports of a tcp or anyconnect vService, up to 8 entries: forwarded to your origins, to the same port unless mapped (ranges and mappings tcp only). Not for https (no UDP listener). Empty: all UDP to the vService's addresses is dropped
proxy_protocol boolean may be null; tcp and anyconnect: send a PROXY protocol v2 header with the client address to your origins, before each TCP connection and each forwarded UDP datagram. Your origin must expect it
tls_termination boolean may be null; tcp only: the edge terminates TLS with the vService's certificate and opens its own connection to each origin as its tls and ssl_verify say. Until a certificate exists, TLS passes through
Entry Edge ports Origin ports
443 443 443
"40000-40009" 40000–40009 the same
"40000-40009=50000" 40000–40009 50000–50009

Read-only fields

Set by support.

Field Type Notes
tcp_timeout TcpTimeout read-only; Set by support on request
udp_timeout UdpTimeout read-only; Set by support on request
sni_policy string one of allowlist, off; default "allowlist"; read-only; Set by support on request

TcpTimeout

Field Type Notes
client string default "30s"; Time the edge waits for the client to send or acknowledge data: a number with the unit us, ms, s, m, h or d, such as 30s
server string default "30s"; Time the edge waits for your origin to send or acknowledge data: a number with the unit us, ms, s, m, h or d, such as 30s
tunnel string default "30s"; Inactivity timeout of tunnelled connections such as WebSocket, both directions: a number with the unit us, ms, s, m, h or d, such as 30s

UdpTimeout

Field Type Notes
idle string default "300s"; A flow without packets in either direction for this long ends: a number with the unit us, ms, s, m, h or d, such as 300s; at least 1 s
unreplied string default "30s"; A flow your origin has not answered within this long ends, so spoofed sources cannot hold flow entries: a number with the unit us, ms, s, m, h or d, such as 30s
unreplied_pkts integer 0–65535; default 32; Packets a flow may send before your origin answers; 0: no cap, for one-way protocols
src_pps integer 0–100000000; default 0; Forwarded packets per second per source over all its flows; 0: no limit

Example

curl -s -X PATCH -H "Authorization: Bearer $ZP_KEY" -H 'Content-Type: application/json' \
  $API/services/vpn.example.com \
  -d '{"ports": [443, "40000-40009=50000"], "udp_ports": [51820], "proxy_protocol": true}'

Behaviour

  • TCP and UDP are each optional; one protocol's entries must not overlap. With one TCP port the edge connects to the origin's port; with several, each keeps its number.
  • Origins see edge addresses: allow them (support has the list).
  • proxy_protocol: a PROXY v2 header before each TCP connection and UDP datagram: client and service address and port; for TCP also the Ray ID (0x05) and, with sni_policy: allowlist, the server name (0x02). Set up the receiver first; trust edge addresses only:
Software Setting
nginx listen … proxy_protocol; with set_real_ip_from <edge addresses>;
HAProxy bind … accept-proxy
dnsdist setProxyProtocolACL({<edge addresses>})
PowerDNS proxy-protocol-from=<edge addresses>
  • tls_termination: the edge presents the service certificate (TLS) and connects per origin tls/ssl_verify; TLS passes through until a certificate exists. Port 80 then serves only ACME HTTP-01 (using port 80 yourself: upload the certificate).
  • PROXY protocol and TLS at the edge do not work with the forward proxy for fixed outgoing addresses.
  • sni_policy: allowlist drops foreign server names; VLESS with REALITY needs off.
Protection, per IPv4 address or IPv6 /64 TCP UDP
Blocklists, country/ASN/IP rules, malformed packets, fragments yes yes
New connections or flows conn_per_10s conn_per_10s
Silent drop of a flooding source conn_drop_per_10s —
From source ports 7, 17, 19, 53, 111, 123, 137, 161, 389, 427, 520, 623 — dropped
Packets before the origin answers — udp_timeout.unreplied_pkts; 0 for one-way protocols
Packets per second, all flows — udp_timeout.src_pps
  • UDP 53 is DNS: only queries pass; each query is a flow, so conn_per_10s (default 50, Rate limits) is too low for busy resolvers.
  • A UDP client sticks to one origin across edges, spread by weight; if that origin fails its health check, the client moves at once and stays. weight 0 drains; without a healthy origin UDP stops; UDP-only origins count as healthy.
  • udp_ports exist for tcp and anyconnect services; an https service has none (no QUIC).
  • anyconnect: TCP 443 (TLS) passes through, UDP 443 (DTLS) is forwarded; the health check verifies the certificate: send the intermediate.
  • IPv4 clients to an IPv6 origin: UDP at most 1480 bytes (WireGuard MTU 1420).
After a client changes edge
TCP, AnyConnect client reconnects
WireGuard, QUIC (HTTP/3, Hysteria2) continues
OpenVPN over UDP needs float
Limit
TCP or UDP entries per service 8 each
Forwarded UDP ports, ranges per port 1024
UDP clients / TCP connections per edge, origin address and port ~20,000 / ~32,000

In the portal: Advanced → Ports; PROXY protocol and TLS at the edge under Origins.