Allow SSL certificate on the default site (#422)#5544
Conversation
Adds the ability to select an SSL certificate and force HTTPS for the "Default Site" used when nginx is hit with an unknown host. The default site now listens on 443 with the chosen certificate (custom or Let's Encrypt) when configured, and optionally redirects HTTP to HTTPS when ssl_forced is enabled. The "congratulations" option keeps its existing behavior and ignores SSL fields. - backend/templates/default.conf: add SSL listener, certificates include, and optional force-SSL block - backend/internal/setting.js: hydrate the default-site row with its certificate before rendering nginx config - backend/schema/paths/settings/settingID/put.json: accept certificate_id and ssl_forced in meta - frontend/src/pages/Settings/DefaultSite.tsx: certificate picker and force-SSL toggle in the Default Site settings page - test/cypress/e2e/api/Settings.cy.js: cover new meta fields
Two follow-up fixes on top of the SSL-on-default-site feature (NginxProxyManager#422): 1. Strip cert PEM contents from the nginx render context. The cert row pulled from the DB carries the certificate and certificate_key PEMs in `meta`, and `internalNginx.generateConfig` debug-logs the full render context via JSON.stringify — so under DEBUG the private key ended up in stdout / /data/logs. The nginx template only reads `certificate.provider` and renders disk paths from `certificate_id`, so meta is unnecessary. 2. Regenerate the default-site nginx config when the cert it references is deleted. Other host types are covered by the existing disableInUseHosts / enableInUseHosts flow, which is keyed off cert domain_names and doesn't see the default-site setting. Without this, deleting a cert in use by the default-site leaves a stale ssl_certificate path on disk and the next nginx reload fails.
|
Heads-up on a couple of follow-ups I just pushed ( Hardening fixes in the new commit
Deliberate scope choice: only I left out HSTS / HSTS-subdomains / HTTP/2 / trust-forwarded-proto, even though |
|
Docker Image for build 3 is available on DockerHub: Note Ensure you backup your NPM instance before testing this image! Especially if there are database changes. Warning Changes and additions to DNS Providers require verification by at least 2 members of the community! |
Code ReviewCorrectness Issues1. In await internalNginx.deleteConfig("default");
await internalNginx.generateConfig("default", renderContext);
await internalNginx.reload(); // ← no test() before reloadThe normal update flow does 2. Stale In
The fix should also update the DB row: await settingModel.query().patchAndFetchById("default-site", {
meta: { ...defaultSite.meta, certificate_id: 0, ssl_forced: false },
});3. In Code Quality4. The component wraps a 5. Redundant condition in template {% if ssl_forced == 1 or ssl_forced == true %}
Test CoverageThe new Cypress test only covers Minor Notes
The feature is well-scoped and the main flows are covered. The two blockers are the missing |
1. regenerateDefaultSiteConfig: run nginx -t before reload, fall back
to an empty config on failure.
2. On certificate delete, also clear stale certificate_id / ssl_forced
from default-site meta in the DB.
3. Move _certificates.conf include inside the certificate guard in
default.conf.
4. ForceSSLField: drop redundant <Field> wrapper, use useFormikContext
directly.
5. Simplify ssl_forced check in default.conf to {% if ssl_forced %}.
6. Cypress: cover SSL-enabled PUT and the cert-deletion fallback.
7. Number(...) instead of Number.parseInt(..., 10).
8. Destructure cert.meta out instead of mutating the ORM row.
|
Thanks again for the review @jc21 and all your help. This change should address all the feedback you requested and hope this small improvement helps others wishing to get a certificate assigned to the default site. |
Summary
Implements #422: adds the ability to attach an SSL certificate (and optionally force HTTPS) to the "Default Site" used when nginx is hit with an unknown host. The Default Site previously only listened on port 80, which made it unusable on HSTS domains, behind Cloudflare-with-strict-SSL, or in browsers that auto-upgrade to HTTPS.
404,444,redirect, andhtmlmodes now accept an optional SSL certificate (custom or Let's Encrypt) and a Force-SSL toggle.congratulationspage is unchanged — it ignores SSL fields, matching today's behavior.default_host/site.confadds alisten 443 ssl defaultserver block and an SNI fallback so unknown hosts hit your default page over HTTPS.Changes
backend/templates/default.conf— render an HTTPS listener,_certificates.conf, and an optional force-SSL include based on the certificate/ssl_forcedfields.backend/internal/setting.js— hydrate the default-site row with its certificate before rendering, and reset the SSL fields if the referenced certificate has been deleted.backend/schema/paths/settings/settingID/put.json— acceptcertificate_id(integer, ≥0) andssl_forced(boolean) inmeta.frontend/src/pages/Settings/DefaultSite.tsx— certificate picker and Force-SSL toggle in the Default Site settings page.test/cypress/e2e/api/Settings.cy.js— coverage for the new meta fields.Test plan
npm run lintinbackend/— cleannpm run lintinfrontend/— cleannpm run validate-schemainbackend/— schema validnpm run buildinfrontend/(tsc + vite) — cleanscripts/start-dev; uploaded a self-signed custom cert; configured the default site through the API withmeta.certificate_idandmeta.ssl_forced; verifiednginx -tpasses and the generated/data/nginx/default_host/site.confis correct.ssl_forced=true; serves the configured content (404/redirect/html) whenssl_forced=false.certificate_id=0) regenerates a port-80-only config.certificate_idthat no longer exists falls back to no-SSL (defensive reset insetting.js).