DOCUMENTATION

FortiGate Integration

Guest WiFi captive portal on FortiGate, authenticating against Captivo RADIUS.

1Register FortiGate in Captivo

In Captivo: Settings → RADIUS → add the FortiGate's source IP and the same shared secret as a NAS client. RADIUS ports 1812/1813 (UDP) must be reachable from the FortiGate.

2Define the Captivo RADIUS server

User & Authentication → RADIUS Servers → Create New. Primary Server = your Captivo RADIUS address, Secret = your shared secret, Authentication method = PAP. Then create a User Group (User & Authentication → User Groups, type Firewall) and add this RADIUS server to it.

config user radius
  edit "captivo"
    set server "<captivo-radius-ip>"
    set secret <shared-secret>
    set auth-type pap
  next
end
config user group
  edit "captivo-guests"
    set member "captivo"
  next
end

3Enable captive portal on the guest SSID

WiFi & Switch Controller → SSIDs → Create New → SSID. Security mode = Captive Portal (not WPA-Enterprise). Enable Captive Portal, Portal type = Authentication, Authentication portal = Local (FortiGate-hosted), and set User Groups to the captivo-guests group from step 1.

config wireless-controller vap
  edit "guest-wifi"
    set ssid "Guest WiFi"
    set security captive-portal
    set portal-type auth
    set selected-usergroups "captivo-guests"
  next
end
Captive Portal can't be combined with WPA/WPA3-Enterprise. Use Open (or a PSK) with the captive portal on top.
For the Active Sessions list to work: enable RADIUS accounting (port 1813) AND make the device send periodic accounting updates. Captivo treats a session as live while the device keeps signalling it; if only a start and a stop are sent, a guest who is still online drops off the list after 10 minutes while the device's own screen still shows them. The setting is usually called Interim, Accounting Interim Update or Update Interval; one minute is a good value.

4Paste the Captivo login page

System → Replacement Messages → category Authentication → 'Login Page'. Paste the FortiGate HTML downloaded from Settings → Portal → Templates. (For a per-SSID page, set portal-message-override-group on the vap.) The page POSTs to FortiGate's %%post_action%% with magic + username + password — exactly what the Captivo template produces, so no edits are needed.

# per-SSID override (optional)
config wireless-controller vap
  edit "guest-wifi"
    set portal-message-override-group "captivo-login"
  next
end

5Walled garden (important)

Before signing in, the guest must be able to reach Captivo; otherwise the portal can't load or request the SMS code. Exempt that destination from the captive portal with a firewall policy (or the interface security-exempt-list).

# exempt destination from captive portal
# create an FQDN address object for app.captivo.io first
config firewall policy
  edit 0
    set name "captivo-exempt"
    set srcintf "guest-wifi"
    set dstintf "wan1"
    set dstaddr "app.captivo.io"
    set action accept
    set schedule "always"
    set service "ALL"
    set captive-portal-exempt enable
  next
end
This is the #1 cause of 'the portal opens but no SMS arrives'. If app.captivo.io isn't reachable before sign-in, the portal's JavaScript can't request the verification code. Add it as an FQDN address object and exempt it. Note: you do NOT need to add your SMS provider's domain — Captivo sends the SMS from its own servers, so only app.captivo.io must be reachable.

Test and troubleshooting

Connect a guest device: you should be redirected to the branded portal, verify via SMS/voucher, and get internet access. Auth and identity logs appear right away; to see live sessions in the dashboard, also enable RADIUS accounting to port 1813 on the FortiGate RADIUS server.

Troubleshooting — Portal doesn't show: captive portal not enabled on the SSID, or the user group doesn't point to the RADIUS server. No SMS: walled garden (step 5). 'Invalid username/password': the NAS client IP/secret in Captivo doesn't match, or the RADIUS auth method isn't PAP.

For product-wide problems: The portal does not open · Sign-in is rejected · Sessions do not appear

Was this article helpful?