SSL certificate maximum validity is being reduced to 200 days from March 2026. Read more →

Fortinet ACME certificates: FortiGate, FortiMail and FortiWeb

FortiOS 7.6.3 and later ships a built-in ACME client with External Account Binding, so FortiGate can fetch and renew certificates from FairSSL on its own. Older FortiGate versions use a proxy host running Lego or simple-acme that pushes the certificate in over SSH. Both approaches are covered here.

Which approach should you use?

Recommended FortiOS 7.6.3+

Native ACME on FortiGate

FortiGate runs the ACME client itself. You register a local certificate object with EAB keys and FortiOS handles issuance and automatic renewal. No extra host required.

Go to native setup →
Fallback Anything before 7.6.3

Proxy via Lego or simple-acme

A small Linux or Windows host issues the certificate and pushes it to FortiGate via SSH or REST API. The same pattern also works for FortiMail and older FortiWeb, neither of which has native ACME.

Go to proxy setup →

Native ACME (FortiOS 7.6.3+)

FortiGate\'s built-in ACME client supports External Account Binding, which is the requirement for fetching certificates from a commercial CA like FairSSL. Everything happens in the CLI.

Step 1: Verify firmware

Log in to FortiGate via SSH or the GUI console and check the version. EAB support requires 7.6.3 or later.

get system status

Step 2: Configure FairSSL AutoDNS

Log in to your FairSSL control panel, go to AutoDNS and create a CNAME for _dnsauth.<your-domain> pointing at the FairSSL DNS server. AutoDNS answers the ACME challenge itself, so FortiGate does not need port 80 open to the internet. See the full AutoDNS guide.

Step 3: Create the local ACME certificate object

Grab the EAB KID and HMAC key from the ACME tab in your FairSSL control panel. Replace the placeholders below and run the commands in the FortiGate CLI.

config vpn certificate local
    edit "fairssl-vpn-cert"
        set enrollment acme
        set acme-ca-url https://fairssl.dk/acme
        set acme-eab-key-id YOUR_EAB_KID
        set acme-eab-key-hmac YOUR_EAB_HMAC
        set acme-email acme-client@fairssl.dk
        set acme-domain vpn.example.com
        set acme-auth-url "http://vpn.example.com/.well-known/acme-challenge"
        set auto-regenerate-days 30
        set auto-regenerate enable
    next
end

auto-regenerate-days 30 means FortiGate will try to renew 30 days before expiry, which is safe for both 90-day and 47-day certificates. Set acme-domain to the exact FQDN the certificate is for. FortiGate\'s built-in ACME flow is intended for a single FQDN; use the proxy approach for wildcards or multi-SAN.

Step 4: Issue the certificate

execute vpn certificate local generate "fairssl-vpn-cert"

FortiGate contacts the FairSSL ACME server, validates ownership via AutoDNS and fetches the certificate. The whole flow normally finishes in under 30 seconds.

Step 5: Verify status

get vpn certificate local | grep -A 3 "fairssl-vpn-cert"

Expect Status: OK and a validity period matching the certificate lifetime (typically 47-90 days).

Step 6: Bind the certificate to services

Use the local certificate object for SSL VPN, admin HTTPS or other services. You can bind the same object to multiple services. All bindings get the renewed certificate automatically.

# SSL VPN
config vpn ssl settings
    set servercert "fairssl-vpn-cert"
end

# Admin HTTPS (optional)
config system global
    set admin-server-cert "fairssl-vpn-cert"
end

Verify with FairSSL\'s SSL scanner that the right certificate is being served on the public FQDN.

Proxy setup (FortiGate < 7.6.3, FortiMail, older FortiWeb)

When the appliance cannot run ACME itself, we issue the certificate on a separate Linux or Windows host and push it over via SSH or REST API. The Linux + Lego variant is the most flexible and is also used for FortiMail and older FortiWeb.

Linux host with Lego and SSH

A small Linux server (Debian, Ubuntu or RHEL) issues the certificate via Lego v5.0.4+ and pushes the PEM files to FortiGate over SSH using sshpass. FairSSL\'s commands require Lego v5.0.4 or newer: early v5 versions had registration and hook problems, and older v4 commands do not match these examples.

# 1. Install Lego and sshpass

arch=$(dpkg --print-architecture)
case "$arch" in amd64|arm64) ;; *) echo "Adjust arch"; exit 1 ;; esac
version=$(curl -fsSL https://api.github.com/repos/go-acme/lego/releases/latest \
  | sed -n 's/.*"tag_name": "\(v[^"]*\)".*/\1/p' | head -1)
curl -fL "https://github.com/go-acme/lego/releases/download/${version}/lego_${version}_linux_${arch}.tar.gz" \
  | sudo tar -xz -C /usr/local/bin lego
sudo apt-get install -y sshpass
sudo install -d -m 0700 /etc/ssl/fortigate

# 2. Create deploy script (save as /usr/local/bin/fortigate-deploy.sh)

#!/bin/bash
set -euo pipefail
FGT_IP="10.0.0.1"
FGT_USER="admin"
FGT_PASS="YOUR_SSH_PASSWORD"
CERT_NAME="fairssl-vpn-cert"

CERT=$(cat "$LEGO_HOOK_CERT_PATH")
KEY=$(cat "$LEGO_HOOK_CERT_KEY_PATH")
SSH="sshpass -p $FGT_PASS ssh -T -o StrictHostKeyChecking=no $FGT_USER@$FGT_IP"

$SSH <<EOF
config vpn certificate local
edit "$CERT_NAME"
set private-key "$KEY"
set certificate "$CERT"
next
end
config vpn ssl settings
set servercert "$CERT_NAME"
end
EOF

# 3. Issue the certificate (EAB keys only needed on first run)

sudo /usr/local/bin/lego run \
  --domains "vpn.example.com" \
  --server https://fairssl.dk/acme \
  --eab --eab.kid YOUR_EAB_KID --eab.hmac YOUR_EAB_HMAC \
  --accept-tos --email acme-client@fairssl.dk \
  --http --http.webroot "/var/www/html" \
  --path "/etc/ssl/fortigate" \
  --deploy-hook "/usr/local/bin/fortigate-deploy.sh"

# 4. Cron renewal, run daily at a random time

# /etc/cron.d/lego-fortigate (example)
17 04 * * * root /usr/local/bin/lego run \
  --domains "vpn.example.com" \
  --server https://fairssl.dk/acme \
  --accept-tos --email acme-client@fairssl.dk \
  --http --http.webroot "/var/www/html" \
  --path "/etc/ssl/fortigate" \
  --renew-days 7 \
  --deploy-hook "/usr/local/bin/fortigate-deploy.sh"

Lego v5+ runs run daily and uses ARI to decide when to renew. If the ACME server is unreachable or ARI does not flag a renewal, it falls back to renewing when 7 days remain. The deploy hook only fires when an actual renewal happens.

Windows host with simple-acme

If you do not have a Linux server, run the setup on Windows with simple-acme and Posh-SSH. Install FairSSL\'s simple-acme build (pre-configured with the right settings and ships Deploy-FortiGate.ps1 in the Scripts folder).

# PowerShell, Run as Administrator

Install-Module Posh-SSH, CredentialManager -Force -Scope CurrentUser

New-StoredCredential -Target "FortiGate-acme-user" `
  -UserName "admin" -Password "YOUR_SSH_PASSWORD" -Persist LocalMachine

cd C:\simple-acme
wacs.exe --verbose --baseuri "https://fairssl.dk/acme" `
  --eab-key-identifier YOUR_EAB_KID --eab-key YOUR_EAB_HMAC --accepttos `
  --source manual --host "vpn.example.com" --validation none `
  --store pemfiles --pemfilespath "C:\simple-acme\Certificates" `
  --pemfilesname "vpn-example-com" --friendlyname "fairssl-vpn-cert" `
  --installation script --script ".\Scripts\Deploy-FortiGate.ps1" `
  --scriptparameters "-StorePath 'C:\simple-acme\Certificates' -FilePrefix 'vpn-example-com' -FirewallHost '10.0.0.1' -CertLabel 'fairssl-vpn-cert' -CredentialTarget 'FortiGate-acme-user'"

Add -UpdateAdmin inside --scriptparameters if admin HTTPS should use the certificate as well. simple-acme creates a scheduled task that runs daily and renews when ARI says so or when fewer than 7 days remain.

FortiMail and FortiWeb

FortiMail and older FortiWeb use the same pattern as FortiGate proxy: an external host issues the certificate and a deploy hook pushes it in via the REST API. The ACME Easy Guides section of the FairSSL control panel ships finished scripts for FortiMail (Linux + Windows) and FortiWeb (Windows). The logic and structure mirror the FortiGate examples above; only the concrete API calls and the install endpoint differ.

Troubleshooting

Status stays Pending

Check that _dnsauth.<domain> is set up as a CNAME pointing to FairSSL\'s DNS server. Verify with dig _dnsauth.your-domain.com CNAME. Retry with execute vpn certificate local renew <name>.

Lego returns account does not exist

Most likely an old Lego version. Upgrade to v5.0.4+ and re-run the issuance command with the same --path. If the error persists, delete /etc/ssl/fortigate/accounts and re-run step 3 with the EAB keys.

SSH deploy fails with Host key verification failed

First time round you have to accept FortiGate\'s SSH host key. Run ssh admin@<fortigate-ip> manually from the deploy host and confirm the fingerprint. Or add the host key to ~/.ssh/known_hosts ahead of time.

SSL VPN still shows the old certificate after deploy

SSL VPN caches the certificate in memory. FairSSL\'s full deploy script in the control panel handles this by setting servercert to self-sign first and then back to the new certificate. If you use a simpler script of your own, the same swap has to be in there too.

Frequently asked questions

Find answers to the most common questions about SSL certificates and FairSSL.

Native ACME with External Account Binding requires FortiOS 7.6.3 or later. Earlier 7.6 releases had ACME but no EAB, which makes them unusable with commercial CAs like FairSSL. Older FortiGate devices (anything before 7.6.3) need the proxy approach with a Linux or Windows host.
No. FairSSL AutoDNS validates the domain via DNS-01 on our side, so FortiGate never has to expose port 80. The acme-auth-url field still has to be set because the FortiOS CLI requires it, but the HTTP-01 flow is never actually used as long as the domain is set to AutoDNS validation in your FairSSL control panel.
Run get vpn certificate local in the CLI. The status should read Status: OK with a valid expiry date. If it stays Pending, check that the domain is set up correctly under AutoDNS in your FairSSL control panel, then run execute vpn certificate local renew <name>.
FortiOS 7.6.3 supports standard ACME, but ARI support is still limited. If you set auto-regenerate-days to 30, FortiGate renews the certificate 30 days before expiry, which is safe for both 90-day and 47-day certificates. For full ARI awareness the proxy approach via Lego or simple-acme is still more flexible.
FortiMail has no native ACME support and always uses the proxy approach. FortiWeb 8.0.3 and later has native ACME with EAB, so the proxy approach is mainly relevant for older FortiWeb versions or when you want to drive the install from FairSSL's ready-made deploy scripts. Both expose well-documented REST APIs, so an external host can issue the certificate via Lego or simple-acme and push it over via the API. We ship example scripts for both in the FairSSL control panel under "ACME Easy Guides".
You only need them for the first registration. FortiGate stores the ACME account internally, and future renewals reuse it. The proxy approach is the same: Lego or simple-acme stores the account locally under the directory given to --path. Keep the EAB keys in a safe place as a backup anyway, in case you ever need to rebuild the setup.
Yes. FortiGate can bind the same local certificate object to multiple services. SSL VPN: config vpn ssl settings, set servercert "name", end. Admin HTTPS: config system global, set admin-server-cert "name", end. When the certificate is renewed, both bindings get the new one automatically.

Ready to automate FortiGate certificates?

Create a free account and issue your first certificate in under 10 minutes.