diff --git a/CHANGELOG.md b/CHANGELOG.md
index cb71b998b8b..8bccfd0d0e5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@ and this project adheres to
 
 ### Added
 
+- Static IP address detection on FreeBSD ([#3289]).
 - Optimistic cache ([#2145]).
 - New possible value of `6h` for `querylog_interval` setting ([#2504]).
 - Blocking access using client IDs ([#2624], [#3162]).
@@ -62,6 +63,7 @@ and this project adheres to
 
 ### Fixed
 
+- Incomplete HTTP response for static IP address.
 - DNSCrypt queries weren't appearing in query log ([#3372]).
 - Wrong IP address for proxied DNS-over-HTTPS queries ([#2799]).
 - Domain name letter case mismatches in DNS rewrites ([#3351]).
@@ -108,6 +110,7 @@ and this project adheres to
 [#3217]: https://github.com/AdguardTeam/AdGuardHome/issues/3217
 [#3256]: https://github.com/AdguardTeam/AdGuardHome/issues/3256
 [#3257]: https://github.com/AdguardTeam/AdGuardHome/issues/3257
+[#3289]: https://github.com/AdguardTeam/AdGuardHome/issues/3289
 [#3335]: https://github.com/AdguardTeam/AdGuardHome/issues/3335
 [#3343]: https://github.com/AdguardTeam/AdGuardHome/issues/3343
 [#3351]: https://github.com/AdguardTeam/AdGuardHome/issues/3351
diff --git a/internal/aghnet/net_freebsd.go b/internal/aghnet/net_freebsd.go
new file mode 100644
index 00000000000..df6e8970481
--- /dev/null
+++ b/internal/aghnet/net_freebsd.go
@@ -0,0 +1,78 @@
+//go:build freebsd
+// +build freebsd
+
+package aghnet
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"net"
+	"os"
+	"strings"
+
+	"github.com/AdguardTeam/AdGuardHome/internal/aghio"
+	"github.com/AdguardTeam/AdGuardHome/internal/aghos"
+	"github.com/AdguardTeam/golibs/errors"
+)
+
+func canBindPrivilegedPorts() (can bool, err error) {
+	return aghos.HaveAdminRights()
+}
+
+// maxCheckedFileSize is the maximum acceptable length of the /etc/rc.conf file.
+const maxCheckedFileSize = 1024 * 1024
+
+func ifaceHasStaticIP(ifaceName string) (ok bool, err error) {
+	const filename = "/etc/rc.conf"
+
+	var f *os.File
+	f, err = os.Open(filename)
+	if err != nil {
+		return false, err
+	}
+	defer func() { err = errors.WithDeferred(err, f.Close()) }()
+
+	var r io.Reader
+	r, err = aghio.LimitReader(f, maxCheckedFileSize)
+	if err != nil {
+		return false, err
+	}
+
+	return rcConfStaticConfig(r, ifaceName)
+}
+
+// rcConfStaticConfig checks if the interface is configured by /etc/rc.conf to
+// have a static IP.
+func rcConfStaticConfig(r io.Reader, ifaceName string) (has bool, err error) {
+	s := bufio.NewScanner(r)
+	for ifaceLinePref := fmt.Sprintf("ifconfig_%s", ifaceName); s.Scan(); {
+		line := strings.TrimSpace(s.Text())
+		if !strings.HasPrefix(line, ifaceLinePref) {
+			continue
+		}
+
+		eqIdx := len(ifaceLinePref)
+		if line[eqIdx] != '=' {
+			continue
+		}
+
+		fieldsStart, fieldsEnd := eqIdx+2, len(line)-1
+		if fieldsStart >= fieldsEnd {
+			continue
+		}
+
+		fields := strings.Fields(line[fieldsStart:fieldsEnd])
+		if len(fields) >= 2 &&
+			strings.ToLower(fields[0]) == "inet" &&
+			net.ParseIP(fields[1]) != nil {
+			return true, s.Err()
+		}
+	}
+
+	return false, s.Err()
+}
+
+func ifaceSetStaticIP(string) (err error) {
+	return aghos.Unsupported("setting static ip")
+}
diff --git a/internal/aghnet/net_freebsd_test.go b/internal/aghnet/net_freebsd_test.go
new file mode 100644
index 00000000000..bf8c7ec384a
--- /dev/null
+++ b/internal/aghnet/net_freebsd_test.go
@@ -0,0 +1,60 @@
+//go:build freebsd
+// +build freebsd
+
+package aghnet
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestRcConfStaticConfig(t *testing.T) {
+	const ifaceName = `em0`
+	const nl = "\n"
+
+	testCases := []struct {
+		name       string
+		rcconfData string
+		wantHas    bool
+	}{{
+		name:       "simple",
+		rcconfData: `ifconfig_em0="inet 127.0.0.253 netmask 0xffffffff"` + nl,
+		wantHas:    true,
+	}, {
+		name:       "case_insensitiveness",
+		rcconfData: `ifconfig_em0="InEt 127.0.0.253 NeTmAsK 0xffffffff"` + nl,
+		wantHas:    true,
+	}, {
+		name: "comments_and_trash",
+		rcconfData: `# comment 1` + nl +
+			`` + nl +
+			`# comment 2` + nl +
+			`ifconfig_em0="inet 127.0.0.253 netmask 0xffffffff"` + nl,
+		wantHas: true,
+	}, {
+		name: "aliases",
+		rcconfData: `ifconfig_em0_alias="inet 127.0.0.1/24"` + nl +
+			`ifconfig_em0="inet 127.0.0.253 netmask 0xffffffff"` + nl,
+		wantHas: true,
+	}, {
+		name: "incorrect_config",
+		rcconfData: `ifconfig_em0="inet6 127.0.0.253 netmask 0xffffffff"` + nl +
+			`ifconfig_em0="inet 127.0.0.253 net-mask 0xffffffff"` + nl +
+			`ifconfig_em0="inet 256.256.256.256 netmask 0xffffffff"` + nl +
+			`ifconfig_em0=""` + nl,
+		wantHas: false,
+	}}
+
+	for _, tc := range testCases {
+		r := strings.NewReader(tc.rcconfData)
+		t.Run(tc.name, func(t *testing.T) {
+			has, err := rcConfStaticConfig(r, ifaceName)
+			require.NoError(t, err)
+
+			assert.Equal(t, tc.wantHas, has)
+		})
+	}
+}
diff --git a/internal/aghnet/net_others.go b/internal/aghnet/net_others.go
index 07add540822..033a40cc7b0 100644
--- a/internal/aghnet/net_others.go
+++ b/internal/aghnet/net_others.go
@@ -1,12 +1,9 @@
-//go:build !(linux || darwin)
-// +build !linux,!darwin
+//go:build !(linux || darwin || freebsd)
+// +build !linux,!darwin,!freebsd
 
 package aghnet
 
 import (
-	"fmt"
-	"runtime"
-
 	"github.com/AdguardTeam/AdGuardHome/internal/aghos"
 )
 
@@ -14,10 +11,10 @@ func canBindPrivilegedPorts() (can bool, err error) {
 	return aghos.HaveAdminRights()
 }
 
-func ifaceHasStaticIP(string) (bool, error) {
-	return false, fmt.Errorf("cannot check if IP is static: not supported on %s", runtime.GOOS)
+func ifaceHasStaticIP(string) (ok bool, err error) {
+	return false, aghos.Unsupported("checking static ip")
 }
 
-func ifaceSetStaticIP(string) error {
-	return fmt.Errorf("cannot set static IP on %s", runtime.GOOS)
+func ifaceSetStaticIP(string) (err error) {
+	return aghos.Unsupported("setting static ip")
 }
diff --git a/internal/dhcpd/http.go b/internal/dhcpd/http.go
index aec55c944b6..4c10bd96eb4 100644
--- a/internal/dhcpd/http.go
+++ b/internal/dhcpd/http.go
@@ -412,7 +412,9 @@ func (s *Server) handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Reque
 	result := dhcpSearchResult{
 		V4: dhcpSearchV4Result{
 			OtherServer: dhcpSearchOtherResult{},
-			StaticIP:    dhcpStaticIPStatus{},
+			StaticIP: dhcpStaticIPStatus{
+				Static: "yes",
+			},
 		},
 		V6: dhcpSearchV6Result{
 			OtherServer: dhcpSearchOtherResult{},
diff --git a/internal/dhcpd/server.go b/internal/dhcpd/server.go
index 13c3b91bb42..d326e812c8e 100644
--- a/internal/dhcpd/server.go
+++ b/internal/dhcpd/server.go
@@ -38,6 +38,9 @@ type V4ServerConf struct {
 
 	GatewayIP  net.IP `yaml:"gateway_ip" json:"gateway_ip"`
 	SubnetMask net.IP `yaml:"subnet_mask" json:"subnet_mask"`
+	// broadcastIP is the broadcasting address pre-calculated from the
+	// configured gateway IP and subnet mask.
+	broadcastIP net.IP
 
 	// The first & the last IP address for dynamic leases
 	// Bytes [0..2] of the last allowed IP address must match the first IP
diff --git a/internal/dhcpd/v4.go b/internal/dhcpd/v4.go
index 654e0c0e49e..2b7c9b254f6 100644
--- a/internal/dhcpd/v4.go
+++ b/internal/dhcpd/v4.go
@@ -927,12 +927,30 @@ func (s *v4Server) packetHandler(conn net.PacketConn, peer net.Addr, req *dhcpv4
 		resp.Options.Update(dhcpv4.OptMessageType(dhcpv4.MessageTypeNak))
 	}
 
+	// peer is expected to be of type *net.UDPConn as the server4.NewServer
+	// initializes it.
+	udpPeer, ok := peer.(*net.UDPAddr)
+	if !ok {
+		log.Error("dhcpv4: peer is of unexpected type %T", peer)
+
+		return
+	}
+
+	// Despite the fact that server4.NewIPv4UDPConn explicitly sets socket
+	// options to allow broadcasting, it also binds the connection to a
+	// specific interface.  On FreeBSD conn.WriteTo causes errors while
+	// writing to the addresses that belong to another interface.  So, use
+	// the broadcast address specific for the binded interface in case
+	// server4.Server.Serve sets it to net.IPv4Bcast.
+	if udpPeer.IP.Equal(net.IPv4bcast) {
+		udpPeer.IP = s.conf.broadcastIP
+	}
+
 	log.Debug("dhcpv4: sending: %s", resp.Summary())
 
 	_, err = conn.WriteTo(resp.ToBytes(), peer)
 	if err != nil {
 		log.Error("dhcpv4: conn.Write to %s failed: %s", peer, err)
-		return
 	}
 }
 
@@ -1043,6 +1061,12 @@ func v4Create(conf V4ServerConf) (srv DHCPServer, err error) {
 		Mask: subnetMask,
 	}
 
+	bcastIP := aghnet.CloneIP(routerIP)
+	for i, b := range subnetMask {
+		bcastIP[i] |= ^b
+	}
+	s.conf.broadcastIP = bcastIP
+
 	s.conf.ipRange, err = newIPRange(conf.RangeStart, conf.RangeEnd)
 	if err != nil {
 		return s, fmt.Errorf("dhcpv4: %w", err)