Got it—your client runs in AWS and must use a corporate proxy. With mTLS, a client-side proxy can absolutely be the culprit. Here’s the playbook.
What’s happening
- If the proxy does TLS inspection (MITM), it terminates TLS and re-signs with its own CA. Your server asks the proxy (not the real client) for a cert → it has none → server logs “trying to obtain a certificate from the client.”
- The client may also see “unable to get local issuer certificate” because it’s validating the proxy’s substituted cert but doesn’t trust the proxy’s Root CA.
Decide the path (pick one)
A) Allow end-to-end mTLS (best):
Ask the proxy admins to bypass SSL inspection for your domain (add it to the proxy’s TLS bypass list) or ensure it does pure CONNECT tunneling. Then the real client cert reaches your server.
B) Two-hop mTLS (enterprise pattern):
- Client ↔ Proxy: mTLS using Client-Cert-#1 (issued by proxy’s CA).
- Proxy ↔ Your Server: mTLS using Client-Cert-#2 (issued by a CA your server trusts).
Your server will authenticate the proxy’s identity, not the original client. If you need end-user identity, have the proxy forward vetted identity (headers/JWT) and verify it.
C) Temporary test bypass:
On that AWS instance, set NO_PROXY for your hostname if policy allows, and open egress 443 to your server to confirm the issue is the proxy.
Quick diagnostics (run both)
1) See if proxy is intercepting
# Behind the proxy
openssl s_client -proxy PROXY_HOST:PROXY_PORT \
-connect YOUR_HOST:443 -servername YOUR_HOST -showcerts </dev/null | \
openssl x509 -noout -issuer -subject -enddate
Compare issuer/subject to the same command from a machine without the proxy.
If issuers differ (e.g., “Zscaler/BlueCoat/Corp Root CA”), it’s inspecting.
2) Prove mTLS works through a tunnel
# If proxy supports pure CONNECT (no inspection)
curl -v https://YOUR_HOST/ \
--proxy http://PROXY_HOST:PROXY_PORT \
--cert client.crt --key client.key \
--cacert server_ca_chain.pem
- If your proxy itself is HTTPS, add:
--proxy-cacert PROXY_ROOT_CA.pem.
Java client notes (common in AWS)
# Client cert for mTLS to your server
-Djavax.net.ssl.keyStore=/path/client.p12 -Djavax.net.ssl.keyStorePassword=***** -Djavax.net.ssl.keyStoreType=PKCS12
# Trust your server (or proxy if inspecting)
-Djavax.net.ssl.trustStore=/path/truststore.jks -Djavax.net.ssl.trustStorePassword=*****
# Proxy settings (CONNECT)
-Dhttps.proxyHost=PROXY_HOST -Dhttps.proxyPort=PROXY_PORT
# Bypass (if allowed)
-Dhttp.nonProxyHosts="|YOUR_HOST|*.yourdomain.com"
If inspection can’t be bypassed, you must import the proxy’s Root CA into the truststore, and if mTLS is required by your server, implement the two-hop mTLS model above.
What to send to the proxy team
- “Please bypass TLS inspection (or allow pure CONNECT) for
YOUR_HOST:443.”
- If not possible: “We’ll run two-hop mTLS. We’ll trust your proxy’s client CA for the proxy→server leg; you’ll trust ours for client→proxy.”
If you share the outputs of the two openssl/curl tests (redact hostnames as needed), I’ll tell you exactly whether it’s inspection vs tunneling and the minimal change to make it work.
Short answer: tcpdump can capture payload, but only if the traffic is plaintext.
If it’s HTTPS/TLS (which most APIs are), tcpdump will not show the body unless you decrypt it. It also won’t write into your “app logs”—it just captures packets.
Here are your practical options: