The reason a seemingly obvious
iptables -t nat -A PREROUTING -d 192.168.12.87 -p tcp --dport 80 -j DNAT --to-destination 192.168.12.77 will not work is how the return packets will be routed. You can set up rules that will cause the packets send to 192.168.12.87 to simply be NATted to 192.168.12.77, but 192.168.12.77 will then send replies directly back to the client. Those replies will not go through the host where your iptables rule is doing NAT, hence the packets in one direction are translated, but packets in the other direction are not.
There are three approaches to solving this problem.
1. On the first host don't just do DNAT, but also do SNAT such that return traffic will be send back through the first host. The rule could look something like
iptables -t NAT -A POSTROUTING -d 192.168.12.77 -p tcp --dport 80 -j SNAT --to-source 192.168.12.87
2. Take inspiration from DSR load balancing and DNAT the packets at Ethernet layer instead of at IP layer. By replacing the destination MAC of the packets with the MAC of 192.168.12.77 and sending it on the Ethernet without touching the IP layer, then 192.168.12.77 could have 192.168.12.87 configured on a dummy interface and thus be able to terminate the TCP connection with the server IP known to the client.
3. Use the naive (but not working) solution on the first host. Then handle the return packets on the second host by doing a SNAT on the return traffic. A rule could look like
iptables -t nat -A OUTPUT -p tcp --sport 80 -j SNAT --to-source 192.168.12.87
Each of those three solutions have drawbacks, so you need to carefully consider, if you really need to do this particular forwarding.
1. Using SNAT will lose the client IP, so host number 2 will think all connections came from 192.168.12.87. Additionally you'll use bandwidth through host number 1 for all reply packets, which would take a more direct route with the other approaches.
2. The DSR approach will break all other communication between the two nodes. The DSR approach is really only appropriate when the server address is not the primary IP of any of the hosts. Each host need to have a primary IP, which is not the DSR IP.
3. Using connection tracking on one host to translate in one direction and connection tracking on another host to translate in the other direction is plain ugly, and there are various ways it could break. For example if port numbers are modified by NAT on either host, there is no way to reconstruct those. It is also not a given, that connection tracking will work correctly, if the first packet it sees is a SYN-ACK rather than an ACK.
Of the three approaches I think the first is the one, which is most likely to work. So if you don't need to know the client IP addresses, that is the one I would recommend.
You can also choose to forget about NAT altogether and not try to solve the problem on MAC or IP layer. You can go all the way up to the HTTP layer and look for a solution there. In that case the solution you will find is an HTTP proxy. If you install an HTTP proxy on 192.168.12.87 and configure it appropriately, you can have it forward the requests to 192.168.12.77 and forward the answers back. Additionally it can insert an X-Forwarded-For header preserving the original client IP. The server on 192.168.12.77 then need to be configured to trust the X-Forwarded-For header from 192.168.12.87.
Until next time!