CVE-2026-1642: NGINX Upstream TLS Injection — Detection, Mitigation, and Safe Rollout
When NGINX trusts an upstream TLS certificate it should not, attackers can inject arbitrary HTTP responses into your reverse-proxy traffic. Here is how to detect exposure, lock down your configuration, patch safely, and verify the fix.
CVE-2026-1642 is a response-injection vulnerability in NGINX's upstream TLS handling. When NGINX is configured to proxy traffic to upstream servers over TLS (proxy_pass https://...) without properly validating the upstream certificate, an attacker who controls or can man-in-the-middle the upstream connection can inject crafted HTTP responses that NGINX forwards to clients as legitimate content.
The root cause is a missing enforcement of the proxy_ssl_verify directive when processing upstream TLS handshakes in specific code paths. Even when operators believe they have enabled upstream certificate validation, certain NGINX versions do not enforce it consistently, leaving a trust-boundary gap between the reverse proxy and its backends.
Exposure Matrix
Use this matrix to determine your exposure level and prioritize response actions.
| Condition | Exposure Level | Action Priority |
|---|---|---|
proxy_pass https:// with proxy_ssl_verify off (or directive absent) | Critical | Immediate — apply emergency config + patch |
proxy_pass https:// with proxy_ssl_verify on but no proxy_ssl_trusted_certificate | Critical | Immediate — verification is silently ineffective without a trust store |
proxy_pass https:// with proxy_ssl_verify on + proxy_ssl_trusted_certificate set, running affected NGINX version | High | Patch promptly — the bypass may still apply in certain code paths |
proxy_pass https:// with full verification, running patched NGINX version | Low | Verify configuration and monitor |
proxy_pass http:// only (no upstream TLS) | Not affected | No action required for this CVE (but consider encrypting upstream traffic) |
Important: The default value of proxy_ssl_verify is off. If you have never explicitly set it, you are in the Critical tier.
Affected Versions
- NGINX open-source mainline 1.25.0 – 1.27.3
- NGINX open-source stable 1.26.0 – 1.26.2
- NGINX Plus R30 – R33 (prior to R33-p1)
Fixed in NGINX mainline 1.27.4, stable 1.26.3, and NGINX Plus R33-p1.
Step 1: Detect Exposure
Audit your running NGINX version and configuration to determine whether you are affected.
Check NGINX version
nginx -v 2>&1
# Expected output: nginx version: nginx/1.27.4 or later (patched)Find all upstream TLS proxy_pass directives
# Search all NGINX config files for proxy_pass using https
grep -rn 'proxy_pass\s\+https://' /etc/nginx/
# For each match, check whether proxy_ssl_verify is set in the same server/location block
grep -rn 'proxy_ssl_verify\|proxy_ssl_trusted_certificate\|proxy_ssl_name' /etc/nginx/Check for evidence of exploitation
Response injection attacks may leave traces in access logs as unexpected status codes, content-length mismatches, or requests to paths that return content inconsistent with the configured upstream.
# Look for anomalous upstream response codes in the error log
grep 'upstream' /var/log/nginx/error.log | grep -i 'SSL\|handshake\|certificate'
# Look for unexpected 502/503 bursts that may indicate handshake failures during injection attempts
awk '$9 ~ /^50[23]/' /var/log/nginx/access.log | tail -50Step 2: Emergency Configuration Actions
Apply these changes immediately to reduce exposure while you plan the patch rollout. These changes do not require an NGINX version upgrade.
Enable upstream certificate verification
For every server or location block that uses proxy_pass https://, add or confirm the following directives:
location /api/ {
proxy_pass https://backend.internal:8443;
# --- Upstream TLS trust boundary ---
proxy_ssl_verify on;
proxy_ssl_verify_depth 2;
proxy_ssl_trusted_certificate /etc/nginx/certs/internal-ca.pem;
proxy_ssl_name backend.internal;
proxy_ssl_server_name on;
}Explanation of each directive:
proxy_ssl_verify on— Require NGINX to validate the upstream server's TLS certificate against the trusted CA bundle.proxy_ssl_verify_depth 2— Allow up to 2 intermediate certificates in the chain. Adjust based on your PKI depth.proxy_ssl_trusted_certificate— Path to the CA certificate (or bundle) that signed your upstream's certificate. Without this, verification has no trust anchor.proxy_ssl_name— The expected hostname in the upstream certificate's SAN/CN. Prevents accepting a valid certificate issued for a different host.proxy_ssl_server_name on— Sends SNI in the upstream TLS handshake, required when upstreams use name-based virtual hosting.
Test before reload
# Validate config syntax
nginx -t
# If syntax is OK, reload gracefully (zero downtime)
nginx -s reloadWarning: If your upstream servers use self-signed certificates and you have not distributed the signing CA, enabling proxy_ssl_verify on will cause all upstream connections to fail with 502 errors. Prepare the CA bundle before enabling verification. If you cannot obtain a proper CA bundle immediately, consider switching to mutual TLS (mTLS) or restricting upstream connectivity at the network level as a temporary measure.
Network-level fallback
If you cannot enable certificate verification immediately (e.g., self-signed certs, no CA bundle available), restrict upstream connectivity as a stopgap:
# Example: restrict NGINX worker to only reach known backend IPs
# (iptables — adapt for nftables or cloud security groups as needed)
iptables -A OUTPUT -m owner --uid-owner nginx -d 10.0.1.10 -p tcp --dport 8443 -j ACCEPT
iptables -A OUTPUT -m owner --uid-owner nginx -d 10.0.1.11 -p tcp --dport 8443 -j ACCEPT
iptables -A OUTPUT -m owner --uid-owner nginx -p tcp --dport 8443 -j DROPStep 3: Patch and Upgrade
The emergency config actions reduce risk but do not fully address the code-path bypass in affected versions. Upgrade NGINX to a patched release.
Upgrade from official repositories
# Debian / Ubuntu (official NGINX repo)
apt update && apt install nginx
# RHEL / CentOS / Rocky
yum update nginx
# or
dnf update nginx
# Verify the installed version
nginx -v 2>&1Container images
# Pull the patched image
docker pull nginx:1.27.4
# or for stable
docker pull nginx:1.26.3
# Verify
docker run --rm nginx:1.27.4 nginx -vSafe rollout strategy
- Canary first: Upgrade one instance behind a load balancer. Monitor error rates, upstream latency, and TLS handshake failures for at least 30 minutes before proceeding.
- Watch for 502s: The patched version enforces upstream verification more strictly. Any previously tolerated certificate issues will now surface as 502 errors.
- Rollback plan: Keep the previous NGINX binary or container image tagged and ready. If 502 rates spike, rollback the binary and investigate upstream certificate configuration before retrying.
Step 4: Validate the Fix
After patching and applying the configuration hardening, verify that upstream TLS verification is working correctly.
Confirm NGINX version is patched
nginx -v 2>&1 | grep -E '1\.27\.[4-9]|1\.26\.[3-9]'
# Should match — if no output, you are still on a vulnerable versionVerify upstream certificate validation is active
# Dump the effective config and check proxy_ssl directives
nginx -T 2>/dev/null | grep -A2 'proxy_ssl_verify'
# Expected: proxy_ssl_verify on; for every upstream TLS blockTest with an invalid certificate
Temporarily point a test location block at a server with a self-signed or mismatched certificate. NGINX should refuse the connection and return a 502.
# In a test config (do NOT deploy to production):
location /tls-verify-test/ {
proxy_pass https://self-signed-test.internal:8443;
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/nginx/certs/internal-ca.pem;
proxy_ssl_name self-signed-test.internal;
}
# After reload, request the test path:
curl -s -o /dev/null -w "%{http_code}" http://localhost/tls-verify-test/
# Expected: 502 (upstream certificate rejected)
# Check the error log for the verification failure:
tail -5 /var/log/nginx/error.log
# Expected: "upstream SSL certificate verify error" or similarVerify legitimate upstreams still work
# Smoke-test each upstream-backed path
curl -s -o /dev/null -w "%{http_code}" https://your-site.com/api/health
# Expected: 200 (or your normal healthy response code)
# Check for upstream TLS errors in the past 10 minutes
grep 'SSL' /var/log/nginx/error.log | awk -v cutoff="$(date -d '10 minutes ago' '+%Y/%m/%d %H:%M')" '$0 >= cutoff'Step 5: Post-Incident Monitoring
After patching and hardening, maintain heightened monitoring for at least 7 days to catch any residual issues or signs of prior exploitation.
Metrics to watch
- 502/503 error rate: A spike after patching likely indicates an upstream certificate problem, not an attack. Investigate the specific upstream.
- Upstream TLS handshake latency: Should remain stable. A sudden increase may indicate certificate chain issues or OCSP stapling failures.
- Unexpected content in responses: If you have a WAF or content-inspection layer, look for responses that do not match expected upstream content patterns. This can indicate prior injection that was cached by a CDN or downstream proxy.
- Certificate expiry alerts: Now that verification is enforced, expired upstream certificates will cause outages. Ensure monitoring covers upstream cert expiry.
Log-based detection rules
# Alert on upstream SSL errors (add to your log monitoring / SIEM)
# Pattern for NGINX error log:
# "upstream SSL certificate verify error"
# "SSL_do_handshake() failed"
# "peer closed connection in SSL handshake"
# Example: count upstream SSL errors in the last hour
grep -c 'upstream SSL\|SSL_do_handshake.*failed\|peer closed connection in SSL' /var/log/nginx/error.logReview CDN and cache layers
If an attacker successfully injected responses before you patched, those responses may be cached in CDN edge nodes or downstream proxies. Purge caches for any paths served through the affected upstream blocks:
# Example: purge Cloudflare cache for the affected zone
# (adapt for your CDN provider)
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/purge_cache" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{"purge_everything":true}'Long-Term Hardening: Upstream TLS Trust Boundaries
CVE-2026-1642 is a reminder that upstream TLS connections deserve the same scrutiny as client-facing TLS. Apply these practices to prevent similar issues in the future.
1. Pin upstream CAs
Use proxy_ssl_trusted_certificate with a minimal CA bundle containing only the CA(s) that sign your upstream certificates — not the system-wide CA store. This limits the blast radius if any public CA is compromised.
2. Use mutual TLS (mTLS) for internal upstreams
location /internal-api/ {
proxy_pass https://backend.internal:8443;
# Verify upstream's certificate
proxy_ssl_verify on;
proxy_ssl_verify_depth 2;
proxy_ssl_trusted_certificate /etc/nginx/certs/internal-ca.pem;
proxy_ssl_name backend.internal;
proxy_ssl_server_name on;
# Present NGINX's own client certificate to upstream
proxy_ssl_certificate /etc/nginx/certs/nginx-client.pem;
proxy_ssl_certificate_key /etc/nginx/certs/nginx-client-key.pem;
}3. Enforce TLS 1.2+ on upstream connections
# Set minimum upstream TLS version
proxy_ssl_protocols TLSv1.2 TLSv1.3;
proxy_ssl_ciphers HIGH:!aNULL:!MD5;4. Automate configuration audits
Add a CI check or cron job that flags any proxy_pass https:// directive without a corresponding proxy_ssl_verify on:
#!/bin/bash
# audit-upstream-tls.sh — exit non-zero if any upstream TLS block lacks verification
CONFIGS=$(grep -rl 'proxy_pass\s\+https://' /etc/nginx/)
FAIL=0
for f in $CONFIGS; do
if ! grep -q 'proxy_ssl_verify\s\+on' "$f"; then
echo "FAIL: $f has proxy_pass https:// without proxy_ssl_verify on"
FAIL=1
fi
done
exit $FAIL5. Monitor upstream certificate lifecycle
With verification enforced, an expired upstream certificate will cause an outage. Set up alerting for certificates expiring within 30 days:
# Check upstream certificate expiry (replace host and port)
echo | openssl s_client -connect backend.internal:8443 -servername backend.internal 2>/dev/null \
| openssl x509 -noout -enddate
# Output example: notAfter=Jun 15 12:00:00 2026 GMTQuick Reference: Response Checklist
- Identify scope: Run
grep -rn 'proxy_pass.*https://' /etc/nginx/and cross-reference with the exposure matrix above. - Apply emergency config: Set
proxy_ssl_verify on,proxy_ssl_trusted_certificate, andproxy_ssl_nameon every affected block. Test withnginx -t, thennginx -s reload. - Upgrade NGINX: Install 1.27.4+ (mainline), 1.26.3+ (stable), or R33-p1+ (Plus). Canary one instance first.
- Validate: Confirm patched version, verify upstream TLS is enforced, test with an invalid cert, smoke-test production paths.
- Purge caches: If injection may have occurred, purge CDN and proxy caches for affected paths.
- Monitor for 7 days: Watch 502 rates, upstream TLS errors, and response anomalies.
- Harden long-term: Pin upstream CAs, enable mTLS for internal traffic, automate config audits, and monitor cert expiry.