Fortinet white logo
Fortinet white logo

Administration Guide

ZTNA domain matching

ZTNA domain matching

A domain filter can be used within ZTNA real server object definitions in the absence of an address, or when you need to match multiple servers in a domain that are not defined in an address or address group. Domain matching is helpful when used with TCP forwarding, where the destinations are configured on the FortiClient but they are not individually configured on the FortiGate ZTNA server configurations. When using domain matching, the destination must be resolvable by the FortiGate.

config firewall access-proxy
    edit <name>
        config api-gateway
            edit <ID>
                config realservers
                    edit 1
                        set domain <domain>
                    next
                end
            next
        end
    next
end

Where <domain> can be a substring of the actual domain. For example, st.com will match test.com or first.com, but will not match trait.com.

Example 1: Wildcard domain match ztnademo.com

In this example, several TCP forwarding entries have been added on the EMS server and pushed to the FortiClient endpoint:

  • finserver.ztnademo.com

  • web1.ztnademo.com

  • web2.ztnademo.com

All 3 servers are resolvable on the FortiGate.

On the ZTNA real server definition on the FortiGate, a wildcard domain filter is used to match everything under ztnademo.com. No address object is defined.

config firewall access-proxy
    edit "ZTNA-server"
        set vip "ZTNA-server"
        config api-gateway
            edit 1
                set url-map "/tcp"
                set service tcp-forwarding
                config realservers
                    edit 1
                        set domain "ztnademo.com"
                        set mappedport 9043 
                    next
                end
            next
        end
    next
end

When accessing these servers from the remote endpoint, each of these can be accessed.

# execute log filter field subtype ztna
# execute log display
164 logs found.
10 logs returned.

1: date=2025-07-29 time=15:37:59 eventtime=1753828678973202823 tz="-0700" logid="0005000024" type="traffic" subtype="ztna" level="notice" vd="root" srcip=10.0.3.2 srcport=62031 srcintf="port3" srcintfrole="wan" dstcountry="Reserved" srccountry="Reserved" dstip=10.0.3.10 dstport=9043 dstintf="port2" dstintfrole="dmz" sessionid=15859 srcuuid="b458a65a-f759-51ea-d7df-ef2e750026d1" service="tcp/9043" proxyapptype="ztna-proxy" proto=6 action="accept" policyid=17 policytype="policy" poluuid="8caee736-6cc5-51f0-837b-5bc10419a710" policyname="ZTNA-traffic" trandisp="dnat" tranip=10.88.0.4 tranport=9043 appcat="unscanned" duration=87 gatewayid=1 vip="ZTNA-server" accessproxy="ZTNA-server" clientdevicemanageable="manageable" clientcert="yes" wanin=303035 rcvdbyte=303035 wanout=2964 lanin=4943 sentbyte=4943 lanout=306164

2: date=2025-07-29 time=15:35:20 eventtime=1753828520045168898 tz="-0700" logid="0005000024" type="traffic" subtype="ztna" level="notice" vd="root" srcip=10.0.3.2 srcport=62016 srcintf="port3" srcintfrole="wan" dstcountry="Reserved" srccountry="Reserved" dstip=10.0.3.10 dstport=9043 dstintf="port2" dstintfrole="dmz" sessionid=15807 srcuuid="b458a65a-f759-51ea-d7df-ef2e750026d1" service="tcp/9043" proxyapptype="ztna-proxy" proto=6 action="accept" policyid=17 policytype="policy" poluuid="8caee736-6cc5-51f0-837b-5bc10419a710" policyname="ZTNA-traffic" trandisp="dnat" tranip=10.88.0.5 tranport=9043 appcat="unscanned" duration=21 gatewayid=1 vip="ZTNA-server" accessproxy="ZTNA-server" clientdevicemanageable="manageable" clientcert="yes" wanin=31581 rcvdbyte=31581 wanout=3699 lanin=5683 sentbyte=5683 lanout=34710

3: date=2025-07-29 time=15:35:20 eventtime=1753828520046436756 tz="-0700" logid="0005000024" type="traffic" subtype="ztna" level="notice" vd="root" srcip=10.0.3.2 srcport=62019 srcintf="port3" srcintfrole="wan" dstcountry="Reserved" srccountry="Reserved" dstip=10.0.3.10 dstport=9043 dstintf="port2" dstintfrole="dmz" sessionid=15812 srcuuid="b458a65a-f759-51ea-d7df-ef2e750026d1" service="tcp/9043" proxyapptype="ztna-proxy" proto=6 action="accept" policyid=17 policytype="policy" poluuid="8caee736-6cc5-51f0-837b-5bc10419a710" policyname="ZTNA-traffic" trandisp="dnat" tranip=10.88.0.3 tranport=9043 appcat="unscanned" duration=6 gatewayid=1 vip="ZTNA-server" accessproxy="ZTNA-server" clientdevicemanageable="manageable" clientcert="yes" wanin=302887 rcvdbyte=302887 wanout=2896 lanin=4875 sentbyte=4875 lanout=306016

Example 2: Wildcard domain match server.ztnademo.com

In this example, the same servers are pushed to the FortiClient endpoint. However, a more specific domain filter is applied to only allow *server.ztnademo.com.

config firewall access-proxy
    edit "ZTNA-server"
        set vip "ZTNA-server"
        config api-gateway
            edit 1
                set url-map "/tcp"
                set service tcp-forwarding
                config realservers
                    edit 1
                        set domain "server.ztnademo.com"
                        set mappedport 9043 
                    next
                end
            next
        end
    next
end

When accessing these servers, only finserver.ztnademo.com can be accessed. Other servers will return with an error.

# execute log filter field subtype ztna
# execute log display
189 logs found.
10 logs returned.

1: date=2025-07-29 time=15:46:20 eventtime=1753829180317849279 tz="-0700" logid="0005000024" type="traffic" subtype="ztna" level="notice" vd="root" srcip=10.0.3.2 srcport=62060 srcintf="port3" srcintfrole="wan" dstcountry="Reserved" srccountry="Reserved" dstip=10.0.3.10 dstport=9043 dstintf="port2" dstintfrole="dmz" sessionid=15987 srcuuid="b458a65a-f759-51ea-d7df-ef2e750026d1" service="tcp/9043" proxyapptype="ztna-proxy" proto=6 action="accept" policyid=17 policytype="policy" poluuid="8caee736-6cc5-51f0-837b-5bc10419a710" policyname="ZTNA-traffic" trandisp="dnat" tranip=10.88.0.5 tranport=9043 appcat="unscanned" duration=95 gatewayid=1 vip="ZTNA-server" accessproxy="ZTNA-server" clientdevicemanageable="manageable" clientcert="yes" wanin=31581 rcvdbyte=31581 wanout=3624 lanin=5608 sentbyte=5608 lanout=34710

2: date=2025-07-29 time=15:45:56 eventtime=1753829155650041262 tz="-0700" logid="0005000024" type="traffic" subtype="ztna" level="notice" vd="root" srcip=10.0.3.2 srcport=62122 srcintf="port3" srcintfrole="wan" dstcountry="Reserved" srccountry="Reserved" dstip=10.0.3.10 dstport=9043 dstintf="root" dstintfrole="undefined" sessionid=16026 srcuuid="b458a65a-f759-51ea-d7df-ef2e750026d1" service="tcp/9043" proxyapptype="http" proto=6 action="deny" policyid=17 policytype="policy" poluuid="8caee736-6cc5-51f0-837b-5bc10419a710" policyname="ZTNA-traffic" trandisp="dnat" tranip=10.0.3.10 tranport=9043 appcat="unscanned" duration=0 vip="ZTNA-server" accessproxy="ZTNA-server" clientdevicemanageable="manageable" clientcert="yes" msg="Traffic denied because failed to find a server: reason: Cannot find the real server in the API gateway., hostname: 10.0.3.10" wanin=0 rcvdbyte=0 wanout=0 lanin=1963 sentbyte=1963 lanout=3014 crscore=30 craction=131072 crlevel="high"

Example 3: Domain match precedence

In this example, an address group is specified in the real server definition, which includes all three server’s FQDN. A domain filter is configured to only filter on web.

config firewall addrgrp
    edit "ztnademo-group"
        set member "web1.ztnademo.com" "web2.ztnademo.com" "finserver.ztnademo.com"
    next
end
config firewall access-proxy
    edit "ZTNA-server"
        set vip "ZTNA-server"
        config api-gateway
            edit 1
                set url-map "/tcp"
                set service tcp-forwarding
                config realservers
                    edit 1
                        set address "ztnademo-group"
                        set domain "web"
                        set mappedport 9043 
                    next
                end
            next
        end
    next
end

When accessing web1.ztnademo.com or web2.ztnademo.com, wad debug shows that domain matching takes precedence over address matching:

# diagnose wad debug enable category all
Debug messages will be on for 30 minutes.
# diagnose wad debug enable level verbose
Debug messages will be on for 30 minutes. 
# diagnose debug enable

GET /tcp?address=web1.ztnademo.com&port=9043&tls=0 HTTP/1.1
Host: 10.0.3.10:9043
User-Agent: Forticlient
Accept: */*
Authorization:
Connection: Upgrade
Cookie:
Upgrade: tcp-forwarding/1.0
X-Fos-Auth:

…
[I][p:2195][s:1334][r:10] wad_vs_gwy_tcp_dst_ovrd           :3392  16363:ZTNA-server:1: req(0x7f4122a79048) found real server by matching domain(web).
[W][p:2195][s:1334][r:10] wad_vs_proxy_dns_resolve          :3280  req(0x7f4122a79048) vs DNS request name=web1.ztnademo.com len=17 type/pref=0/0
[I][p:2195][s:1334][r:10] __wad_dns_send_query              :863   0:0: sending DNS request for remote peer web1.ztnademo.com id=0 IPv4
…
[I][p:2195][s:1334][r:10] wad_vs_proxy_dns_request_done     :3229  req(0x7f4122a79048) vs DNS resolved: 10.88.0.3
…

The following debug indicates DNS was able to resolve the requested destination, and domain matching was successful. Address matching was not attempted.

[I][p:2195][s:1334][r:10] wad_http_req_exec_on_vs_dns_ready :12216 req(0x7f4122a79048) vs DNS ready: dns_resolved(1), domain_matched(1), addr_matched(0)
[V][p:2195][s:1334][r:10] wad_get_dst_intf_idx2             :174   Matched route cache.local=0, oif=4
[V][p:2195][s:1334][r:10] wad_http_req_get_dst_intf         :11982 vd=0 dst=10.88.0.3 ifidx=4

On the other hand, when accessing finserver.ztnademo.com, domain matching is unsuccessful, but address matching was successful.

GET /tcp?address=finserver.ztnademo.com&port=9043&tls=0 HTTP/1.1
Host: 10.0.3.10:9043
User-Agent: Forticlient
Accept: */*
Authorization:
Connection: Upgrade
Cookie:
Upgrade: tcp-forwarding/1.0
X-Fos-Auth:

…
[I][p:2195][s:1347][r:11] wad_http_req_exec_on_vs_dns_ready :12216 req(0x7f4122a79048) vs DNS ready: dns_resolved(1), domain_matched(0), addr_matched(1)
[V][p:2195][s:1347][r:11] wad_get_dst_intf_idx2             :174   Matched route cache.local=0, oif=4
[V][p:2195][s:1347][r:11] wad_http_req_get_dst_intf         :11982 vd=0 dst=10.88.0.5 ifidx=4

ZTNA domain matching

ZTNA domain matching

A domain filter can be used within ZTNA real server object definitions in the absence of an address, or when you need to match multiple servers in a domain that are not defined in an address or address group. Domain matching is helpful when used with TCP forwarding, where the destinations are configured on the FortiClient but they are not individually configured on the FortiGate ZTNA server configurations. When using domain matching, the destination must be resolvable by the FortiGate.

config firewall access-proxy
    edit <name>
        config api-gateway
            edit <ID>
                config realservers
                    edit 1
                        set domain <domain>
                    next
                end
            next
        end
    next
end

Where <domain> can be a substring of the actual domain. For example, st.com will match test.com or first.com, but will not match trait.com.

Example 1: Wildcard domain match ztnademo.com

In this example, several TCP forwarding entries have been added on the EMS server and pushed to the FortiClient endpoint:

  • finserver.ztnademo.com

  • web1.ztnademo.com

  • web2.ztnademo.com

All 3 servers are resolvable on the FortiGate.

On the ZTNA real server definition on the FortiGate, a wildcard domain filter is used to match everything under ztnademo.com. No address object is defined.

config firewall access-proxy
    edit "ZTNA-server"
        set vip "ZTNA-server"
        config api-gateway
            edit 1
                set url-map "/tcp"
                set service tcp-forwarding
                config realservers
                    edit 1
                        set domain "ztnademo.com"
                        set mappedport 9043 
                    next
                end
            next
        end
    next
end

When accessing these servers from the remote endpoint, each of these can be accessed.

# execute log filter field subtype ztna
# execute log display
164 logs found.
10 logs returned.

1: date=2025-07-29 time=15:37:59 eventtime=1753828678973202823 tz="-0700" logid="0005000024" type="traffic" subtype="ztna" level="notice" vd="root" srcip=10.0.3.2 srcport=62031 srcintf="port3" srcintfrole="wan" dstcountry="Reserved" srccountry="Reserved" dstip=10.0.3.10 dstport=9043 dstintf="port2" dstintfrole="dmz" sessionid=15859 srcuuid="b458a65a-f759-51ea-d7df-ef2e750026d1" service="tcp/9043" proxyapptype="ztna-proxy" proto=6 action="accept" policyid=17 policytype="policy" poluuid="8caee736-6cc5-51f0-837b-5bc10419a710" policyname="ZTNA-traffic" trandisp="dnat" tranip=10.88.0.4 tranport=9043 appcat="unscanned" duration=87 gatewayid=1 vip="ZTNA-server" accessproxy="ZTNA-server" clientdevicemanageable="manageable" clientcert="yes" wanin=303035 rcvdbyte=303035 wanout=2964 lanin=4943 sentbyte=4943 lanout=306164

2: date=2025-07-29 time=15:35:20 eventtime=1753828520045168898 tz="-0700" logid="0005000024" type="traffic" subtype="ztna" level="notice" vd="root" srcip=10.0.3.2 srcport=62016 srcintf="port3" srcintfrole="wan" dstcountry="Reserved" srccountry="Reserved" dstip=10.0.3.10 dstport=9043 dstintf="port2" dstintfrole="dmz" sessionid=15807 srcuuid="b458a65a-f759-51ea-d7df-ef2e750026d1" service="tcp/9043" proxyapptype="ztna-proxy" proto=6 action="accept" policyid=17 policytype="policy" poluuid="8caee736-6cc5-51f0-837b-5bc10419a710" policyname="ZTNA-traffic" trandisp="dnat" tranip=10.88.0.5 tranport=9043 appcat="unscanned" duration=21 gatewayid=1 vip="ZTNA-server" accessproxy="ZTNA-server" clientdevicemanageable="manageable" clientcert="yes" wanin=31581 rcvdbyte=31581 wanout=3699 lanin=5683 sentbyte=5683 lanout=34710

3: date=2025-07-29 time=15:35:20 eventtime=1753828520046436756 tz="-0700" logid="0005000024" type="traffic" subtype="ztna" level="notice" vd="root" srcip=10.0.3.2 srcport=62019 srcintf="port3" srcintfrole="wan" dstcountry="Reserved" srccountry="Reserved" dstip=10.0.3.10 dstport=9043 dstintf="port2" dstintfrole="dmz" sessionid=15812 srcuuid="b458a65a-f759-51ea-d7df-ef2e750026d1" service="tcp/9043" proxyapptype="ztna-proxy" proto=6 action="accept" policyid=17 policytype="policy" poluuid="8caee736-6cc5-51f0-837b-5bc10419a710" policyname="ZTNA-traffic" trandisp="dnat" tranip=10.88.0.3 tranport=9043 appcat="unscanned" duration=6 gatewayid=1 vip="ZTNA-server" accessproxy="ZTNA-server" clientdevicemanageable="manageable" clientcert="yes" wanin=302887 rcvdbyte=302887 wanout=2896 lanin=4875 sentbyte=4875 lanout=306016

Example 2: Wildcard domain match server.ztnademo.com

In this example, the same servers are pushed to the FortiClient endpoint. However, a more specific domain filter is applied to only allow *server.ztnademo.com.

config firewall access-proxy
    edit "ZTNA-server"
        set vip "ZTNA-server"
        config api-gateway
            edit 1
                set url-map "/tcp"
                set service tcp-forwarding
                config realservers
                    edit 1
                        set domain "server.ztnademo.com"
                        set mappedport 9043 
                    next
                end
            next
        end
    next
end

When accessing these servers, only finserver.ztnademo.com can be accessed. Other servers will return with an error.

# execute log filter field subtype ztna
# execute log display
189 logs found.
10 logs returned.

1: date=2025-07-29 time=15:46:20 eventtime=1753829180317849279 tz="-0700" logid="0005000024" type="traffic" subtype="ztna" level="notice" vd="root" srcip=10.0.3.2 srcport=62060 srcintf="port3" srcintfrole="wan" dstcountry="Reserved" srccountry="Reserved" dstip=10.0.3.10 dstport=9043 dstintf="port2" dstintfrole="dmz" sessionid=15987 srcuuid="b458a65a-f759-51ea-d7df-ef2e750026d1" service="tcp/9043" proxyapptype="ztna-proxy" proto=6 action="accept" policyid=17 policytype="policy" poluuid="8caee736-6cc5-51f0-837b-5bc10419a710" policyname="ZTNA-traffic" trandisp="dnat" tranip=10.88.0.5 tranport=9043 appcat="unscanned" duration=95 gatewayid=1 vip="ZTNA-server" accessproxy="ZTNA-server" clientdevicemanageable="manageable" clientcert="yes" wanin=31581 rcvdbyte=31581 wanout=3624 lanin=5608 sentbyte=5608 lanout=34710

2: date=2025-07-29 time=15:45:56 eventtime=1753829155650041262 tz="-0700" logid="0005000024" type="traffic" subtype="ztna" level="notice" vd="root" srcip=10.0.3.2 srcport=62122 srcintf="port3" srcintfrole="wan" dstcountry="Reserved" srccountry="Reserved" dstip=10.0.3.10 dstport=9043 dstintf="root" dstintfrole="undefined" sessionid=16026 srcuuid="b458a65a-f759-51ea-d7df-ef2e750026d1" service="tcp/9043" proxyapptype="http" proto=6 action="deny" policyid=17 policytype="policy" poluuid="8caee736-6cc5-51f0-837b-5bc10419a710" policyname="ZTNA-traffic" trandisp="dnat" tranip=10.0.3.10 tranport=9043 appcat="unscanned" duration=0 vip="ZTNA-server" accessproxy="ZTNA-server" clientdevicemanageable="manageable" clientcert="yes" msg="Traffic denied because failed to find a server: reason: Cannot find the real server in the API gateway., hostname: 10.0.3.10" wanin=0 rcvdbyte=0 wanout=0 lanin=1963 sentbyte=1963 lanout=3014 crscore=30 craction=131072 crlevel="high"

Example 3: Domain match precedence

In this example, an address group is specified in the real server definition, which includes all three server’s FQDN. A domain filter is configured to only filter on web.

config firewall addrgrp
    edit "ztnademo-group"
        set member "web1.ztnademo.com" "web2.ztnademo.com" "finserver.ztnademo.com"
    next
end
config firewall access-proxy
    edit "ZTNA-server"
        set vip "ZTNA-server"
        config api-gateway
            edit 1
                set url-map "/tcp"
                set service tcp-forwarding
                config realservers
                    edit 1
                        set address "ztnademo-group"
                        set domain "web"
                        set mappedport 9043 
                    next
                end
            next
        end
    next
end

When accessing web1.ztnademo.com or web2.ztnademo.com, wad debug shows that domain matching takes precedence over address matching:

# diagnose wad debug enable category all
Debug messages will be on for 30 minutes.
# diagnose wad debug enable level verbose
Debug messages will be on for 30 minutes. 
# diagnose debug enable

GET /tcp?address=web1.ztnademo.com&port=9043&tls=0 HTTP/1.1
Host: 10.0.3.10:9043
User-Agent: Forticlient
Accept: */*
Authorization:
Connection: Upgrade
Cookie:
Upgrade: tcp-forwarding/1.0
X-Fos-Auth:

…
[I][p:2195][s:1334][r:10] wad_vs_gwy_tcp_dst_ovrd           :3392  16363:ZTNA-server:1: req(0x7f4122a79048) found real server by matching domain(web).
[W][p:2195][s:1334][r:10] wad_vs_proxy_dns_resolve          :3280  req(0x7f4122a79048) vs DNS request name=web1.ztnademo.com len=17 type/pref=0/0
[I][p:2195][s:1334][r:10] __wad_dns_send_query              :863   0:0: sending DNS request for remote peer web1.ztnademo.com id=0 IPv4
…
[I][p:2195][s:1334][r:10] wad_vs_proxy_dns_request_done     :3229  req(0x7f4122a79048) vs DNS resolved: 10.88.0.3
…

The following debug indicates DNS was able to resolve the requested destination, and domain matching was successful. Address matching was not attempted.

[I][p:2195][s:1334][r:10] wad_http_req_exec_on_vs_dns_ready :12216 req(0x7f4122a79048) vs DNS ready: dns_resolved(1), domain_matched(1), addr_matched(0)
[V][p:2195][s:1334][r:10] wad_get_dst_intf_idx2             :174   Matched route cache.local=0, oif=4
[V][p:2195][s:1334][r:10] wad_http_req_get_dst_intf         :11982 vd=0 dst=10.88.0.3 ifidx=4

On the other hand, when accessing finserver.ztnademo.com, domain matching is unsuccessful, but address matching was successful.

GET /tcp?address=finserver.ztnademo.com&port=9043&tls=0 HTTP/1.1
Host: 10.0.3.10:9043
User-Agent: Forticlient
Accept: */*
Authorization:
Connection: Upgrade
Cookie:
Upgrade: tcp-forwarding/1.0
X-Fos-Auth:

…
[I][p:2195][s:1347][r:11] wad_http_req_exec_on_vs_dns_ready :12216 req(0x7f4122a79048) vs DNS ready: dns_resolved(1), domain_matched(0), addr_matched(1)
[V][p:2195][s:1347][r:11] wad_get_dst_intf_idx2             :174   Matched route cache.local=0, oif=4
[V][p:2195][s:1347][r:11] wad_http_req_get_dst_intf         :11982 vd=0 dst=10.88.0.5 ifidx=4