A VPN server that answers from a mobile network and fails from the sofa is usually not a certificate problem. The client inside the LAN is asking the router to translate a packet whose destination is the router's own public address, which means the packet has to leave and come straight back in. That manoeuvre is hairpinning, and plenty of routers either do not implement it or implement it only when a checkbox is enabled. The outside test passing is what makes this confusing, because it proves the listener and the forwarding rule are fine.

The packet goes out and comes back in

Walk the packet. The client resolves the public name, connects to the public address, and the router's destination NAT rewrites the destination to the internal server. The reply from the server is addressed to the client's LAN address, so it goes back through the router's LAN interface without ever traversing the NAT rule that would have un-translated the source.

Now the client receives a packet from an address it never sent anything to, and drops it. That asymmetry is the whole failure.

RFC 4787 describes the behaviour a NAT is expected to have in this case, and the short version is that it is optional in practice even where it is specified in theory. Some routers call the setting NAT loopback, others call it hairpin NAT or NAT reflection, and some expose nothing at all. A router that lacks it is not misconfigured; it is doing the ordinary thing, and the fix belongs somewhere else. Do not conclude from a failed local test that the public forwarding rule is wrong.

Check 1: test the two paths separately

From outside the LAN, connect with the same profile a real user would use and confirm the session establishes. From inside, connect to the public address with the same profile and record the exact error and the time.

A precise local test keeps the hostname and overrides only the address: curl -v --resolve vpn.example.com:443:203.0.113.10 https://vpn.example.com/ sends the request to the public address while still presenting the correct server name. That separates a name-resolution problem from a translation problem, which are easy to confuse when the same name works from a coffee shop. Note whether the client fails at TCP connect or later at TLS, because the first points at translation and the second at identity.

Check 2: read what the server sees

The most useful observation is the server's view. Run a capture on the VPN host filtered to the client's LAN address and to the router's LAN address, then repeat the local attempt. If nothing arrives, the packet never survived translation.

If a connection arrives from the router's LAN address rather than the client's, hairpinning is happening and the source was rewritten. That is a working configuration, not a fault.

Check 3: fix it without touching the outside path

Three supported shapes exist, in increasing order of blast radius. The narrowest is split DNS: publish an internal record so the same server name resolves to the internal address for clients on the LAN, which avoids the public path entirely. The next is enabling the router's documented loopback or reflection option, if it has one, and applying it to this service only. The widest is a source-NAT rule on the router that rewrites LAN-sourced traffic to the server so the reply returns through the router; on a Linux router that is a MASQUERADE rule in POSTROUTING matching the server's address and port. All three keep the external path untouched, which is the property to preserve.

Do not broaden unrelated port forwarding to make the local path resemble the external one, and do not move the service to a different port to dodge the problem. Both changes alter what the outside world can reach in order to fix something only the inside world sees.

Split DNS has one trap worth naming. Replacing a hostname with an arbitrary IP changes what the client validates, so the internal record must still match the certificate's name.

Check 4: verify both paths afterwards

Repeat the external attempt from outside the LAN, repeat the internal attempt with the same profile, and confirm the server log shows two distinct successful sessions rather than one success and one silence. Then check that other clients still reach the listener and that private-resource routes are unchanged.

Keep the record short: which address each client used, what the server saw, and which of the three fixes was applied. The RFC 4787 hairpinning requirements are the reference for what a NAT is expected to do here, and they explain why a router without the feature is behaving normally. A local failure that the outside world cannot reproduce is a translation question until proven otherwise, so test it as one.

Amanqephe ahlobene