Nginx Proxy Pass Works on HTTP but Fails on HTTPS Until proxy_set_header Host Added

We recently faced a strange behavior with one of our Nginx reverse-proxy configurations. The following proxy block was working perfectly for a long time:

location /mobile/Sub/ {
    proxy_pass https://www.test.com/subscription-test/upgrade-user-name/;
}

Suddenly, this stopped working only when we accessed it using HTTPS and the hostname.
When we accessed the same location using HTTP or the server’s local IP, it worked normally and returned the expected response.

However, when reaching it through HTTPS + hostname, the backend returned 404.

To fix it temporarily, we added the following header:

proxy_set_header Host www.test.com;

After adding this header, the proxy started working again — even with Cloudflare disabled.
The strange part is that the configuration worked fine before without this header.


What I Want to Understand

We are trying to identify the actual root cause of why HTTPS started returning 404 while HTTP/local IP worked.

Some details:

  • The webserver sits behind Cloudflare, but we also tested with Cloudflare completely disabled and still saw the same 404 issue.

  • Only after adding
    proxy_set_header Host www.test.com
    the backend responded correctly again.

  • We want to know why this behavior suddenly changed, and whether there could be any dependency or indirect impact from Cloudflare or backend routing.

issue solved after config like this –>

location /mobile/Sub/ {
    proxy_pass https://www.test.com/subscription-test/upgrade-user-name/;
    proxy_set_header Host www.test.com
}

Need to find the root cause for this .. Kindly help me with this if you have any idea or a technical explanation.

1 Like

Hello,
As you noticed, the 404 error comes from the backend. Can you check the recent changes of the backend configuration? It seems that before, such requests were handled by the proper virtual server at the backend.
NGINX proxies the client request as it is. So the “Host“ header should contain the frontend hostname. If it is different from the backend one, it can cause this error.
I hope this helps to find the root cause.

1 Like

Hi Jimf5,

Thank you very much for your response and for sharing your advice.

As mentioned, this configuration was working previously without explicitly setting the Host header and without any changes on the backend. Additionally, there are other client environments configured with the same proxy setup and backend URL, and they are working without any issues.

This is why the behavior is concerning and why we are unable to clearly identify the exact root cause. The only notable difference between the working and non-working client environments is that the affected environment is operating behind Cloudflare.

Could Cloudflare introduce any SNI validation issues or other abnormal behavior that might impact the Host header handling or backend virtual host selection? Your insights or recommendations on this would be greatly appreciated.

I hope this additional context helps in further narrowing down the root cause.
Thank you again for your support.

HI @Menuka_DP ,
Cloudflare does some validation for SNI and the “Host” header. Can you check “Origin Rules settings“?

Hi @Menuka_DP ,

The fact that something stopped working ‘by itself’ is practically impossible, most likely there were changes that you didn’t pay attention to. + there were no changes in nginx either that could have caused this behavior.

I would recommend carefully reviewing the docs for

because some nuances may not be obvious.

What’s important to understand:

  1. In ngx_http_proxy_module, by default nginx does NOT forward the client’s original Host header. Instead, it overrides host h with $proxy_host (the host from proxy_pass) and sets Connection: close. This is explicitly in the docs.
  2. proxy_set_header directives are inherited from the upper level only if there are no proxy_set_header directives defined at the current level.

This is the key to ‘why it suddenly started working alter adding a single line’. That line may have simply overridden a previously inherited Host value (for example, one defined somethere in an included fle).

If http2 on; is defined in your VS, the following scenario is also possible:
the real host is present in the :authority, while the Host header may be null = > $http_host becomes empty > nginx sends an empty or incorrect host header to the upstream (or none at all, depending on conf) > the upstream selects the default vhost and returns 404.