OpenBSD PF Firewall: Configuration Guide and Examples
- Last updated: May 2, 2026
This page collects practical notes and examples for Packet Filter (PF), OpenBSD's firewall, NAT, and traffic filtering system.
PF rules are stored in /etc/pf.conf and managed with pfctl. The examples below are meant as starting points: always test a ruleset before loading it on a remote system.
Information
- Main configuration file:
/etc/pf.conf
- Example configuration file:
/etc/examples/pf.conf
- Official documentation:
man pf man pf.conf man pfctl
The pfctl command
- Check the syntax of the configuration file without loading it:
OpenBSD# pfctl -nf /etc/pf.conf
- Check the configuration file and display the parsed rules:
OpenBSD# pfctl -nvf /etc/pf.conf
- Load rules from the configuration file:
OpenBSD# pfctl -f /etc/pf.conf
- Enable PF:
OpenBSD# pfctl -e
- Disable PF:
OpenBSD# pfctl -d
- Flush only the filter rules. Be careful: with no rules loaded, the firewall will no longer enforce your ruleset:
OpenBSD# pfctl -F rules
- Flush everything, including rules, states, tables, sources, and statistics:
OpenBSD# pfctl -F all
- Show the currently loaded filter rules:
OpenBSD# pfctl -s rules
- Show PF status and counters:
OpenBSD# pfctl -s info
- Show all available PF information:
OpenBSD# pfctl -s all
- Show the contents of the state table:
OpenBSD# pfctl -s state
- Kill all state entries originating from a host or a network:
OpenBSD# pfctl -k host
OpenBSD# pfctl -k network
Macros
Macros make rules easier to read and maintain. They are especially useful for interfaces, networks, and lists of ports.
- Example:
tcp_services = "{ ssh, domain, www, https }"
- The macro can then be used inside a rule:
pass out proto tcp to any port $tcp_services keep state
Enable PF at boot
- Edit
/etc/rc.conf.local:
pf=YES
pf_rules=/etc/pf.conf
Using /etc/rc.conf.local keeps local changes separate from the default /etc/rc.conf file.
Router mode
Enable router mode temporarily
- Enable IPv4 forwarding:
OpenBSD# sysctl net.inet.ip.forwarding=1
- Enable IPv6 forwarding:
OpenBSD# sysctl net.inet6.ip6.forwarding=1
Enable router mode permanently
Edit /etc/sysctl.conf:
net.inet.ip.forwarding=1
net.inet6.ip6.forwarding=1
Examples
NAT from LAN to WAN, allow all outbound traffic
# MACROS
ext_if = "re0"
int_if = "re1"
localnet = $int_if:network
# NAT
match out on $ext_if from $localnet to any nat-to $ext_if
# FILTER RULES
set skip on lo
block all
pass from { self, $localnet } to any keep state
NAT from LAN to WAN with basic packet filtering
# MACROS
ext_if = "re0"
int_if = "re1"
localnet = $int_if:network
tcp_services = "{ ssh, domain, www, https, pop3, nntp, cvspserver, 2628, 5999, 8000, 8080 }"
udp_services = "{ domain, ntp }"
# NAT
match out on $ext_if from $localnet to any nat-to $ext_if
# FILTER RULES
set skip on lo
block all
pass in on $int_if inet proto tcp from $localnet to any port $tcp_services keep state
pass in on $int_if inet proto udp from $localnet to any port $udp_services keep state
pass out on $ext_if inet from $localnet to any keep state
NAT, port forwarding, and packet filtering
This example allows HTTP, HTTPS, and DNS from the 192.168.2.0/24 LAN. It also forwards inbound RDP connections to a host inside the LAN.
# MACROS
ext_if = "em0"
lan_if = "em1"
lan_net = "192.168.2.0/24"
rdp_host = "192.168.2.200"
tcp_services = "{ domain, www, https }"
udp_services = "{ domain }"
# OPTIONS
set skip on lo
# DEFAULT POLICY
block return
# NAT
match out on $ext_if inet from $lan_net to any nat-to $ext_if
# REDIRECTION: forward incoming RDP to the internal host
pass in quick on $ext_if inet proto tcp from any to ($ext_if) port 3389 rdr-to $rdp_host port 3389
# FILTER RULES
pass in on $lan_if inet proto tcp from $lan_net to any port $tcp_services modulate state
pass in on $lan_if inet proto udp from $lan_net to any port $udp_services keep state
pass out on $ext_if inet from $lan_net to any keep state
pass out on $lan_if inet proto tcp to $rdp_host port 3389 keep state
# Allow SSH access to the OpenBSD firewall itself
pass in inet proto tcp from any to self port ssh keep state
# By default, do not permit remote connections to X11
block return in on ! lo0 proto tcp to port 6000:6010
SSH redirection example
Redirection rule
- Redirect incoming SSH traffic to
10.0.0.2:
pass in quick on $ext_if proto tcp from any to ($ext_if) port ssh rdr-to 10.0.0.2 port ssh
- Allow the forwarded SSH connection:
pass out on $int_if proto tcp to 10.0.0.2 port ssh keep state