Can't access localhost server after pf redirect After adding some pf rules to redirect certain egress traffic to a localhost server, I can't access that localhost server directly. I can access it through the redirect just fine though.

I basically followed this answer and added the following rules:

rdr pass on lo0 inet proto tcp from any to 169.254.169.254 port 80 -&gt; 127.0.0.1 port 1254
pass out route-to lo0 proto tcp from any to 169.254.169.254 port 80 keep state


After doing this, I can test successfully with curl http://169.254.169.254. However curl http://127.0.0.1:1254 does not work.
If I remove the rules (specifically the rdr rule), 127.0.0.1:1254 works just fine.

When I perform a tcpdump on lo0, I see the following:

08:27:49.659051 IP 127.0.0.1.63994 &gt; 127.0.0.1.1254: Flags [S], seq 677650599, win 65535, options [mss 16344,nop,wscale 5,nop,nop,TS val 1709439529 ecr 0,sackOK,eol], length 0
08:27:49.659177 IP 169.254.169.254.80 &gt; 127.0.0.1.80: Flags [S.], seq 2981033646, ack 677650600, win 65535, options [mss 16344,nop,wscale 5,nop,nop,TS val 1709439529 ecr 1709439529,sackOK,eol], length 0
08:27:49.762036 IP 169.254.169.254.80 &gt; 127.0.0.1.63994: Flags [S.], seq 2981033646, ack 677650600, win 65535, options [mss 16344,nop,wscale 5,nop,nop,TS val 1709439629 ecr 1709439529,sackOK,eol], length 0
08:27:49.762056 IP 127.0.0.1.63994 &gt; 127.0.0.1.1254: Flags [S], seq 677650599, win 65535, options [mss 16344,nop,wscale 5,nop,nop,TS val 1709439629 ecr 0,sackOK,eol], length 0
08:27:49.762089 IP 169.254.169.254.80 &gt; 127.0.0.1.63994: Flags [S.], seq 2981033646, ack 677650600, win 65535, options [mss 16344,nop,wscale 5,nop,nop,TS val 1709439629 ecr 1709439629,sackOK,eol], length 0
08:27:49.866634 IP 127.0.0.1.63994 &gt; 127.0.0.1.1254: Flags [S], seq 677650599, win 65535, options [mss 16344,nop,wscale 5,nop,nop,TS val 1709439729 ecr 0,sackOK,eol], length 0
08:27:49.866690 IP 169.254.169.254.80 &gt; 127.0.0.1.63994: Flags [S.], seq 2981033646, ack 677650600, win 65535, options [mss 16344,nop,wscale 5,nop,nop,TS val 1709439729 ecr 1709439729,sackOK,eol], length 0
08:27:49.967548 IP 169.254.169.254.80 &gt; 127.0.0.1.63994: Flags [S.], seq 2981033646, ack 677650600, win 65535, options [mss 16344,nop,wscale 5,nop,nop,TS val 1709439829 ecr 1709439729,sackOK,eol], length 0
...


So we see the original SYN with the correct source &amp; dest. But then we see two responses coming from 169.254.169.254:80, one to 127.0.0.1:80, and another to 127.0.0.1:63994.

When I add log (all) to the rule, I see similar information:

 00:00:00.000000 rule 0/0(match): rdr out on lo0: 169.254.169.254.80 &gt; 127.0.0.1.80: Flags [S.], seq 1734908561, ack 3748497075, win 65535, options [mss 16344,nop,wscale 5,nop,nop,TS val 1711591000 ecr 1711591000,sackOK,eol], length 0
 00:00:00.100977 rule 0/0(match): rdr out on lo0: 169.254.169.254.80 &gt; 127.0.0.1.80: Flags [S.], seq 1734908561, ack 3748497075, win 65535, options [mss 16344,nop,wscale 5,nop,nop,TS val 1711591101 ecr 1711591000,sackOK,eol], length 0
 00:00:00.000044 rule 0/0(match): rdr out on lo0: 169.254.169.254.80 &gt; 127.0.0.1.80: Flags [S.], seq 1734908561, ack 3748497075, win 65535, options [mss 16344,nop,wscale 5,nop,nop,TS val 1711591101 ecr 1711591101,sackOK,eol], length 0


Why is the rdr rule matching the response traffic and rewriting it? The match rule is defined as to 169.254.169.254 port 80.

This is on High Sierra.
To make it work you'd have to add that rule:


pass quick on lo0


— I typically have it as the first line of filtering rulesets I manage.

To be precise, even this would be enough:


pass in quick on lo0


but why bothering with directions if you typically need it both ways on loopback anyways(?)…