NginxProxyManager / NginxProxyManager/nginx-proxy-manager
Force SSL redirect is applied before Access List, leaking host existence via 301 and aiding host enumeration
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 34.2k
- Forks
- 3.9k
- Avg merge
- 21h 12m
- Merged PRs (30d)
- 20
Description
Checklist
- Have you pulled and found the error with
jc21/nginx-proxy-manager:latestdocker image?- Yes (tested with jc21/nginx-proxy-manager:2.13.6)
- Are you sure you're not using someone else's docker image?
- Yes
- Have you searched for similar issues (both open and closed)?
- Yes (#4984 and others)
Describe the bug
This is a real and reproducible security issue. When both "Force SSL" and an Access List are active on a Proxy Host, HTTP requests from unauthorized IPs receive a 301 Moved Permanently to HTTPS instead of being blocked immediately.
This means an attacker can scan your IP and brute-force the Host header; if the 301 response is returned, it confirms the host (subdomain) exists, even if the attacker can't reach the service itself. Thus, sensitive/private hosts are discoverable, leaking internal subdomain names intended to be zero-trust/hidden.
Only after the browser/client follows the redirect to HTTPS does the Access List return a 403 Forbidden (if the IP is not allowed).
Nginx Proxy Manager Version
jc21/nginx-proxy-manager:2.13.6
To Reproduce
Steps to reproduce the behavior:
- Create a Proxy Host and assign an Access List that only allows certain IPs (deny all others)
- Enable "Force SSL"
- From an unauthorized IP (not in the allow list), run:
curl -H "Host: private.internal.net" http://SERVER_IP - Observe: Gets
301 Moved Permanently - Follow the redirect manually:
curl -k -H "Host: private.internal.net" https://SERVER_IP - Observe: Gets
403 Forbidden(as expected)
Expected behavior
HTTP requests from unauthorized IPs should be blocked (403 Forbidden or 444) with no redirect. Only allowed IPs should get redirected to HTTPS. Host existence must not be leaked by a different status code.
Screenshots
Not a UI bug, but here are terminal outputs:
$ curl -H "Host: private.internal.net" http://SERVER_IP
<html>
<head><title>301 Moved Permanently</title></head>
...
After redirect:
$ curl -k -H "Host: private.internal.net" https://SERVER_IP
<html>
<head><title>403 Forbidden</title></head>
...
Operating System
Docker on Ubuntu/Debian, but should reproduce on all OS/container setups.
Additional context
How we verified this:
- Inspected generated nginx config with:
docker exec nginx-proxy-manager grep -rl "yourhost" /data/nginx/proxy_host/ docker exec nginx-proxy-manager cat /data/nginx/proxy_host/X.conf - Noticed
include conf.d/include/force-ssl.conf;is outside thelocation /with access rules, so HTTPS redirect happens before Access List is checked:server { ... # Force SSL include conf.d/include/force-ssl.conf; ... location / { # Access Rules allow 100.64.0.0/10; deny all; satisfy all; ... } } - This allows attackers to enumerate valid hosts (internal subdomains) by Host header brute force, learning subdomain names that are not meant to be public.
- If "Force SSL" is off, the Access List blocks requests immediately as expected.
- Related issue: #4984 (basic-auth scenario, not IP ACL).
Proposed fix:
Check Access List before Force SSL redirect, so redirects only occur for allowed clients. Unauthorized IPs should receive a block and no redirect, preventing host enumeration.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the behavior with the provided curl commands, then inspect the generated configuration under /data/nginx/proxy_host/ and the included conf.d/include/force-ssl.conf. Trace how the Force SSL include and Access List rules are ordered in the generated server configuration. Done means unauthorized HTTP requests are blocked without a redirect, while allowed clients can still reach HTTPS through the redirect.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, nginx
- Domain
- infrastructure, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100