[GH-ISSUE #1616] PROTOCOL_ERROR when curl https #1215

Closed
opened 2026-02-26 06:36:14 +03:00 by kerem · 6 comments
Owner

Originally created by @Showfom on GitHub (Nov 29, 2021).
Original GitHub issue: https://github.com/NginxProxyManager/nginx-proxy-manager/issues/1616

Checklist

  • Have you pulled and found the error with jc21/nginx-proxy-manager:latest docker image?
    • Yes
  • Are you sure you're not using someone else's docker image?
    • Yes
  • Have you searched for similar issues (both open and closed)?
    • Yes

Describe the bug

root@s185 ~ # curl -I https://s.lu/
curl: (92) HTTP/2 stream 0 was not closed cleanly: PROTOCOL_ERROR (err 1)

root@s185 ~ # curl -I https://s.lu/ -vv
*   Trying 45.80.188.90:443...
* Connected to s.lu (45.80.188.90) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=s.lu
*  start date: Oct 31 19:11:37 2021 GMT
*  expire date: Jan 29 19:11:36 2022 GMT
*  subjectAltName: host "s.lu" matched cert's "s.lu"
*  issuer: C=US; O=Let's Encrypt; CN=R3
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x55c059789f70)
> HEAD / HTTP/2
> Host: s.lu
> user-agent: curl/7.74.0
> accept: */*
> 
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
* http2 error: Invalid HTTP header field was received: frame type: 1, stream: 1, name: [upgrade], value: [h2]
* HTTP/2 stream 0 was not closed cleanly: PROTOCOL_ERROR (err 1)
* stopped the pause stream!
* Connection #0 to host s.lu left intact
curl: (92) HTTP/2 stream 0 was not closed cleanly: PROTOCOL_ERROR (err 1)

Nginx Proxy Manager Version

2.9.12

To Reproduce
Steps to reproduce the behavior:

  1. Set a proxy with HTTP/2 support
  2. Use curl to check

Operating System

Debian 11

Originally created by @Showfom on GitHub (Nov 29, 2021). Original GitHub issue: https://github.com/NginxProxyManager/nginx-proxy-manager/issues/1616 <!-- Are you in the right place? - If you are looking for support on how to get your upstream server forwarding, please consider asking the community on Reddit. - If you are writing code changes to contribute and need to ask about the internals of the software, Gitter is the best place to ask. - If you think you found a bug with NPM (not Nginx, or your upstream server or MySql) then you are in the *right place.* --> **Checklist** - Have you pulled and found the error with `jc21/nginx-proxy-manager:latest` docker image? - Yes - Are you sure you're not using someone else's docker image? - Yes - Have you searched for similar issues (both open and closed)? - Yes **Describe the bug** <!-- A clear and concise description of what the bug is. --> ``` root@s185 ~ # curl -I https://s.lu/ curl: (92) HTTP/2 stream 0 was not closed cleanly: PROTOCOL_ERROR (err 1) root@s185 ~ # curl -I https://s.lu/ -vv * Trying 45.80.188.90:443... * Connected to s.lu (45.80.188.90) port 443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: /etc/ssl/certs/ca-certificates.crt * CApath: /etc/ssl/certs * TLSv1.3 (OUT), TLS handshake, Client hello (1): * TLSv1.3 (IN), TLS handshake, Server hello (2): * TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8): * TLSv1.3 (IN), TLS handshake, Certificate (11): * TLSv1.3 (IN), TLS handshake, CERT verify (15): * TLSv1.3 (IN), TLS handshake, Finished (20): * TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1): * TLSv1.3 (OUT), TLS handshake, Finished (20): * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 * ALPN, server accepted to use h2 * Server certificate: * subject: CN=s.lu * start date: Oct 31 19:11:37 2021 GMT * expire date: Jan 29 19:11:36 2022 GMT * subjectAltName: host "s.lu" matched cert's "s.lu" * issuer: C=US; O=Let's Encrypt; CN=R3 * SSL certificate verify ok. * Using HTTP2, server supports multi-use * Connection state changed (HTTP/2 confirmed) * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0 * Using Stream ID: 1 (easy handle 0x55c059789f70) > HEAD / HTTP/2 > Host: s.lu > user-agent: curl/7.74.0 > accept: */* > * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): * old SSL session ID is stale, removing * Connection state changed (MAX_CONCURRENT_STREAMS == 128)! * http2 error: Invalid HTTP header field was received: frame type: 1, stream: 1, name: [upgrade], value: [h2] * HTTP/2 stream 0 was not closed cleanly: PROTOCOL_ERROR (err 1) * stopped the pause stream! * Connection #0 to host s.lu left intact curl: (92) HTTP/2 stream 0 was not closed cleanly: PROTOCOL_ERROR (err 1) ``` **Nginx Proxy Manager Version** <!-- What version of Nginx Proxy Manager is reported on the login page? --> 2.9.12 **To Reproduce** Steps to reproduce the behavior: 1. Set a proxy with HTTP/2 support 2. Use curl to check **Operating System** <!-- Please specify if using a Rpi, Mac, orchestration tool or any other setups that might affect the reproduction of this error. --> Debian 11
kerem 2026-02-26 06:36:14 +03:00
  • closed this issue
  • added the
    stale
    bug
    labels
Author
Owner

@the1ts commented on GitHub (Nov 30, 2021):

Looks like this is the known issue of HTTP/2 client connection to a proxy, HTTP/1.1 connection from the proxy to an HTTP/2 capable backend. Nginx uses HTTP/1.1 only to do the proxy in the middle, so the backend HTTP/2 service sends the upgrade header advertising its HTTP/2 credentials to allow an upgrade from the proxy if it can. The problem appears to be nginx frontend leaks that header to the client over the already HTTP/2 connection which it shouldn't by the RFC (curl and safari error on this, others ignore).

This nginx ticket suggests this is the same issue and this ticket suggests that nginx are not going to go to HTTP/2 on the proxy module soon. There is discussion of the proxy_hide_header Upgrade; but I'm not sure where and how that would break things, does it apply only to the proxy module connection to the backend or also to the frontend client connection? We want NPM to send the upgrade header to HTTP/1.1 clients, but cannot send it if the connection is already HTTP/2.

<!-- gh-comment-id:982530774 --> @the1ts commented on GitHub (Nov 30, 2021): Looks like this is the known issue of HTTP/2 client connection to a proxy, HTTP/1.1 connection from the proxy to an HTTP/2 capable backend. Nginx uses HTTP/1.1 only to do the proxy in the middle, so the backend HTTP/2 service sends the upgrade header advertising its HTTP/2 credentials to allow an upgrade from the proxy if it can. The problem appears to be nginx frontend leaks that header to the client over the already HTTP/2 connection which it shouldn't by the RFC (curl and safari error on this, others ignore). [This nginx ticket](https://trac.nginx.org/nginx/ticket/915) suggests this is the same issue and [this ticket](https://trac.nginx.org/nginx/ticket/923) suggests that nginx are not going to go to HTTP/2 on the proxy module soon. There is discussion of the `proxy_hide_header Upgrade;` but I'm not sure where and how that would break things, does it apply only to the proxy module connection to the backend or also to the frontend client connection? We want NPM to send the upgrade header to HTTP/1.1 clients, but cannot send it if the connection is already HTTP/2.
Author
Owner

@Showfom commented on GitHub (Nov 30, 2021):

The backend server is Apache 2.4 with HTTP/2 enabled

Server version: Apache/2.4.51 (Debian)

Apache2 configuration is from Mozilla:

https://ssl-config.mozilla.org/#server=apache&version=2.4.41&config=intermediate&openssl=1.1.1k&guideline=5.6

I have added proxy_hide_header Upgrade; and the problem was solved.

<!-- gh-comment-id:982539655 --> @Showfom commented on GitHub (Nov 30, 2021): The backend server is Apache 2.4 with HTTP/2 enabled ``` Server version: Apache/2.4.51 (Debian) ``` Apache2 configuration is from Mozilla: https://ssl-config.mozilla.org/#server=apache&version=2.4.41&config=intermediate&openssl=1.1.1k&guideline=5.6 I have added `proxy_hide_header Upgrade;` and the problem was solved.
Author
Owner

@github-actions[bot] commented on GitHub (Mar 1, 2024):

Issue is now considered stale. If you want to keep it open, please comment 👍

<!-- gh-comment-id:1972305611 --> @github-actions[bot] commented on GitHub (Mar 1, 2024): Issue is now considered stale. If you want to keep it open, please comment :+1:
Author
Owner

@davidindra commented on GitHub (Mar 3, 2024):

Hi, this issue should absolutely remain open 👍 as it causes nginx-proxy-manager is unusable with default configuration, when HTTPS is used and iPhone users are expected. Related issues: https://github.com/NginxProxyManager/nginx-proxy-manager/issues/571, https://github.com/NginxProxyManager/nginx-proxy-manager/issues/661.

Workaround is provided (add proxy_hide_header Upgrade; to the Custom Nginx config textbox), but I think this should work out-of-the-box. Should this line be included in the default configuration? I can prepare the Pull Request, if we agree this is the correct fix. What do you think @jc21?

I've investigated it a bit and it seems to cause trouble specifically when the proxy target is supporting HTTP/2 (i.e. Apache2 with HTTP2 enabled), however including the workaround line causes WebSockets stop working.

Thanks in advance and nice day to everyone,
D.

<!-- gh-comment-id:1975334547 --> @davidindra commented on GitHub (Mar 3, 2024): Hi, this issue should absolutely remain open 👍 as it causes nginx-proxy-manager is unusable with default configuration, when HTTPS is used and iPhone users are expected. Related issues: https://github.com/NginxProxyManager/nginx-proxy-manager/issues/571, https://github.com/NginxProxyManager/nginx-proxy-manager/issues/661. Workaround is provided (add `proxy_hide_header Upgrade;` to the Custom Nginx config textbox), **but** I think this should work out-of-the-box. Should this line be included in the default configuration? I can prepare the Pull Request, if we agree this is the correct fix. What do you think @jc21? I've investigated it a bit and it seems to cause trouble specifically when the proxy target is supporting HTTP/2 (i.e. Apache2 with HTTP2 enabled), however including the workaround line causes WebSockets stop working. Thanks in advance and nice day to everyone, D.
Author
Owner

@github-actions[bot] commented on GitHub (Dec 5, 2024):

Issue is now considered stale. If you want to keep it open, please comment 👍

<!-- gh-comment-id:2518932617 --> @github-actions[bot] commented on GitHub (Dec 5, 2024): Issue is now considered stale. If you want to keep it open, please comment :+1:
Author
Owner

@github-actions[bot] commented on GitHub (Dec 6, 2025):

Issue was closed due to inactivity.

<!-- gh-comment-id:3619267911 --> @github-actions[bot] commented on GitHub (Dec 6, 2025): Issue was closed due to inactivity.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/nginx-proxy-manager-NginxProxyManager#1215
No description provided.