By Manny Fernandez

July 27, 2026

Mastering FortiGate Service Objects: Custom Services, Groups, Categories, and the Options Nobody Uses

1. Executive Summary

Objective

This guide takes the FortiGate service object from the five fields everyone knows to the full option set that FortiOS actually exposes, including source port constraints, destination scoping by IP range or FQDN, per service session TTL and TCP/UDP timers, RST range validation, session helper overrides, application aware services, and explicit proxy services. By the end you will be able to build service objects that enforce far more than a destination port, and you will know exactly which of those options quietly break traffic when misapplied.

Target Audience

Network Security Engineers, Firewall Administrators, NOC and SOC engineers who own FortiGate policy, and Solutions Architects who write firewall standards. Also relevant to anyone migrating from a legacy vendor where port objects and address objects were separate concepts and now need to map that model cleanly onto FortiOS.


2. Prerequisites and Architecture

Assumed Knowledge

  • Comfort with the FortiOS CLI: config, edit, set, next, end, and the difference between show and show full-configuration.
  • Understanding of the FortiGate policy model: interface pair, source address, destination address, schedule, service, action, and the implicit deny at the bottom of the list.
  • Working knowledge of the FortiGate packet flow, specifically that destination NAT is applied before policy lookup, and that the session table is the source of truth for what the firewall actually decided.
  • TCP and UDP fundamentals: three way handshake, FIN and RST semantics, ephemeral source port behavior, and ICMP type and code pairs.
  • Basic familiarity with diagnose sys session list and diagnose debug flow.

Environment and Lab Requirements

Requirement Detail
Platform FortiGate hardware or FortiGate-VM
FortiOS version 7.4.x or 7.6.x. Options noted as version sensitive are called out inline. Most of this applies back to 6.4.
Licensing Base FortiOS for everything except application aware services, which require an active FortiGuard Application Control subscription
Access Super admin, or an admin profile with read and write on Firewall Objects and Firewall Policy
Lab environment EVE-NG, GNS3, Proxmox, VMware ESXi, or a physical unit. Two client endpoints and two server endpoints are enough to exercise every option here.
Backup Take a config backup before you begin. Several options in this guide can blackhole production traffic instantly.
Optional A second FortiGate joined to the same Security Fabric, to demonstrate fabric object synchronization

Component Table

Component Role Interface Address or FQDN
FGT-EDGE-01 FortiGate NGFW under configuration, Security Fabric root mgmt 10.0.255.10
FGT-BRANCH-01 Downstream Security Fabric member mgmt 10.0.255.20
WAN uplink Internet edge port1 198.18.10.1/24
DMZ segment Published applications port2 10.0.20.1/24
Core server segment Internal server VLAN port3 10.0.40.1/24
User segment Corporate clients and admin jump host port4 10.0.30.1/24
WEB-01 Published web application, listener on TCP 8443 port2 10.0.20.50
VIP-WEB-01 Public entry point for WEB-01, external TCP 443 to mapped TCP 8443 port1 198.18.10.50
SYSLOG-01 / SYSLOG-02 Syslog collectors, UDP 514 port3 10.0.40.10 and 10.0.40.11
ORA-DB-01 Oracle TNS listener, TCP 1521 port3 10.0.40.60
SIP-PBX-01 IP PBX, SIP signaling on UDP and TCP 5060 port3 10.0.40.70
JUMP-01 Administrative jump host port4 10.0.30.25
Partner SaaS endpoint External API consumed by internal apps n/a api.<your_domain>

3. Step by Step Implementation Workflow

Phase 0: The Object Model

Goal: Understand what a FortiOS service object actually is before you start building them.

FortiOS has exactly three tables in the service world, and they are not peers:

Table CLI path What it holds
Service Category config firewall service category A label used to group services in the GUI. Purely organizational. No matching logic.
Custom Service config firewall service custom The actual match object. Every predefined service such as HTTP, DNS, and PING is an entry in this table, not a separate read only table.
Service Group config firewall service group An ordered list of custom service members evaluated as a logical OR.

Two facts that surprise people:

  1. The roughly ninety predefined services shipped with FortiOS live in config firewall service custom. They are editable. You can change HTTPS to include TCP 8443 and every policy referencing HTTPS inherits it immediately. This is powerful and dangerous in equal measure.
  2. A service object is not limited to protocol and port. It can carry a destination IP constraint, a source port constraint, timer overrides, a session helper binding, and an application identity. Most of the FortiOS service surface area is invisible in the GUI.

Action: Confirm your starting state and get a feel for the table size.

# List every service object name currently defined
show firewall service custom | grep "edit "

# Inspect a single predefined service with every field exposed
show full-configuration firewall service custom HTTPS

# List categories
show firewall service category

# List groups
show firewall service group

GUI Verification: Navigate to Policy & Objects > Services. The list is grouped by category. Use the By Category and By Sequence toggles at the top right to change the view. The Ref. column shows how many policies reference each object.

Phase 1: Service Categories

Goal: Create a category so that operational service objects are visually separated from the predefined FortiOS catalog.

Categories carry no matching logic whatsoever. Their entire job is to make a table with two hundred entries navigable, and to give an auditor an obvious grouping. FortiOS ships with a default set that typically includes General, Web Access, File Access, Email, Network Services, Authentication, Remote Access, Tunneling, VoIP, Messaging and Other Applications, Web Proxy, and Uncategorized. Verify the exact list on your build, because it has shifted between major versions.

Action: Create two categories that reflect ownership rather than protocol family.

config firewall service category
    edit "CORP-Standard"
        set comment "Approved service objects, change controlled"
        set fabric-object enable
    next
    edit "CORP-Exception"
        set comment "Time bound exceptions, reviewed quarterly"
    next
end

The non-common part

set fabric-object enable on a category promotes it to a Security Fabric object. On the fabric root, that means the category is pushed down to member FortiGates and appears there as a read only object with a fabric badge. This is how you keep object taxonomy consistent across a fabric without FortiManager.

GUI Verification: Policy & Objects > Services, then Create New > Service Category. Once created, categories become selectable in the Category dropdown of every service object.

Phase 2: Standard Layer 4 Services and Multi Range Ports

Goal: Build a TCP/UDP/SCTP service correctly, including the multi range syntax that removes the need for three separate objects.

Action: Create a service covering a non contiguous set of ports.

config firewall service custom
    edit "SVC-WEBAPP-ALT-PORTS"
        set category "CORP-Standard"
        set protocol TCP/UDP/SCTP
        set tcp-portrange 8080 8443 9000-9100
        set comment "Alternate HTTP and HTTPS listeners plus app node range"
        set color 6
    next
end

Key syntax points:

  • TCP/UDP/SCTP is a single protocol keyword, not three. You then populate tcp-portrange, udp-portrange, and sctp-portrange independently within the same object. An object can carry all three at once.
  • Port entries are space separated. Ranges use a hyphen. A single port is written bare.
  • Setting udp-portrange on the same object is entirely valid and creates one object matching both transports.
  • set color <1-32> sets the GUI icon color. Cosmetic, but a color coded policy list is genuinely faster to audit at 2am.
config firewall service custom
    edit "SVC-CUSTOM-DNS"
        set protocol TCP/UDP/SCTP
        set tcp-portrange 5353
        set udp-portrange 5353
    next
end

GUI Verification: Policy & Objects > Services > Create New > Service. Set Type to TCP/UDP/SCTP, then add Destination Port rows. The GUI exposes a Source Port column only after you expand a port row on recent builds. That column is the subject of the next phase.

Phase 3: Source Port Constraints

Goal: Constrain the source port as well as the destination port, and understand why this is the single most common way to break a policy.

FortiOS port range syntax is:

set tcp-portrange <dst-low>-<dst-high>:<src-low>-<src-high>

Everything to the left of the colon is destination. Everything to the right is source. Omit the colon and the source is unconstrained, which is what you want in almost every case.

Action: Build a service that only matches syslog originating from a privileged source port.

config firewall service custom
    edit "SVC-SYSLOG-PRIVSRC"
        set category "CORP-Standard"
        set protocol TCP/UDP/SCTP
        set udp-portrange 514:1-1023
        set comment "Syslog from privileged source ports only"
    next
end

Legitimate uses for source port constraints are narrow but real:

  • Protocols where the source port is genuinely fixed by specification or by a hardened appliance, for example some OT and SCADA implementations, or a NetFlow exporter pinned to a static source port.
  • NTP client to server on UDP 123 to 123 where you want to exclude clients using ephemeral source ports.
  • Legacy DNS zone transfers or rsh style protocols with reserved source port behavior.

To constrain source while leaving the destination wide open, you still must supply the destination side:

config firewall service custom
    edit "SVC-ANYDST-SRC5000"
        set protocol TCP/UDP/SCTP
        set tcp-portrange 1-65535:5000-5000
    next
end

Critical warning

Ordinary client traffic uses a random ephemeral source port, typically 32768 to 60999 on Linux and 49152 to 65535 on Windows. Any service object carrying a source port constraint will silently fail to match normal client sessions, the policy will be skipped, and the traffic will fall through to the implicit deny. This is covered again in Section 5 because it accounts for a disproportionate share of real world tickets.

GUI Verification: Policy & Objects > Services, edit the object, and confirm the Source Port column shows the range you configured. If the column is not visible, the object has no source constraint.

Phase 4: ICMP, ICMPv6, and Raw IP Protocol Services

Goal: Build services for traffic that has no concept of a port.

Action: Create granular ICMP services rather than allowing the blanket ALL_ICMP.

config firewall service custom
    edit "SVC-ICMP-ECHO-REQ"
        set category "CORP-Standard"
        set protocol ICMP
        set icmptype 8
        set icmpcode 0
        set comment "Echo request only, no other ICMP types"
    next
    edit "SVC-ICMP-UNREACH-FRAGNEEDED"
        set protocol ICMP
        set icmptype 3
        set icmpcode 4
        set comment "Fragmentation needed, required for PMTUD"
    next
    edit "SVC-ICMP6-NDP-NS"
        set protocol ICMP6
        set icmptype 135
        set comment "IPv6 Neighbor Solicitation"
    next
end
  • If icmptype is left unset, the service matches every ICMP type. If icmptype is set but icmpcode is not, it matches every code within that type.
  • Blocking ICMP type 3 code 4 is a classic cause of Path MTU Discovery blackholes. If you are locking ICMP down, permit this one explicitly.

For protocols identified only by IP protocol number, use protocol IP:

config firewall service custom
    edit "SVC-GRE"
        set protocol IP
        set protocol-number 47
    next
    edit "SVC-ESP"
        set protocol IP
        set protocol-number 50
    next
    edit "SVC-OSPF"
        set protocol IP
        set protocol-number 89
    next
    edit "SVC-VRRP"
        set protocol IP
        set protocol-number 112
    next
end

The built in ALL service is simply protocol IP with protocol-number 0, which FortiOS interprets as any protocol. Inspect it to confirm:

show full-configuration firewall service custom ALL

GUI Verification: Policy & Objects > Services > Create New > Service, set Type to ICMP, ICMPv6, or IP. The Type dropdown swaps the visible fields to Type and Code, or to Protocol Number.

Phase 5: Scoping a Service to a Destination IP Range or FQDN

Goal: Embed a destination address constraint inside the service object itself.

This is one of the least used capabilities in FortiOS. The iprange and fqdn fields add a destination IP condition to the service, which is ANDed with the policy destination address. The practical value is that you can define a single reusable object meaning “syslog, but only to the sanctioned collectors” and reuse it across dozens of policies without maintaining a parallel address group in every one.

Action: Create an IP scoped service.

config firewall service custom
    edit "SVC-SYSLOG-TO-COLLECTORS"
        set category "CORP-Standard"
        set protocol TCP/UDP/SCTP
        set iprange 10.0.40.10-10.0.40.11
        set udp-portrange 514
        set comment "UDP 514 permitted only toward sanctioned collectors"
    next
end

Action: Create an FQDN scoped service for a partner API whose address changes.

config firewall service custom
    edit "SVC-PARTNER-API"
        set category "CORP-Standard"
        set protocol TCP/UDP/SCTP
        set fqdn "api.<your_domain>"
        set tcp-portrange 443
        set comment "TLS to the partner API endpoint only"
    next
end

Behavior and constraints:

  • iprange accepts a single address, a hyphenated range, or a subnet in the forms FortiOS accepts for that field. It does not accept a firewall address object name.
  • iprange and fqdn are mutually exclusive. Setting one clears the other.
  • The constraint applies to the destination only. There is no source equivalent in the service object.
  • FQDN resolution uses the FortiGate’s own DNS configuration under config system dns. If resolution fails, the service matches nothing and the policy is skipped. Treat FortiGate DNS as a hard dependency for any policy using an FQDN scoped service.
  • For inbound flows traversing a VIP, the destination address the service sees is the post DNAT address, because destination NAT precedes policy lookup. Scope to the mapped internal address, not the public one.

GUI Verification: Policy & Objects > Services, edit a TCP/UDP/SCTP service. The IP/FQDN field sits directly under the Type selector. Entering a value there writes iprange or fqdn depending on whether the input parses as an address.

Phase 6: Per Service Session TTL and Protocol Timers

Goal: Override how long the FortiGate holds a session for a specific service, without touching global timers.

This is the correct place to solve the classic “my database connection drops after an hour of idle” complaint. The alternative, raising the global session TTL, inflates the session table for every flow on the box and is a memory and conserve mode risk.

Action: Give the Oracle listener a long idle timeout while leaving everything else alone.

config firewall service custom
    edit "SVC-ORACLE-TNS-LONGLIVED"
        set category "CORP-Standard"
        set protocol TCP/UDP/SCTP
        set tcp-portrange 1521
        set session-ttl 43200
        set comment "12 hour idle tolerance for Oracle TNS sessions"
    next
end

set session-ttl 0 means inherit rather than override. The platform enforces a minimum, typically 300 seconds, so values below that are rejected.

The order of precedence for session TTL, most specific first:

  1. set session-ttl on the matching config firewall policy entry
  2. set session-ttl on the matching service object
  3. A matching port entry under config system session-ttl
  4. The global default under config system session-ttl (set default)

Action: Tune the TCP and UDP state timers for a single service.

config firewall service custom
    edit "SVC-CHATTY-UDP-APP"
        set protocol TCP/UDP/SCTP
        set udp-portrange 9999
        set udp-idle-timer 30
    next
    edit "SVC-TCP-FASTCLEANUP"
        set protocol TCP/UDP/SCTP
        set tcp-portrange 7777
        set tcp-halfopen-timer 5
        set tcp-halfclose-timer 15
        set tcp-timewait-timer 1
        set tcp-rst-timer 3
    next
end
Field What it governs Global equivalent
tcp-halfopen-timer How long a session survives after SYN with no SYN-ACK config system global > set tcp-halfopen-timer
tcp-halfclose-timer How long a session survives after the first FIN config system global > set tcp-halfclose-timer
tcp-timewait-timer How long the session lingers after the close completes config system global > set tcp-timewait-timer
tcp-rst-timer How long the session lingers after a RST config system global > set tcp-rst-timer
udp-idle-timer Idle timeout for UDP pseudo sessions config system global > set udp-idle-timer
session-ttl Overall idle timeout for the session config system session-ttl

In every case, a value of 0 on the service means do not override, use the global value. Lowering tcp-halfopen-timer on an exposed service is a cheap and effective SYN flood mitigation that costs nothing and does not require a DoS policy.

GUI Verification: Session TTL is exposed in the GUI service editor as Session TTL under the advanced area on recent builds. The four TCP timers and udp-idle-timer are CLI only.

Phase 7: RST and ICMP Error Validation with check-reset-range

Goal: Harden a specific service against blind TCP reset injection and forged ICMP error messages.

Fortinet documents check-reset-range as ICMP error message verification with strict RST range checking. Operationally, strict makes the FortiGate validate that the sequence number carried in a TCP RST falls inside the expected window before it tears down the session, and validate the embedded headers inside ICMP error messages against the existing session before acting on them. Without it, an attacker who can guess or observe the four tuple can knock down long lived sessions with a forged RST.

Action: Apply strict checking to a long lived BGP or database service.

config firewall service custom
    edit "SVC-BGP-HARDENED"
        set category "CORP-Standard"
        set protocol TCP/UDP/SCTP
        set tcp-portrange 179
        set check-reset-range strict
        set session-ttl 86400
        set comment "Strict RST validation for eBGP peering sessions"
    next
end
Value Behavior
default Inherit the global setting from config system global > set check-reset-range
strict Validate RST sequence numbers and ICMP error payloads against the session table
disable No validation for this service, regardless of the global setting

Trade offs to weigh before you roll this out broadly:

  • Strict checking costs CPU cycles per packet on the affected sessions.
  • Asymmetric routing, where the FortiGate only sees one direction of a flow, will produce sequence numbers the firewall considers out of window. Strict mode plus asymmetry equals dropped legitimate resets and lingering dead sessions.
  • Some poorly behaved embedded TCP stacks emit resets with sequence numbers that fail validation. Test before deploying to OT segments.

GUI Verification: CLI only. There is no GUI representation of check-reset-range at the service level.

Phase 8: Overriding the Session Helper Per Service

Goal: Turn a session helper on or off for one specific flow without altering the global session helper table.

FortiOS maintains a global port to helper map in config system session-helper. Helpers such as ftp, sip, tftp, pptp, rsh, dns-udp, dcerpc, and rtsp perform application layer fixups and open pinholes for dynamically negotiated ports. When a helper misbehaves, the instinctive fix is to delete the global entry, which affects every flow on the appliance. The surgical fix is to override the helper on a service object.

Action: Inspect the global helper table first.

show system session-helper

Action: Disable the SIP helper for one segment’s signaling traffic, typically because a VoIP profile is doing the work and the helper is double processing the messages.

config firewall service custom
    edit "SVC-SIP-NOHELPER"
        set category "CORP-Exception"
        set protocol TCP/UDP/SCTP
        set udp-portrange 5060
        set tcp-portrange 5060
        set helper disable
        set comment "SIP ALG handled by VoIP profile, kernel helper suppressed"
    next
end

Action: Force a helper onto a non standard port.

config firewall service custom
    edit "SVC-FTP-ALTPORT"
        set protocol TCP/UDP/SCTP
        set tcp-portrange 2121
        set helper ftp
        set comment "FTP control channel on 2121, helper forced for data channel pinholing"
    next
end

Available helper values include auto, disable, ftp, tftp, ras, h323, tns, mms, sip, pptp, rtsp, dns-udp, dns-tcp, pmap, rsh, dcerpc, mgcp, and the GTP variants gtp-c, gtp-u, and gtp-b on carrier capable builds. auto is the default and means consult the global session-helper table by port.

GUI Verification: CLI only. Neither the helper override nor the global session helper table is exposed in the GUI.

Phase 9: Application Aware Services

Goal: Build a service object that matches on application identity rather than on a port number.

app-service-type converts a service object from a Layer 4 matcher into a Layer 7 matcher backed by the FortiGuard Application Control database. This is the mechanism behind NGFW policy based mode, where you write policies that say “allow SSH” and mean the protocol, not TCP 22.

Action: Look up the application ID you need.

config application name
    edit "SSH"
        get
    next
end

The id field in the output is the value you feed to set application.

Action: Build an application ID service, then an application category service.

config firewall service custom
    edit "SVC-APP-SSH-ANYPORT"
        set category "CORP-Standard"
        set app-service-type app-id
        set application <app_id>
        set comment "Matches SSH by signature regardless of destination port"
    next
    edit "SVC-APP-CAT-P2P"
        set app-service-type app-category
        set app-category <category_id>
        set comment "Matches an entire application category"
    next
end

Behavior notes that matter in production:

  • Once app-service-type is set to app-id or app-category, the Layer 4 port fields on the object are not used for matching. The object matches purely on application identity.
  • Application identification requires the flow to progress far enough for the IPS engine to fingerprint it. The first packets of a session cannot be identified, so FortiOS performs the initial policy lookup without an application verdict and re-evaluates once identification completes. Expect a short window where the session is treated as unknown.
  • This requires an active FortiGuard Application Control subscription. Without a current database, the object will not match anything useful. Verify with diagnose autoupdate versions.
  • Encrypted traffic without deep inspection limits what can be identified. An application aware service behind an SSL inspection profile set to certificate inspection only will identify considerably less than one behind deep inspection.

GUI Verification: In Policy & Objects > Services, set the service Type to Application and select applications or categories from the picker. If the Type dropdown does not offer Application, check that Application Control is enabled under System > Feature Visibility.

Phase 10: Proxy Services for Explicit Proxy Policies

Goal: Create services usable in explicit proxy policies, and understand why they are invisible everywhere else.

Action: Create a proxy service.

config firewall service custom
    edit "SVC-PROXY-HTTP-ALT"
        set proxy enable
        set protocol HTTP
        set tcp-portrange 8080
        set comment "Explicit proxy HTTP on an alternate listener"
    next
    edit "SVC-PROXY-CONNECT-ALL"
        set proxy enable
        set protocol CONNECT
        set tcp-portrange 1-65535
        set comment "CONNECT tunneling, any destination port"
    next
end

When proxy is enabled, the protocol field switches to a completely different value set: ALL, CONNECT, FTP, HTTP, SOCKS-TCP, and SOCKS-UDP. These reflect the explicit proxy’s protocol handlers rather than IP transports.

The gotcha to internalize

Proxy services and non proxy services occupy the same table but are filtered by policy type. A service with set proxy enable appears in config firewall proxy-policy and nowhere else. A service with set proxy disable, the default, appears in config firewall policy, config firewall security-policy, config firewall shaping-policy, and config firewall local-in-policy, but never in a proxy policy. Flipping proxy on an object already referenced by the wrong policy type is rejected with a reference error.

GUI Verification: The GUI service list only shows proxy services when explicit proxy is enabled under System > Feature Visibility > Explicit Proxy. In the service editor the setting appears as a Proxy toggle at the top of the form. If a service you know exists is missing from a policy’s service picker, this toggle is the first thing to check.

Phase 11: Service Groups

Goal: Bundle services into a single policy reference.

Action: Build an operational group.

config firewall service group
    edit "SG-DC-MANAGEMENT"
        set member "SSH" "HTTPS" "SVC-WEBAPP-ALT-PORTS" "SVC-ICMP-ECHO-REQ"
        set comment "Jump host to data centre management plane"
        set color 7
        set fabric-object enable
    next
    edit "SG-MONITORING-EGRESS"
        set member "SNMP" "SVC-SYSLOG-TO-COLLECTORS" "NTP"
        set comment "Monitoring and telemetry egress"
    next
end

Rules and constraints, several of which are not documented in an obvious place:

  • Groups cannot be nested. A service group may not contain another service group. Address groups permit nesting. Service groups do not. Attempting it is rejected at commit time.
  • Proxy and non proxy members cannot be mixed. A group is either a proxy group or a standard group, determined by its members. The group’s own proxy flag follows.
  • A group cannot be empty. At least one member is required.
  • Members are evaluated as a logical OR. A packet matching any single member matches the group.
  • The group inherits nothing from its members. Timers, helpers, and destination scoping remain properties of each individual member and apply only when that member is the one that matched.
  • A service that is a member of a group cannot be deleted until it is removed from the group.

GUI Verification: Policy & Objects > Services > Create New > Service Group. The member picker will not offer service groups, which is the visual confirmation that nesting is unsupported.

Phase 12: Visibility, Color, and Fabric Object Flags

Goal: Control which objects clutter the GUI and which propagate across the Security Fabric.

Action: Hide a rarely used predefined service from GUI pickers while keeping it functional.

config firewall service custom
    edit "GOPHER"
        set visibility disable
    next
end

The object still exists, still matches, and any policy already referencing it continues to work. It simply stops appearing in GUI dropdowns. This is the cleanest way to trim the predefined catalog down to what your organization actually uses without deleting objects and losing the ability to restore them.

Action: Promote objects to the Security Fabric.

config firewall service custom
    edit "SVC-SYSLOG-TO-COLLECTORS"
        set fabric-object enable
    next
end

config firewall service group
    edit "SG-MONITORING-EGRESS"
        set fabric-object enable
    next
end

On the fabric root, fabric-object enable synchronizes the object to downstream fabric members, where it appears as read only with a fabric badge. This requires the Security Fabric to be established and object synchronization to be permitted under config system csf. Verify the fabric state before relying on it:

get system csf status
diagnose sys csf global

GUI Verification: Fabric objects display a small fabric icon next to the object name in Policy & Objects > Services. On downstream members the Edit button is replaced with a read only view.

Phase 13: Binding Services to Policy

Goal: Apply the objects and confirm they are actually referenced.

Action: Reference the group in a standard firewall policy.

config firewall policy
    edit 0
        set name "JUMPHOST-TO-DC-MGMT"
        set srcintf "port4"
        set dstintf "port3"
        set srcaddr "H-JUMP-01"
        set dstaddr "N-DC-CORE"
        set action accept
        set schedule "always"
        set service "SG-DC-MANAGEMENT"
        set logtraffic all
    next
end

Action: Reference the proxy service in a proxy policy.

config firewall proxy-policy
    edit 0
        set name "EXPLICIT-PROXY-ALT-PORT"
        set proxy explicit-web
        set dstintf "port1"
        set srcaddr "N-CORP-USERS"
        set dstaddr "all"
        set service "SVC-PROXY-HTTP-ALT"
        set action accept
        set schedule "always"
    next
end

Action: Reference an IP scoped service in a policy that would otherwise be too broad.

config firewall policy
    edit 0
        set name "SERVERS-TO-SYSLOG"
        set srcintf "port3"
        set dstintf "port3"
        set srcaddr "N-DC-CORE"
        set dstaddr "all"
        set action accept
        set schedule "always"
        set service "SVC-SYSLOG-TO-COLLECTORS"
        set logtraffic all
        set comments "Destination scoping is enforced by the service object"
    next
end

Why this pattern matters

The policy destination is all, but the service object restricts the effective destination to 10.0.40.10 and 10.0.40.11. This lets you maintain the sanctioned collector list in exactly one place instead of in every policy that touches logging.


4. Verification and Validation

4.1 Confirm the Object Was Written As Intended

show suppresses default values. show full-configuration does not. Always verify with the latter when working with the options in this guide, because a rejected or silently normalized value is invisible under plain show.

show full-configuration firewall service custom SVC-SYSLOG-TO-COLLECTORS
show full-configuration firewall service custom SVC-ORACLE-TNS-LONGLIVED
show firewall service group SG-DC-MANAGEMENT

Success looks like: every field you set appears with your value, and iprange, session-ttl, helper, and check-reset-range are not sitting at their defaults.

4.2 Confirm Policy Reference and Match

Find every policy referencing an object:

diagnose sys checkused firewall.service.custom.name SVC-SYSLOG-TO-COLLECTORS
diagnose sys checkused firewall.service.group.name SG-DC-MANAGEMENT

Success looks like: a list of entry used by table firewall.policy:policyid lines. An empty result means the object is orphaned.

Run a synthetic policy lookup without generating traffic:

diagnose firewall iprope lookup 10.0.40.60 33000 10.0.40.10 514 17 port3

The argument order is source address, source port, destination address, destination port, IP protocol number, and ingress interface. Protocol 6 is TCP and 17 is UDP.

Success looks like: the output names the policy ID you expect. If it names a different policy, or reports the implicit deny, your service object is not matching.

4.3 Validate Timers, Helpers, and State in the Session Table

diagnose sys session filter clear
diagnose sys session filter dport 1521
diagnose sys session filter proto 6
diagnose sys session list
Field What to check
timeout= Should reflect the service session-ttl, counting down from your configured value
proto_state= The TCP state machine position, useful when validating half open and half close timers
helper= Present only when a session helper is attached. Absent after set helper disable.
policy_id= Confirms which policy actually matched
npu_state= and no_ofld_reason= Reveals whether the session was pushed off hardware offload

Success looks like: for the Oracle service, timeout=43200 on a freshly established session rather than the global default of 3600.

4.4 Validate FQDN Resolution for FQDN Scoped Services

get system dns
execute ping api.<your_domain>
diagnose firewall fqdn list

Success looks like: the FortiGate resolving the name to one or more addresses. If resolution fails, an FQDN scoped service matches nothing and the policy is skipped without any obvious error.

4.5 Live Trace with Debug Flow

diagnose debug reset
diagnose debug flow filter clear
diagnose debug flow filter addr 10.0.40.10
diagnose debug flow filter port 514
diagnose debug flow filter proto 17
diagnose debug flow show function-name enable
diagnose debug flow show iprope enable
diagnose debug console timestamp enable
diagnose debug flow trace start 20
diagnose debug enable

Stop cleanly when you are finished:

diagnose debug flow trace stop
diagnose debug disable
diagnose debug flow filter clear
diagnose debug reset

Success looks like: a trace containing Allowed by Policy-<id>. A trace containing Denied by forward policy check (policy 0) means nothing matched and the implicit deny fired.

4.6 Validate Fabric Object Propagation

On the downstream member:

show firewall service custom SVC-SYSLOG-TO-COLLECTORS
get system csf status

Success looks like: the object present on the member, and the GUI showing it as read only with a fabric badge.


5. Troubleshooting and Gotchas

Gotcha 1: A Source Port Constraint Silently Kills the Policy

Symptom

A newly built policy never matches. Traffic falls through to the implicit deny. The service object looks correct at a glance because the destination port is right.

Cause

The object carries a source port range. Client operating systems select a random ephemeral source port for every outbound connection, so the source condition almost never satisfies. This is especially common when a service was cloned from a template, or when a config was migrated from another vendor where the source port field meant something different.

Diagnosis

show full-configuration firewall service custom <service_name> | grep portrange

Anything containing a colon has a source constraint. Then confirm the match failure:

diagnose debug flow filter clear
diagnose debug flow filter addr <client_ip>
diagnose debug flow show function-name enable
diagnose debug flow trace start 10
diagnose debug enable

A trace showing the packet arriving and then Denied by forward policy check (policy 0) while the policy visibly exists confirms a match failure rather than a missing policy.

Resolution

config firewall service custom
    edit "<service_name>"
        set tcp-portrange 8443
    next
end

If you genuinely need a source constraint, widen it to the full ephemeral range rather than a narrow band, and document why the constraint exists.

Gotcha 2: VIP Port Forwarding Requires the Mapped Port, Not the External Port

Symptom

A port forwarding VIP is configured, the external port is open at the edge, and inbound traffic is still denied.

Cause

FortiOS performs destination NAT before the firewall policy lookup. By the time policy matching runs, the destination port has already been rewritten to the mapped port. A policy whose service references the external port will therefore never match. Given a VIP translating external TCP 443 on 198.18.10.50 to mapped TCP 8443 on 10.0.20.50, the policy service must reference 8443, not 443.

Diagnosis

diagnose debug flow filter clear
diagnose debug flow filter addr 198.18.10.50
diagnose debug flow show function-name enable
diagnose debug flow trace start 10
diagnose debug enable

The trace will show the packet arriving with the original destination, then a DNAT line rewriting it, then the policy lookup against the rewritten values. Compare the port in the DNAT line to the port in your service object.

Resolution

config firewall service custom
    edit "SVC-WEB-MAPPED"
        set protocol TCP/UDP/SCTP
        set tcp-portrange 8443
    next
end

config firewall policy
    edit <policy_id>
        set service "SVC-WEB-MAPPED"
    next
end

Compounding factor

If the service on a VIP policy also carries a source port constraint, the policy will check the source port as well and deny on mismatch. For VIP policies specifically, use destination only services. This combination of the two gotchas produces a failure that looks unexplainable until you inspect the full configuration.

Gotcha 3: Service Groups Cannot Be Nested, and Proxy Members Cannot Be Mixed

Symptom

A group edit is rejected at end, or a service you expect to see is absent from the group member picker.

Cause

Two independent constraints. FortiOS rejects a service group as a member of another service group. It also rejects mixing services where proxy is enabled with services where it is disabled, because the two match in fundamentally different engines.

Diagnosis

show full-configuration firewall service custom <member_name> | grep proxy
show firewall service group <group_name>

Resolution

For nesting, flatten the hierarchy by listing the leaf services directly. For the proxy conflict, maintain two parallel groups, one for standard firewall policy and one for proxy policy:

config firewall service group
    edit "SG-WEB-STANDARD"
        set member "HTTP" "HTTPS"
    next
    edit "SG-WEB-PROXY"
        set member "SVC-PROXY-HTTP-ALT" "SVC-PROXY-CONNECT-ALL"
    next
end

Gotcha 4: Changing session-ttl Does Not Affect Existing Sessions

Symptom

You raise session-ttl on a service to fix idle disconnects, and connections continue to drop at the old interval.

Cause

The TTL is stamped onto the session when it is created. Existing entries in the session table retain the value they were built with. This also cuts the other way: lowering a TTL does not immediately reclaim memory.

Diagnosis

diagnose sys session filter clear
diagnose sys session filter dport 1521
diagnose sys session list

Compare timeout= on an old session against a newly established one.

Resolution

diagnose sys session filter clear
diagnose sys session filter dport 1521
diagnose sys session filter proto 6
diagnose sys session filter dst 10.0.40.60
diagnose sys session clear

Do not skip the filter

An unfiltered diagnose sys session clear flushes every session on the appliance, which is a service impacting event. Always run diagnose sys session list first to confirm the filter selects exactly what you intend before issuing clear.

Gotcha 5: Session Helper Conflicts and Double Processing

Symptom

SIP calls establish but audio is one way, or FTP data channels fail on a non standard control port. VoIP behavior changes unpredictably after enabling a VoIP profile.

Cause

The kernel session helper and a security profile ALG are both processing the same traffic, or a helper is bound to the wrong port. The global config system session-helper table maps ports to helpers appliance wide, so a non standard port gets no helper and a standard port gets one whether you want it or not.

Diagnosis

show system session-helper

diagnose sys session filter clear
diagnose sys session filter dport 5060
diagnose sys session list

Look for the helper= field in the session output. Its presence or absence tells you which path the traffic took.

Resolution

config firewall service custom
    edit "SVC-SIP-NOHELPER"
        set protocol TCP/UDP/SCTP
        set udp-portrange 5060
        set helper disable
    next
end

Then clear the affected sessions so they rebuild without the helper, using a tight filter as shown in Gotcha 4.

Gotcha 6: FQDN Scoped Services Fail Silently When DNS Breaks

Symptom

A policy using an FQDN scoped service stops matching. No error is logged against the policy. The object looks fine in the GUI.

Cause

The FortiGate could not resolve the FQDN. An unresolved FQDN produces an empty address set, so the service matches nothing and the policy is skipped as if it did not exist.

Diagnosis

get system dns
execute ping api.<your_domain>
diagnose firewall fqdn list
diagnose test application dnsproxy 1

Resolution

Fix name resolution at the FortiGate itself, not just at the clients. Confirm that config system dns points at reachable resolvers, that any policy governing the FortiGate’s own DNS egress permits it, and that the DNS server responds to the FortiGate’s source address. For services with a hard availability requirement, prefer an iprange scoped service or a standard address object over an FQDN dependency.

Gotcha 7: Application Aware Services Do Not Match the First Packets

Symptom

A policy using an app-service-type service behaves inconsistently. Some sessions match, some do not. Short lived connections in particular seem to bypass it.

Cause

Application identification is a Layer 7 verdict that requires several packets of the flow. The initial policy lookup happens before any verdict exists, so FortiOS matches on what it knows and re-evaluates once identification completes. Sessions that finish before identification never get the verdict.

Diagnosis

diagnose autoupdate versions
diagnose sys session filter clear
diagnose sys session filter dport <port>
diagnose sys session list

Check that the Application Control database in the diagnose autoupdate versions output is current. An expired or missing database is the most common root cause.

Resolution

Confirm the FortiGuard Application Control subscription is active. Pair the application aware service with an appropriate SSL inspection profile, since certificate inspection alone limits identification on encrypted flows. For traffic that must be controlled deterministically, keep a Layer 4 service as the enforcement point and use application awareness for visibility rather than as the sole control.

Gotcha 8: Editing a Predefined Service Has Blast Radius

Symptom

An unrelated policy changes behavior after someone adds a port to HTTPS for a single application.

Cause

Predefined services are ordinary entries in config firewall service custom. Editing one instantly changes every policy that references it, across every VDOM that shares the object scope.

Diagnosis

diagnose sys checkused firewall.service.custom.name HTTPS

Resolution

Never edit predefined services. Clone to a new object in your own category and edit that instead. Make it a written standard: predefined objects are read only by policy even though FortiOS permits writes.

config firewall service custom
    edit "SVC-HTTPS-PLUS-8443"
        set category "CORP-Standard"
        set protocol TCP/UDP/SCTP
        set tcp-portrange 443 8443
        set comment "Clone of HTTPS with the application alternate port"
    next
end

6. Quick Reference: The Full Custom Service Option Set

Option Type Purpose GUI exposed
category string Assigns the object to a service category Yes
protocol enum TCP/UDP/SCTP, ICMP, ICMP6, IP. Switches to proxy handlers when proxy is enabled Yes
tcp-portrange port spec Destination and optional source TCP ports Partial
udp-portrange port spec Destination and optional source UDP ports Partial
sctp-portrange port spec Destination and optional source SCTP ports Partial
protocol-number integer IP protocol number when protocol IP is set Yes
icmptype / icmpcode integer ICMP and ICMPv6 type and code Yes
iprange address range Destination IP constraint embedded in the service Yes
fqdn string Destination FQDN constraint, mutually exclusive with iprange Yes
session-ttl seconds Per service session idle timeout override Yes
tcp-halfopen-timer seconds SYN with no SYN-ACK timeout override No
tcp-halfclose-timer seconds Post first FIN timeout override No
tcp-timewait-timer seconds Post close lingering timeout override No
tcp-rst-timer seconds Post RST lingering timeout override No
udp-idle-timer seconds UDP pseudo session idle timeout override No
check-reset-range enum RST sequence and ICMP error validation: default, strict, disable No
helper enum Session helper binding or suppression for this service No
app-service-type enum disable, app-id, or app-category. Converts the object to a Layer 7 matcher Yes
application id list Application IDs when app-service-type app-id Yes
app-category id list Application category IDs when app-service-type app-category Yes
proxy enable/disable Restricts the object to explicit proxy policies Yes, when explicit proxy is visible
fabric-object enable/disable Synchronizes the object across the Security Fabric Yes
visibility enable/disable Shows or hides the object in GUI pickers No
color 1 to 32 GUI icon color Yes
comment string Free text. Use it. Yes

7. Operational Standards Worth Adopting

  1. Never edit a predefined service. Clone it.
  2. Prefix every custom object with a consistent tag such as SVC- and every group with SG-. It makes grep and audit exports trivial.
  3. Put a comment on every object explaining why it exists and who owns it. The comment field is free and the alternative is archaeology in eighteen months.
  4. Reserve source port constraints for cases you can justify in writing, and note the justification in the comment.
  5. Solve idle timeout complaints with per service session-ttl, never by raising the global default.
  6. Prefer iprange scoping over duplicating address groups when a service is only ever valid toward a fixed set of destinations.
  7. Use visibility disable to prune the predefined catalog instead of deleting objects.
  8. Run diagnose sys checkused before deleting or modifying anything.
  9. Review CORP-Exception category members quarterly and remove what is no longer justified.

Recent posts

  • If you've spent any time configuring user authentication on... Full Story

  • DNS is one of those technologies that quietly underpins... Full Story

  • BGP issues on FortiGate firewalls usually trace back to... Full Story

  • Every time your laptop talks to your router, a... Full Story

  • If you've spent any time configuring NAT on a... Full Story

  • If you have spent any time configuring firewall policies... Full Story

  • High availability on FortiGate is one of those features... Full Story

  • If you've configured SD-WAN on a FortiGate, you've almost... Full Story

  • FortiLink is the management protocol that turns a FortiSwitch... Full Story

  • FortiSwitches are pretty rock solid from Mean Time Between... Full Story

  • This is a quicky tip.  Have you ever gone... Full Story

  • DNS is one of those quiet pieces of internet... Full Story

  • This article is an updated version of the previous... Full Story

  • You will add ns2 as a secondary (slave) BIND9... Full Story

  • In the process of deploying my lab, I needed... Full Story

  • RFC 8805, used to be known as Self-Correcting IP... Full Story

  • Years back, I wrote an article about certificate pinning. ... Full Story

  • FortiGates have the ability to send alerts to Microsoft... Full Story

  • In this post, I am going to walk through... Full Story

  • Troubleshooting VoIP on a FortiGate can feel like trying... Full Story

  • Prior to FortiOS 7.0, there were three commands to... Full Story

  • In this post, I am going to go over... Full Story

  • What we are going to do:  We are going... Full Story

  • Choosing between FGCP (FortiGate Clustering Protocol) and FGSP (FortiGate... Full Story

  • Creating a VLAN on macOS (The "Pro" Move) A... Full Story

  • This blog post explores the logic behind how macOS... Full Story

  • Pretty Fly for a Wi-Fi Tell My Wi-Fi Love... Full Story

  • Part of my daily gig is creating BoMs (Bill-of-Materials)... Full Story

  • ICMP introduces several security risks, but careful filtering, rate... Full Story

  • The command diag debug application dhcps -1 enables full... Full Story

  • In the world of FortiOS, execute tac report is... Full Story

  • LLDP; What is it The Link Layer Discovery Protocol... Full Story

  • What it actually does When you run diagnose fdsm... Full Story

  • Monkey Bites are bite-sized, high-impact security insights designed for... Full Story

  • I have run macOS in macOS with Parallels but... Full Story

  • Don't be confused with my other FortiNAC posts where... Full Story

  • This is the third session in a multi-part article... Full Story

  • Today I was configuring key-based authentication on a FortiGate... Full Story

  • Netcat, often called the "Swiss Army knife" of networking,... Full Story

  • At its core, IEEE 802.1X is a network layer... Full Story

  • In case you did not see the previous FortiNAC... Full Story

  • This is our 5th session where we are going... Full Story

  • Now that we have Wireshark installed and somewhat configured,... Full Story

  • The Philosophy of Packet Analysis Troubleshooting isn't about looking... Full Story

  • 1. Executive Summary Objective This guide walks through the... Full Story

  • If you have ever tried to stand up a... Full Story

  • 1. Executive Summary Objective This guide takes the FortiGate... Full Story