Problem Description: OPC UA Client Fails to Connect Through a NAT Router
When a SIMATIC S7-1500 (or ET 200SP) OPC UA client tries to reach a server across a Network Address Translation (NAT) boundary, a 1:1 DNAT mapping, or a stateful perimeter firewall, the connection that worked flawlessly on the plant LAN often fails in a specific, repeatable way. The TCP three-way handshake completes, the OPC UA Hello/Ack exchange succeeds, an OpenSecureChannel is established, and the GetEndpoints request returns a list of EndpointDescription records. The very next message, CreateSession, then either times out or returns a status code such as Bad_CommunicationError (hex 0x80050000), Bad_Timeout (hex 0x800A0000), or a stack-specific BadConnectionRejected error. In UaExpert (Unified Automation) the user typically sees a timeout pop-up and a status string ending in BadConnectionClosed; in the S7-1500 OPC UA client FB the instance DB status output shows values in the 16#8005_0000 to 16#800A_0000 range.
The root cause is not certificate trust, not authentication, and not the OPC UA server license — it is the URL that the server advertises in its GetEndpointResponse. A server that is bound to its internal interface (e.g., 192.168.10.50) will hand that private IP back to the client, even when the client reached the server through a public IP (203.0.113.25). The client, now stuck with the server's internal IP, attempts to open a new SecureChannel to 192.168.10.50 — an address it cannot route to from outside the LAN. The session then dies.
Siemens documented this exact failure mode in support entry 109766709, where the remedy section explicitly instructs the integrator to override the IP address returned in the GetEndpointResponse with the IP address used in the GetEndpointRequest. This article expands on that remedy with a full engineering walkthrough: protocol-level diagnosis, code-level override, alternative architectures (reverse connect, VPN, server-side configuration), and a verification matrix that lets a commissioning engineer prove the fix end-to-end.
OPC UA Connection Sequence and the GetEndpoint Service
OPC UA Part 4 of the specification defines a canonical client-to-server connection sequence built on the OPC UA Binary protocol over TCP (default port 4840). Understanding the message flow is essential before applying any fix, because the override targets step 4 of the sequence.
| Step | Service | Direction | Purpose | Wire-Level Identifier |
|---|---|---|---|---|
| 1 | OPC UA Hello | Client → Server | Propose protocol version, buffer sizes, max chunk count, endpoint URL | MessageType HEL
|
| 2 | OPC UA Acknowledge | Server → Client | Acknowledge or reject with BadProtocolVersion
|
MessageType ACK
|
| 3 | OpenSecureChannel | Client → Server | Negotiate security policy, derive keys, agree on channel and token IDs | MessageType OPN
|
| 4 | GetEndpoints | Client → Server | Discover all EndpointDescription records the server exposes |
MessageType MSG, NodeId numeric 428 (GetEndpoints) |
| 5 | CreateSession | Client → Server | Create a logical session using one of the returned endpoint URLs | MessageType MSG, NodeId numeric 461 |
| 6 | ActivateSession | Client → Server | Provide UserIdentityToken and session signature | MessageType MSG, NodeId numeric 467 |
| 7 | Read / Write / Subscribe / Browse / Call | Bidirectional | Application-level traffic | MessageType MSG
|
The GetEndpoints service (OPC UA Part 4, §5.6.5) is the source of the problem. The server returns an array of EndpointDescription structures. The most important field in each is EndpointUrl, an OPC UA URL of the form opc.tcp://<host>:<port>/<optional-path>. A typical response contains one entry per combination of SecurityPolicy and MessageSecurityMode, for example:
opc.tcp://192.168.10.50:4840
opc.tcp://PLC-1500-1.contoso.local:4840
opc.tcp://[2001:db8::5]:4840
The wire-level failure chain on a NAT link looks like this:
- The integrator configures the discovery URL
opc.tcp://203.0.113.25:4840(public IP, NAT IP, or DDNS hostname). - TCP terminates at the server's internal address after NAT translation, e.g.
192.168.10.50:4840. - The server's
GetEndpointResponseis generated using its configured bind address or local interface address — almost always the internal IP, hostname, or FQDN. - The client receives
opc.tcp://192.168.10.50:4840as the endpoint, but it sits on a network that cannot route to192.168.10.50. - The client attempts to open a new SecureChannel to
192.168.10.50:4840and fails with a timeout orBadCommunicationError.
The OPC UA specification does not mandate that the returned EndpointUrl match the discovery URL — and this is where the spec collides with the realities of NAT, reverse proxies, and split-horizon DNS. Siemens' documented remedy sidesteps the ambiguity by having the client perform a pragmatic substitution: the response URL is replaced with the request URL's host portion. This is a client-side concern; the server configuration is rarely under the integrator's control when the server is, for example, a third-party MES, a vendor-managed PLC, or a hosted cloud OPC UA endpoint.
Root Cause: Why NAT Breaks the Endpoint URL Exchange
NAT devices translate addresses only on packets that traverse the device. The OPC UA server is unaware of the public IP the client used to reach it — unless the server is explicitly configured to publish its public IP, the server's GetEndpointResponse will use the address on which the server's listening socket is bound. The following network scenarios all converge on the same symptom:
| Scenario | Server-side endpointUrl
|
Client behavior |
|---|---|---|
| Server bound to internal IP | opc.tcp://192.168.10.50:4840 |
Client cannot route to RFC1918 address over the public Internet |
| Server bound to internal hostname | opc.tcp://plc1500.local:4840 |
DNS resolution fails on the client side, or returns a wrong address |
| Server behind 1:1 NAT still advertises internal IP | opc.tcp://192.168.10.50:4840 |
Identical to the first row — same root cause |
| Server with multiple NICs, wrong default route |
opc.tcp://10.0.0.5:4840 (DMZ NIC) |
Client cannot reach the DMZ subnet |
| Server bound to IPv6, client on IPv4 | opc.tcp://[2001:db8::5]:4840 |
Client stack has no IPv6 path |
| Server with split-horizon DNS misconfiguration | URL contains internal FQDN | Public resolver returns public IP but server still binds internal |
The diagram below illustrates the failing path through a typical NAT topology. The client at 203.0.113.25 reaches the NAT at 198.51.100.10, which forwards to the server's internal 192.168.10.50. The GetEndpointResponse carries the internal IP back to the client, and the client has no return path to that address.
Affected Siemens Hardware, Firmware, and TIA Portal Versions
The issue is independent of the OPC UA server vendor. It manifests anywhere a client and server are separated by a NAT. The following Siemens products are typical on the client side and the server side.
Typical server-side hardware (where the OPC UA server runs):
- S7-1500 CPU, firmware V2.0 or higher — for example CPU 1515-2 PN (6ES7515-2AM02-0AB0), CPU 1511-1 PN (6ES7511-1AK02-0AB0), CPU 1518-4 PN/DP (6ES7518-4AP00-0AB0).
- S7-1200 CPU, firmware V4.4 or higher — for example CPU 1215C DC/DC/DC (6ES7215-1AG40-0XB0).
- ET 200SP CPU 1510SP-1 PN (6ES7510-1DJ02-0AB0) or 1512SP-1 PN (6ES7512-1DK01-0AB0), firmware V2.0 or higher.
- CP 1543-1 (6GK7543-1AX00-0XE0) for routing through firewalls with NAT.
Typical client-side components:
- S7-1500 CPU with the SIMATIC OPC UA Client library, available in TIA Portal V16 SP1 or higher. The library is found in Libraries → SIMATIC → OPC UA Client and is commonly referenced as
LBC_UA_ClientorOPC_UA_Clientdepending on the TIA version. Reference the S7-1500 OPC UA function manual available in the Siemens support portal for block-level documentation. - PC-based clients: UaExpert (Unified Automation), Ignition by Inductive Automation, Kepware/KEPServerEX, NI LabVIEW DSC, MatrikonOPC Explorer, custom .NET Standard / C++ applications built on the OPC Foundation reference stack.
Library compatibility matrix:
| TIA Portal Version | OPC UA Client Library | Block Names | Notes |
|---|---|---|---|
| V16 SP1 | OPC UA Client V1.0 |
UA_Connect, UA_Disconnect, UA_ReadList, UA_WriteList
|
Basic feature set |
| V17 | OPC UA Client V2.0 | Above + UA_Browse, UA_MethodCall
|
Adds structured browse; ServerEndpointURL writable |
| V18 | OPC UA Client V3.0 | Above + UA_Translate, UA_Subscribe
|
Translation lists, subscription sets |
| V19 | OPC UA Client V3.1 | Same as V3.0 with firmware-aligned bug fixes | Current production |
GetEndpoints internally, so the override must be performed by a custom client, by library flags (where exposed), or by replacing the S7-1500 OPC UA client with a PC-based client.Diagnostic Procedure: Confirming the NAT GetEndpoint Mismatch
Before applying the remedy, prove that the failure is in fact the EndpointUrl mismatch and not, for example, certificate trust, TCP port blocking, or application-level authentication. The diagnostic procedure has four steps that should be executed in order.
Step 1 — Reachability from the public side. From a host on the same network as the OPC UA client, run tcping <public-or-NAT-IP> 4840 (Windows: Test-NetConnection -Port 4840; Linux: nc -vz <ip> 4840). A successful TCP open proves the port forward and firewall rules are correct. If the port is closed, fix the NAT and firewall before continuing.
Step 2 — Wireshark capture on the WAN side of the NAT. Filter on opcua (or tcp.port == 4840). Confirm the following on the wire:
- TCP three-way handshake completes (SYN, SYN-ACK, ACK).
- OPC UA Hello (
HELon the wire) and Ack (ACK) exchange completes. -
OpenSecureChannelRequestandOpenSecureChannelResponsesucceed — expand the message and verify theServiceResultisGood(hex0x00000000). -
GetEndpointsRequestis sent. -
GetEndpointsResponsereturns one or moreEndpointDescriptionrecords.
Expand the GetEndpointsResponse in Wireshark and inspect the EndpointUrl field of each EndpointDescription. If the URL contains a private IP (10.x, 172.16-31.x, 192.168.x), an internal hostname that does not resolve on the public side, an internal FQDN, a DMZ address, or an IPv6 address the client cannot route, the diagnosis is confirmed.
Step 3 — Server-side packet capture. On the server's internal interface, run Wireshark and filter on the OPC UA port. Confirm the server is binding the listening socket to the internal IP, which causes the URL it advertises to be the internal address. If the server is an S7-1500, this is configured under CPU Properties → OPC UA Server → Server Settings → Endpoint configuration. If the server is a third-party application, the bind address is usually controlled by the application's configuration file.
Step 4 — Quick verification with UaExpert. Open UaExpert, add a new server with the discovery URL opc.tcp://<public-or-NAT-IP>:4840, and connect. Open the certificate dialog and inspect the server endpoint URL reported. If it differs from the discovery URL, the bug is reproduced. UaExpert will then fail with BadConnectionClosed or BadTimeout when it tries to open a new SecureChannel to the server's advertised URL.
Diagnostic decision matrix:
| Symptom observed | Likely cause | Next step |
|---|---|---|
| TCP three-way handshake fails | Port forward missing or firewall blocking 4840 | Fix NAT/firewall; re-test |
| Handshake OK, OpenSecureChannel fails | Certificate trust not configured on the client | Trust the server certificate in TIA Portal / UaExpert |
| GetEndpointResponse returns internal IP | NAT GetEndpoint mismatch (this article) | Apply KB 109766709 override |
CreateSession returns BadIdentityTokenRejected (0x80200000) |
Wrong username/password or anonymous not allowed | Configure user identity token |
Connect succeeds, but Read/Write returns BadUserAccessDenied (0x801F0000) |
User lacks read/write permission on the target node | Add user to the appropriate role on the server |
| Session drops every 30–60 seconds | NAT session table timeout; no TCP keepalives | Enable TCP keepalives on the client and server |
Solution 1: GetEndpointRequest IP Override (Siemens KB 109766709)
This is the remedy Siemens prescribes and is the most surgical fix. It requires access to the GetEndpointResponse payload in the client code. The logic, expressed in IEC 61131-3 Structured Text (SCL) for an S7-1500 and as pseudocode for a PC client, is the same: extract the host portion of the discovery URL, extract the host portion of each returned endpoint URL, compare, and substitute if they differ.
// Pseudocode for the override logic (per Siemens KB 109766709)
Function EstablishSession(RequestUrl, ResponseEndpoints):
For each ep in ResponseEndpoints:
requestedHost = ExtractHost(RequestUrl) // "203.0.113.25"
responseHost = ExtractHost(ep.endpointUrl) // "192.168.10.50"
If requestedHost <> responseHost Then
ep.endpointUrl = ReplaceHost(ep.endpointUrl, requestedHost)
End If
End For
// Continue CreateSession using the (now-corrected) endpointUrl
End Function
The helper functions ExtractHost and ReplaceHost are straightforward URL parsers. ExtractHost returns the substring between :// and the next : or /. ReplaceHost returns a new string with the host segment swapped. Both functions must correctly handle IPv6 hosts (which are wrapped in square brackets, e.g., [2001:db8::5]); the bracket characters must be preserved in the output.
Platform-specific override hook points:
| OPC UA stack | Override hook point | Implementation note |
|---|---|---|
| OPC Foundation .NET Standard (2.x) | Intercept EndpointDescription from DiscoveryClient.GetEndpointsAsync
|
Mutate EndpointUrl before passing to Session.CreateAsync
|
| OPC Foundation C++ (1.x) | Override GetEndpoints in the client SDK |
Wrap the call in a helper class |
| Unified Automation UaExpert / .NET SDK | Use UAClientSdk::ServerUrl setter after discovery |
Custom client only — UaExpert itself does not expose this directly |
| Kepware/KEPServerEX OPC UA client driver | Use the Use discovery URL flag in channel properties (if exposed) | Check the channel's advanced settings |
| NI LabVIEW DSC | Configure Use URL as configured in the I/O server binding | Pre-populate the URL with the public IP |
| Siemens LBC_UA_Client (S7-1500) | No direct hook — workaround required (see below) | Use ServerEndpointURL parameter or replace with PC client |
TIA Portal OPC UA Client library workarounds: The standard OPC UA Client FB library for S7-1500 does not expose GetEndpoints directly. Three practical workarounds exist:
-
Use the discovery URL as the public IP and pre-populate the connection DB's
ServerEndpointURLparameter. In TIA Portal V17 library V2.0 and later, this parameter is writable from the user program. Setting it to the public IP forces the library to use that URL for the session. The library still issuesGetEndpointsinternally, but the subsequentCreateSessionuses the configured URL. This is the closest thing to a one-line fix in TIA Portal. -
Build a custom OPC UA client on the S7-1500 using
TCON,TSEND,TRCV, andTUSEND/TURCV(for TLS). Parse the OPC UA binary encoding manually, extract theGetEndpointResponsepayload, perform the override, and proceed withCreateSession. This is non-trivial but is the only way to implement the override purely on the S7-1500 with full transparency. - Replace the S7-1500 OPC UA client with a PC-based client service. Run a small OPC UA client on a PC (Windows or Linux) that performs the IP override and exposes the data via a SIMATIC S7 connection (PUT/GET, S7 Comm, or OPC UA server on the PC). The S7-1500 then becomes a server to the PC client, and the PC client is the gateway. This is the most flexible architecture and is well-suited to multi-server aggregations.
GetEndpoints, ignore the server's internal IP, and use the configured public IP for CreateSession. This is effectively the Siemens remedy implemented at the library level.Alternative Solutions: Reverse Connect, VPN, and Server Configuration
When the client cannot be modified — for example, the client is a third-party SCADA package, an OPC UA wrapper around a proprietary bus, or a vendor appliance — the fix must live in the network or on the server. Three alternatives address the same root cause without touching the client code.
OPC UA Reverse Connect (server-initiated). OPC UA Part 4 defines an extension where the server opens a connection to a pre-configured client. This is the cleanest technical answer to NAT, and the OPC Foundation explicitly recommends it for firewall and NAT scenarios. The flow becomes:
- Server has a configured list of client endpoints (URL, certificate trust, security policy).
- Server opens an outbound TCP connection to the client, traversing the NAT (outbound is allowed by default; inbound is not).
- The client accepts; the server performs the Hello, security handshake, and discovery in the reverse direction.
- From the client's perspective, the server is "pushed" the connection. No IP override is needed.
Siemens supports reverse connect on S7-1500 servers via configuration in TIA Portal. Under CPU Properties → OPC UA Server → Server Settings, locate the ReverseConnect settings and add the client endpoint URL. The function manual available in the Siemens support portal details the exact paths for each TIA Portal version.
Site-to-Site VPN or TLS tunnel. If NAT cannot be eliminated, encapsulate the OPC UA traffic in a tunnel:
-
IPsec / WireGuard / OpenVPN site-to-site between the two sites. OPC UA then sees a flat L3 path; the
EndpointUrlreturned by the server (often an internal address) is now routable through the tunnel. - SSH reverse tunnel for ad-hoc access when one side has SSH but not VPN.
- Cloud relay: AWS IoT SiteWise, Azure Industrial IoT, Siemens Insights Hub (formerly MindSphere), or a generic MQTT broker can carry the data. This is a different architecture, not a NAT fix per se.
The VPN approach has the advantage of securing the traffic with an additional layer, but adds configuration overhead, latency, and a single point of failure. For OPC UA traffic carrying sub-second process data, the added latency of a cloud relay is usually unacceptable.
Server-side endpoint URL configuration. If the server is also a Siemens PLC under the integrator's control, configure the OPC UA server to advertise a specific EndpointUrl. In TIA Portal:
- CPU Properties → OPC UA Server → Server Settings → Endpoint configuration.
- Manually set the
EndpointUrlto the public-facing hostname or IP. - The server then includes that URL in
GetEndpointResponse, eliminating the mismatch.
This works when the server is the integrator's responsibility, the public IP is stable (or a DDNS name is used), and the server is not required to also be reachable on its internal address for local clients. If local clients need the internal URL, the server can advertise multiple endpoints with different URLs (OPC UA supports this). The client then selects the one matching its reachable path.
TIA Portal Implementation: Code Skeleton and FBs
The following is an SCL (Structured Control Language) skeleton for an S7-1500 that demonstrates the IP-override logic in a function block, intended to be called before LBC_UA_Connect or equivalent. The example assumes the discovery URL is stored in a tag and the ServerEndpointURL parameter on the OPC UA client instance DB is writable from the user program.
FUNCTION_BLOCK "FB_UA_NAT_Override"
VAR
DiscoveryURL : STRING; // opc.tcp://203.0.113.25:4840
CorrectedURL : STRING[255];
RequestHost : STRING[64];
ResponseHost : STRING[64];
SchemeEndPos : INT;
HostStartPos : INT;
HostEndPos : INT;
END_VAR
BEGIN
// Extract host from the discovery URL the client will use.
// URL format: opc.tcp://<host>:<port>/<path>
SchemeEndPos := FIND(DiscoveryURL, "://");
IF SchemeEndPos = 0 THEN
// No scheme; treat the whole string as the host.
RequestHost := DiscoveryURL;
ELSE
HostStartPos := SchemeEndPos + 3;
HostEndPos := FIND(DiscoveryURL, ":", HostStartPos);
IF HostEndPos = 0 THEN
HostEndPos := FIND(DiscoveryURL, "/", HostStartPos);
END_IF;
IF HostEndPos = 0 THEN
HostEndPos := LEN(DiscoveryURL) + 1;
END_IF;
RequestHost := MID(DiscoveryURL,
HostEndPos - HostStartPos + 1,
HostStartPos);
END_IF;
// Caller must have obtained ResponseURL from GetEndpoints.
// Apply the same extraction logic to ResponseURL -> ResponseHost.
IF RequestHost <> ResponseHost THEN
// Substitute the request host into the response URL.
CorrectedURL := ReplaceHostInURL(ResponseURL, RequestHost);
ELSE
CorrectedURL := ResponseURL;
END_IF;
// Pass CorrectedURL to LBC_UA_Connect or equivalent.
// In TIA Portal V17+ library V2.0+:
// "LBC_UA_Connect_DB".ServerEndpointURL := CorrectedURL;
END_FUNCTION_BLOCK
Helper function ReplaceHostInURL is a straightforward string substitution. The block LBC_UA_Connect accepts the corrected URL via the instance DB parameter ServerEndpointURL in TIA Portal V17 (library V2.0) and later. Earlier library versions (V1.x in TIA V16) do not expose this as a writable input; in that case, workaround 3 (replace the S7-1500 OPC UA client with a PC-based client) is the practical path.
Custom OPC UA client on the S7-1500 (TCON/TSEND/TRCV approach). For full control over the binary OPC UA protocol, build the GetEndpoints request manually. The OPC UA binary encoding is defined in Part 6 of the specification; the high-level sequence is:
-
TCON— establish TCP connection to<public-ip>:4840. -
TSEND— transmit the Hello message:HEL+ final length + protocol version + receive buffer + send buffer + max message size + max chunk count + endpoint URL. -
TRCV— receive the Acknowledge (ACK) message. Validate that the protocol version matches. -
TSEND— transmitOpenSecureChannelRequestwithSecurityPolicy = Nonefor the discovery channel. Message typeOPN, chunk typeF(final). -
TRCV— receiveOpenSecureChannelResponse. Capture the assignedSecureChannelIdandTokenId. -
TSEND— transmitGetEndpointsRequestwith the discovery URL in theEndpointUrlfield. Message typeMSG, chunk typeF. -
TRCV— receiveGetEndpointsResponse. Parse eachEndpointDescriptionand extract theEndpointUrl. - Compare host portion, substitute as in Solution 1, then proceed with
CreateSessionusing the substituted URL. - Switch the SecureChannel to the substituted endpoint. The new channel is opened on the same TCP connection (different
SecureChannelId).
This approach is non-trivial — the OPC UA binary encoding is verbose, and the chunking / final-bit handling must be correct. The advantage is that the entire flow is transparent, and the override is implemented exactly as Siemens describes in KB 109766709.
Solution comparison matrix:
| Solution | Pros | Cons | Best for |
|---|---|---|---|
| GetEndpoint IP override (KB 109766709) | No network changes, surgical fix, fully spec-compliant workaround | Requires client-side code access, must be re-implemented per platform | Single client, full control of stack, PC-based or custom S7-1500 client |
| OPC UA Reverse Connect | No client-side override, server-initiated, NAT-friendly by design | Requires stable client endpoint reachable from server, additional server config | Server under integrator control, blocked inbound, multi-client setup |
| Site-to-Site VPN | Generic, secures all traffic, transparent to applications | Latency, configuration overhead, single point of failure | Multi-service, security-conscious deployments, brownfield integrations |
| Server-side URL configuration | No client change, supported natively in TIA Portal | Requires server access, breaks local clients if not configured for split-horizon | Single-server, single-client setups where the server is integrator-managed |
Verification, Acceptance Test, and Troubleshooting Matrix
After applying any of the four solutions, validate with the following acceptance test sequence. Each step produces an unambiguous pass/fail criterion.
-
Network reachability. From the client subnet,
tcping <public-ip> 4840(orTest-NetConnection -Port 4840) returns success. Failure here means the port forward is missing — do not proceed. -
GetEndpoints round-trip. With Wireshark on both sides, confirm the
GetEndpointResponseURL matches the path the client will use forCreateSession. If applying the override, confirm the client's outgoingCreateSessionRequestuses the substituted URL. -
Session establishment.
LBC_UA_Connectreturnsstatus = 0(Good). For UaExpert, the session transitions from "Connected" to "Active" without an error pop-up. -
Read smoke test. Read a known tag (e.g.,
ns=1;s="TestTag"), confirm the value matches the server. Use a tag that is updated by the PLC program or has a fixed known value. - Write smoke test. Write a value, read it back, confirm. Use a dedicated test tag — never write to a process tag during commissioning.
- Subscription test (if applicable). Create a monitored item, confirm publishing and value updates at the configured sampling interval. If publishing is missed, the subscription's RevisedPublishingInterval on the server may differ from the request — adjust the request interval upward by 10% as a margin.
- Long-run stability test. Leave the session open for 24–72 hours. Monitor for reconnection storms caused by the NAT session table timing out. If reconnects are excessive (more than once per hour), configure TCP keepalives on both the client and server. For Siemens S7-1500, the OPC UA Connection settings include a Keep-Alive Time parameter; set it to 30 seconds or less.
-
Failover test (if using a VPN). Drop the VPN, re-establish, confirm the OPC UA session recovers. The OPC UA client should automatically reconnect; if not, the
SessionTimeoutparameter may be too short. Increase it to match the expected VPN re-establishment time. -
Security posture. Re-check that the server certificate is trusted (UA Expert certificate store, Windows certificate store, or TIA Portal Trusted Partners), the SecurityPolicy is
Basic256Sha256or stronger (avoidNoneandBasic128Rsa15in production — both are deprecated by the OPC Foundation), and that the user identity token type matches the server policy.
Comprehensive troubleshooting matrix:
| Symptom | Likely root cause | Resolution |
|---|---|---|
| TCP three-way handshake fails | Port 4840 not forwarded, firewall blocking, wrong public IP | Verify NAT/firewall rules; check IP with curl ifconfig.me from server |
Handshake OK, OpenSecureChannel fails with BadCertificateUntrusted (0x801A0000) |
Client does not trust the server certificate | Add the server certificate to the client's trust list |
| OpenSecureChannel OK, GetEndpointResponse returns internal IP, CreateSession times out | NAT GetEndpoint mismatch (this article) | Apply KB 109766709 override or alternative solution |
CreateSession returns BadSecurityPolicyRejected (0x80130000) |
Client and server do not share a common SecurityPolicy | Lower the client's minimum SecurityPolicy (e.g., allow Basic256Sha256) or raise the server's |
ActivateSession returns BadIdentityTokenRejected (0x80200000) |
Wrong username/password, or anonymous denied when required | Re-enter credentials; enable anonymous if appropriate for the deployment |
Session established, but Read/Write returns BadUserAccessDenied (0x801F0000) |
User lacks read/write permission on the target node | Add the user to the appropriate role on the server (S7-1500: CPU Properties to OPC UA Server to User Management) |
| Session drops every 30–60 seconds, then reconnects | NAT session table timeout; no TCP keepalives | Enable TCP keepalives on the client and server; reduce SessionTimeout on the server |
Session drops, reconnect fails with BadNonceInvalid (0x80240000) |
Server clock skewed; the nonce validation fails | Synchronize clocks via NTP; on S7-1500, enable Time synchronization in CPU Properties |
| Reads return stale data after network blip | Subscription is using cached values | Call UA_ReadList to refresh; re-create the subscription if the session was re-established |
OPC UA Client FB returns 16#8001_0000 on disconnect |
Generic BadCommunicationError; check the connection status word | Inspect the instance DB status and connectionState fields; re-initialize the connection |
Hex status code reference (commonly encountered):
| Hex code | Symbolic name | Typical meaning in this scenario |
|---|---|---|
| 0x00000000 | Good | Success |
| 0x80000000 | Bad (generic) | High bit set indicates an error |
| 0x80050000 | BadCommunicationError | Cannot reach the endpoint — typical of NAT mismatch |
| 0x800A0000 | BadTimeout | No response received within the timeout window |
| 0x80130000 | BadSecurityPolicyRejected | No common SecurityPolicy between client and server |
| 0x801A0000 | BadCertificateUntrusted | Server certificate not in the client's trust store |
| 0x801F0000 | BadUserAccessDenied | User lacks permission on the requested node |
| 0x80200000 | BadIdentityTokenRejected | Invalid username/password or anonymous denied |
| 0x80240000 | BadNonceInvalid | Clock skew between client and server |
| 0x80260000 | BadSessionClosed | The session was closed by the server |
| 0x80270000 | BadSessionNotActivated | ActivateSession not completed before use |
Frequently Asked Questions
Why does my Siemens OPC UA client connect fine on the LAN but fail over the internet?
The server's GetEndpointResponse advertises its internal IP (e.g., 192.168.10.50:4840), but the client cannot route to that RFC1918 address from outside the LAN. The client must either override the IP with the public/NAT IP it used in the GetEndpointRequest (per Siemens KB 109766709), use OPC UA reverse connect, or terminate the NAT with a VPN.
Does the TIA Portal OPC UA Client FB library perform the GetEndpoint IP override automatically?
No. The standard LBC_UA_Client / OPC_UA_Client libraries use the URL returned by the server. To override, you must either configure the discovery URL and pre-populate the ServerEndpointURL parameter in the instance DB (TIA V17+ library V2.0 and later), build a custom client on top of TCON/TSEND/TRCV with manual OPC UA binary parsing, or replace the S7-1500 client with a PC-based client that intercepts the GetEndpointResponse.
What is the cleanest technical fix for OPC UA across a NAT boundary?
OPC UA reverse connect. The server opens an outbound TCP connection to the client, which traverses NAT without requiring inbound firewall rules. The client no longer needs to perform any IP override because it is the one accepting the connection. Configure reverse connect in the server's TIA Portal OPC UA Server settings and provide a stable client endpoint URL.
Which OPC UA error code indicates a GetEndpoint URL mismatch?
The mismatch itself does not produce a unique error code. The client typically sees Bad_CommunicationError (hex 0x80050000), BadTimeout (hex 0x800A0000), or BadSessionClosed (hex 0x80260000) when it cannot reach the advertised endpoint. Capture the GetEndpointResponse in Wireshark to confirm the URL mismatch is the root cause before chasing the status code.
Can UaExpert be configured to use the discovery URL even when the server returns a different one?
UaExpert honors the server's GetEndpointResponse by default. The cleanest workaround is to add a custom URL pointing at the public IP, select Custom Discovery in the Advanced tab of the Add Server dialog, and connect directly to that URL. This bypasses the GetEndpoints discovery phase and forces the session URL to the custom value. For programmatic control, use the Unified Automation .NET SDK and override the URL programmatically.
What TCP keepalive settings are recommended for OPC UA across a NAT?
Configure TCP keepalives to 30 seconds on both the client and the server. Most NAT devices drop idle TCP sessions after 60–120 seconds; a 30-second keepalive interval keeps the session table entry alive without generating excessive traffic. On the S7-1500, the OPC UA Connection settings include a Keep-Alive Time parameter. On Linux clients, set the socket options SO_KEEPALIVE, TCP_KEEPIDLE, TCP_KEEPINTVL, and TCP_KEEPCNT accordingly.