Skip to content

fix: disable hosts and prevent nginx crash when certificate is deleted - #5732

Open
mrbaloghakos wants to merge 2 commits into
NginxProxyManager:developfrom
mrbaloghakos:fix/cert-delete-nginx-offline
Open

mrbaloghakos wants to merge 2 commits into
NginxProxyManager:developfrom
mrbaloghakos:fix/cert-delete-nginx-offline

Conversation

@mrbaloghakos

@mrbaloghakos mrbaloghakos commented Jul 22, 2026

Copy link
Copy Markdown

When a certificate is deleted in NPM, the nginx config files for proxy hosts using that certificate were left referencing cert files that no longer exist on disk. This caused nginx to refuse to start on the next restart with:

  nginx: [emerg] cannot load certificate ".../fullchain.pem": BIO_new_file()
  failed (SSL: error:80000002:... No such file or directory)

Two complementary fixes:

  1. certificate.delete() now finds all enabled hosts that reference the deleted certificate, removes their nginx config files, sets meta.nginx_online=false with nginx_err='Certificate was deleted', and reloads nginx. Hosts are taken fully offline (rather than silently downgraded to HTTP) so the user sees a clear signal in the UI.

  2. A new prepare script (55-nginx-validate.sh) runs before nginx starts and scans all host config files for ssl_certificate directives. Any config that references a cert file missing from disk is renamed to .conf.err so nginx never attempts to load it. This acts as a safety net for scenarios where cert files go missing without NPM knowing (volume recreation, manual deletion, etc.).

Also adds a Cypress test that verifies a proxy host is taken offline when its certificate is deleted.

ℹ️ Vibecoded using Claude Sonnet 4.6

Fixes the following issues:

Why

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Code refactoring
  • API changes
  • Performance improvement
  • Test addition or update

AI Usage

  • AI was used to write this (Claude Sonnet 4.6)
  • AI was used to review this

…eleted

When a certificate is deleted in NPM, the nginx config files for proxy hosts
using that certificate were left referencing cert files that no longer exist on
disk. This caused nginx to refuse to start on the next restart with:

  nginx: [emerg] cannot load certificate ".../fullchain.pem": BIO_new_file()
  failed (SSL: error:80000002:... No such file or directory)

Two complementary fixes:

1. certificate.delete() now finds all enabled hosts that reference the deleted
   certificate, removes their nginx config files, sets meta.nginx_online=false
   with nginx_err='Certificate was deleted', and reloads nginx. Hosts are taken
   fully offline (rather than silently downgraded to HTTP) so the user sees a
   clear signal in the UI.

2. A new prepare script (55-nginx-validate.sh) runs before nginx starts and
   scans all host config files for ssl_certificate directives. Any config that
   references a cert file missing from disk is renamed to .conf.err so nginx
   never attempts to load it. This acts as a safety net for scenarios where
   cert files go missing without NPM knowing (volume recreation, manual
   deletion, etc.).

Also adds a Cypress test that verifies a proxy host is taken offline when its
certificate is deleted.
The explicit DELETE /api/nginx/proxy-hosts cleanup at the end of the
'Deleting a certificate takes dependent proxy hosts offline' test was
causing a 502 in the MySQL CI environment. The double back-to-back
nginx reload (cert deletion + proxy host deletion) caused the backend
to become temporarily unreachable, failing the test.

The cleanup is unnecessary: every test spec's before() already calls
cy.resetUsers() which wipes all data before the next spec runs.
@nginxproxymanagerci

Copy link
Copy Markdown

Docker Image for build 3 is available on DockerHub:

nginxproxymanager/nginx-proxy-manager-dev:pr-5732

Note

Ensure you backup your NPM instance before testing this image! Especially if there are database changes.
This is a different docker image namespace than the official image.

Warning

Changes and additions to DNS Providers require verification by at least 2 members of the community!

@jc21

jc21 commented Sep 23, 2026

Copy link
Copy Markdown
Member

Thanks for taking the time to fix this. I've found some issues, some for consideration, some need to be resolved.

Consideration:

  1. backend/internal/certificate.js:424, files deleted too early: the Let's Encrypt revoke deletes the cert files before the configs of hosts using that cert are removed. The revoke is a network call that can take several seconds, and during it nginx -t fails on the missing files. If another host is saved in that window, it gets marked offline with an unrelated error, or the reload is rejected. Removing the configs before revoking fixes this.
  2. backend/internal/certificate.js:468, false delete error: the new internalNginx.reload() runs nginx -t after the cert is already marked deleted, audited and revoked. If that test fails for any reason (for example, some unrelated host's config is broken), the DELETE returns a 500 even though the delete fully happened.
  3. backend/internal/certificate.js:430, hosts come back over plain HTTP: affected hosts stay enabled and keep pointing at the deleted cert. Because the cert lookup skips deleted certs, any later config rebuild writes a config with no SSL lines. Updating an attached access list, or saving the host itself, brings it back over plain HTTP, which is the downgrade the PR says it avoids. With an access-list update, the UI also still shows the host as offline with "Certificate was deleted".

Require fixing:

  1. docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/55-nginx-validate.sh:28, container can fail to start: the new script renames config files with mv but lacks the is_mounted check that 50-ipv6.sh has. If a user has bind-mounted a single .conf file and its cert is missing, mv fails, set -e stops the prepare stage, and the container won't start.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants