NGINX and bad gateway

Your question

When I try to access the WebUI, I get a 502 Bad Gateway error. In NGINX logs I see that there is an error stating

*447 upstream prematurely closed connection while reading response header from upstream,

My NGINX server configuration is.

server {
	server_name mail.domain.dk;
	listen 443 ssl; # managed by Certbot
	ssl_certificate /etc/letsencrypt/live/mail.domain.dk/fullchain.pem; # managed by Certbot
	ssl_certificate_key /etc/letsencrypt/live/mail.domain.dk/privkey.pem; # managed by Certbot
	include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
	ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
	error_log /var/log/nginx/mail.domain.dk.error.log;
	
        location / {
    	    proxy_pass http://127.0.0.1:8081;
    	    proxy_set_header Host $host;
    	    proxy_set_header X-Real-IP $remote_addr;
    	    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    	    proxy_set_header X-Forwarded-Proto $scheme;
	}
    }

There are a few likely causes for the 502 here. The most important one is that Stalwart’s HTTP listener defaults to port 8080, not 8081, so your proxy_pass http://127.0.0.1:8081 may simply be hitting a closed port. Also, if Stalwart’s HTTP listener has TLS enabled (i.e. it is listening on HTTPS rather than plain HTTP), NGINX’s plain http:// upstream will get a TLS handshake where it expects plain HTTP, causing the “upstream prematurely closed connection” error.

Here are the things to check and fix:

  1. Confirm the correct port. Stalwart’s default plain-HTTP listener is on port 8080, not 8081. Verify the actual port your Stalwart instance uses (check your NetworkListener settings in the WebUI or via stalwart-cli), then update proxy_pass accordingly:

    proxy_pass http://127.0.0.1:8080;
    
  2. Make sure you are pointing at the plain HTTP listener, not the HTTPS one. The docs recommend the HTTP-upstream pattern for simplicity: NGINX terminates TLS for clients and forwards plain HTTP to Stalwart’s HTTP listener on port 8080. If your Stalwart listener has TLS/implicit TLS enabled, you must either switch to an HTTP listener for the backend, or configure NGINX with https:// upstream and certificate verification.

  3. Set STALWART_PUBLIC_URL. Because NGINX is terminating TLS but Stalwart sees plain HTTP internally, Stalwart needs to know the public URL so that its OAuth/OIDC/JMAP discovery documents emit correct https://... absolute URLs. Set the STALWART_PUBLIC_URL environment variable to https://mail.domain.dk.

  4. Auto-ban risk. Without the Proxy Protocol or X-Forwarded-For trust configured, Stalwart may auto-ban the loopback/proxy address after a few failed login attempts. The docs recommend configuring the X-Forwarded-For trust (useXForwarded on the HTTP listener) or the Proxy Protocol, and adding the proxy’s IP to proxyTrustedNetworks in SystemSettings.

The full NGINX guide (including a TCP-passthrough example using stream {}) is at: NGINX | Stalwart

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong.

How can I reset or disable autoban without access to the WebUI?

Using the Command Line Interface

I get

stalwart-cli get Security
error: server returned HTTP 502

Did you at least take a look at the documentation to understand how to use the command line?

I did, yes, thank you. I am still getting 502 bad gateway. And I can’t really fix a 502 error with a tool that depends on not getting a… 502 error.

So I reinstalled my Stalwart server and restored its contents. I then enabled getting client ID from HTTP headers using the WebUI… Then a few hours later I get 502 bad gateway again… :cry:

The NGINX error you gave combined with saying you come back later and get a 502 sounds like a connection reuse issue to me.

You could try disabling connection reuse in Nginx as a test.

keepalive_timeout 0;

Hi, and thank you for the suggestion :slight_smile: I set the option but there was no change, unfortunately.

Updating from 0.16.10 to 0.16.11 did not resolve things either.

I added the IP address to ALLOWED IPs. In my case, it was 172.16.7.1, some unknown local address that differs from the Nginx address.