FAQ: common Nginx & Apache errors
Short, direct answers to the errors people hit most when setting up a reverse proxy, HTTPS or redirects. Each one links to the generator option that fixes it.
Last updated 27 October 2026 · New to ProxyForge? Start with the how-to guide.
- Why does
nginx -tsayunknown directive "http2"? - Why does my Let's Encrypt renewal fail behind an HTTPS redirect?
- Why can't my
.htaccessuseProxyPass? - Why do uploads fail with
413 Request Entity Too Large? - Why won't Apache start after I added
SSLStaplingCache? - Why do I get
ERR_TOO_MANY_REDIRECTSbehind Cloudflare? nginx -tpasses, but nothing answers on port 443. Why?- Why do I get
502 Bad Gatewayfrom Nginx or Apache? - Why does Nginx say
bind() to 0.0.0.0:80 failed (98: Address already in use)? nginx -s reloadsaid OK, but my change didn't apply. Why?
Why does nginx -t say unknown directive "http2"?
Your Nginx is older than 1.25.1. The separate http2 on; line only exists from 1.25.1 onward; older versions (for example 1.24 on Ubuntu 24.04, and the Ubuntu 22.04 and Debian 11 packages) turn HTTP/2 on in the listen line instead:
listen 443 ssl http2;Check your version with nginx -v. If it's below 1.25.1, tick Older Nginx (before 1.25.1) in the Config Generator or SSL Generator.
Why does my Let's Encrypt renewal fail behind an HTTPS redirect?
With certbot --webroot, Let's Encrypt checks a file at http://your-domain/.well-known/acme-challenge/. If port 80 redirects everything to HTTPS, that check is redirected too, often into your app, and renewal fails about 60 days after setup with no warning. The port 80 block needs an exception:
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 301 https://$host$request_uri;
}The return must sit inside location /: a return placed directly in the server block runs before Nginx looks at any location. ProxyForge's HTTPS and redirect output already includes this. Test it now with sudo certbot renew --dry-run.
Why can't my .htaccess use ProxyPass?
Apache only allows ProxyPass, ProxyPassReverse and ProxyPreserveHost in the main server config or a virtual host, never in .htaccess, even with AllowOverride All. In .htaccess the only way to proxy is mod_rewrite's [P] flag, which needs mod_proxy, mod_proxy_http and mod_rewrite enabled. That's what the Config Generator outputs in Apache mode. If you need the original Host header passed through or backend redirects rewritten, move the proxy rules into the virtual host.
Why do uploads fail with 413 Request Entity Too Large?
Nginx rejects request bodies over 1 MB by default. Raise the limit with client_max_body_size, for example client_max_body_size 50m;, using the Max upload size field in the Config Generator, then test and reload.
Why won't Apache start after I added SSLStaplingCache?
SSLStaplingCache (like SSLSessionCache) is only allowed in the main server config, outside any <VirtualHost>. Put it inside a virtual host and apachectl configtest fails with "SSLStaplingCache cannot occur within <VirtualHost> section". The SSL Generator splits its Apache output into the server-wide lines and the vhost lines for this reason.
Why do I get ERR_TOO_MANY_REDIRECTS behind Cloudflare?
Cloudflare's Flexible SSL mode connects to your server over plain HTTP, so your HTTP-to-HTTPS redirect sends every request back to HTTPS forever. Set Cloudflare's SSL/TLS mode to Full (strict), which needs a valid certificate on your server. Tick Behind Cloudflare in the Config Generator to also restore real visitor IPs.
nginx -t passes, but nothing answers on port 443. Why?
Usually the site file is empty or not enabled. A paste into nano can silently fail and save an empty file, which still passes the test. Check that the file has content with grep -c . /etc/nginx/sites-available/your-site, that it's linked into /etc/nginx/sites-enabled/, and that Nginx is listening with sudo ss -tlnp | grep :443. Then run sudo nginx -t && sudo systemctl reload nginx again.
Why do I get 502 Bad Gateway from Nginx or Apache?
The proxy is working, but it can't reach your app. Check the error log for the exact reason: sudo tail -n 20 /var/log/nginx/error.log (or /var/log/apache2/error.log, /var/log/httpd/error_log on RHEL).
connect() failed (111: Connection refused) while connecting to upstream(Apache:AH00957 ... failed): nothing is listening on the backend address. Check the app is running (systemctl status your-appordocker ps) and listening on the port you set:sudo ss -tlnp | grep :3000. A typo in the port is common.connect() to 127.0.0.1:3000 failed (13: Permission denied): on RHEL, Rocky or Alma, SELinux blocks the web server from making network connections. Allow it withsudo setsebool -P httpd_can_network_connect 1.- The app is in Docker: publish its port to the host (
-p 127.0.0.1:3000:3000), and make sure the app inside the container listens on0.0.0.0, not only onlocalhost.
Once the app answers locally (curl -I http://127.0.0.1:3000), the 502 goes away. No reload needed. The backend address comes from the Config Generator's Backend field.
Why does Nginx say bind() to 0.0.0.0:80 failed (98: Address already in use)?
Another program already owns port 80 (or 443), so Nginx can't start. Apache shows the same problem as (98)Address already in use: AH00072: make_sock: could not bind to address [::]:80.
Don't stop processes blindly. First see what's actually listening: sudo ss -tlnp | grep -E ':(80|443) '. The usual answers:
- Apache and Nginx both installed. Some packages pull in Apache, and it starts on port 80. Pick the one you want. If it's Nginx:
sudo systemctl disable --now apache2(httpdon RHEL). - An old Nginx process left over from a failed restart.
sudo systemctl restart nginxusually clears it. Check with thesscommand again. - Certbot in standalone mode needs port 80 free while it runs. Use the webroot or the Nginx/Apache plugin instead, as the SSL Generator offers.
- Docker or another app publishing port 80. Move it to another port and proxy to it instead.
Then sudo nginx -t && sudo systemctl start nginx.
nginx -s reload said OK, but my change didn't apply. Why?
A reload only sends a signal to Nginx, so the command reports success even if the new config is rejected. Nginx then quietly keeps running the old config and writes the reason to the error log. One change that always does this: editing the key of an existing limit_req_zone (or limit_conn_zone). Tested on Nginx 1.27: nginx -t passes, the reload exits with 0, and the old key stays in use. The log shows:
[emerg] limit_req "one" uses the "$server_name" key while previously it used the "$binary_remote_addr" key
Two ways to apply it:
- Restart instead of reload:
sudo nginx -t && sudo systemctl restart nginx. Open connections are dropped for a moment. - Give the zone a new name (e.g.
zone=one_v2) and update everylimit_req zone=line that uses it. A normal reload then works.
Good habit after any reload: check it took with sudo tail -n 5 /var/log/nginx/error.log (or journalctl -u nginx -n 20).
Hit an error that isn't here?
Email admin@proxyforge.tech with the exact error message, your Nginx or Apache version and distro. Real errors get added to this page.
How to use ProxyForge · What's new · About · Featured On · Privacy