Bubbly 3.0 is a rebuild rather than a refresh. The TLS configuration is re-sourced from the TLSRef configurator, the site templates are renamed and restructured around a default server, certificates are now ECDSA, and the whole repository is tested by CI on every push.
Upgrade Warnings
- Ubuntu 26.04 LTS or Debian 13 only. 3.x needs Nginx 1.25.1+ for the
http2directive and OpenSSL 3.5+ for theX25519MLKEM768group. Ubuntu 24.04, Ubuntu 22.04 and Debian 12 fail both. Stay on 2.2.0 on those. Check withnginx -V. - Site templates are renamed.
bubbly_verify.confis nowbubbly_http.conf,bubbly_live.confis nowbubbly_https.conf. Rename your copies to match, or keep the old names and re-read them against the new ones. - The HTTP site now stays enabled permanently. It serves ACME challenges and redirects everything else, so renewals keep working after a certificate expires. Do not unlink
example.com_http.confafter issuance. - There is a new install step: enable the default server.
sites-available/bubbly_default.confanswers unmatchedHostheaders and connections without SNI, and — because a handshake begins before SNI selects a server — it is wheressl_protocolsandssl_ecdh_curveactually take effect for the whole socket. Runsudo rm -f /etc/nginx/sites-enabled/defaultfirst, or Nginx refuses to start with a duplicate default server. - The TLS profile moved. Protocols, groups and ciphers now live in
directive/bubbly_ssl-profile.conf, included fromserverblocks; session resumption lives inconf.d/bubbly_ssl.conf, which Nginx loads by itself.bubbly_rock-hard-ssl.confis now a thin per-server file. Never include the profile at thehttplevel — Debian and Ubuntu set those directives in their ownnginx.confand a repeat is a fatal "directive is duplicate". - Diffie-Hellman parameters are gone. No DHE ciphersuites remain, so
dhparam3.pemis unused.bubbly_generate-statics.shhas been removed; drop thessl_dhparamline from any site file of your own. - The ACME webroot moved from
/tmp/bubbly-authenticatorto/var/lib/bubbly-authenticator. Any local user or process — a compromised PHP application included — can create a directory in/tmpbefore root does and then answer ACME challenges for any name pointing at the machine. Re-runbubbly_copy-configs.shand reload before the next issuance. - Certificates are now ECDSA P-384 rather than RSA 3072. The next
bubbly_renew-ssl.shrun replaces the key type. Anything pinning the key or expecting RSA needs updating. - Renewal is Certbot's systemd timer, not yours. The script now registers
--deploy-hook "service nginx reload", recorded in/etc/letsencrypt/renewal/. Remove any Bubbly renewal cron job;crontab.confhas been deleted. - Session ticket keys ship commented out. Nginx 1.23.2+ generates and rotates its own in the shared cache.
bubbly_generate-tickets.shis now optional, for several instances behind a load balancer only, and requires uncommentingssl_session_ticket_keyinconf.d/bubbly_ssl.conf. - OCSP stapling ships off, in its own
directive/bubbly_ocsp-stapling.conf. Let's Encrypt stopped issuing an OCSP responder URL on 2025-05-07 and shut the responders down on 2025-08-06, sossl_stapling on;only logs a warning. Still worth enabling behind a CA that publishes one. Expect-CTis gone, deprecated and ignored by every browser.X-XSS-Protectionis now0, the XSS Auditor having been removed as itself exploitable. The unprefixedFrame-Options,Content-Type-Options,XSS-ProtectionandX-UA-Compatibleheaders never existed and have been dropped, as hasAccess-Control-Allow-Origin: origin, which is not a valid value — same-origin is now expressed by sending no header.server_tokens off;is now active rather than commented.- HSTS
max-ageis now 63072000 (two years), still withoutincludeSubDomains. - Rate limit zones have been renamed and trimmed in
conf.d/bubbly_limits.conf.bubbly_limits_20.confandbubbly_limits_server_2k.confare gone. Update any include of your own. - Nginx Amplify and NixStats stub-status configs have been removed.
Security
- Re-source the TLS profile from the TLSRef configurator, Guideline v6.0, for Nginx 1.28 with OpenSSL 3.5.
- Add
X25519MLKEM768as the preferred key exchange group, for post-quantum forward secrecy. - Issue ECDSA
secp384r1certificates instead of RSA 3072. - Move the ACME webroot out of
/tmp, where it could be pre-created by any local user. - Add
Cross-Origin-Embedder-Policy,Cross-Origin-Opener-PolicyandCross-Origin-Resource-Policy, each with three documented options. - Add
Permissions-Policy, denying 21 features by default. - Raise HSTS
max-agefrom one year to two. - Enable
server_tokens off;by default. - Write ticket keys
600, and rotate rather than overwrite, so issued tickets keep working. - Add a default server, so an unknown
Hostor a connection without SNI is refused rather than being served whichever site sorts first.
Bugfix
bubbly_copy-configs.shresolves its own directory, so it works from any working directory rather than only from~.- The Content Security Policy is built across several
setdirectives and emitted as one header. The previous multi-line quoted value produced a header containing literal newlines, which is invalid HTTP — and it was sent under a header name with a trailing colon. expiresno longer applies to every vhost on the machine.- Rate limits keyed on
$server_name, not$host, so a catch-all server cannot be used to mint unlimited keys and fill the zone. - Fix
"listen ... http2" directive is deprecatedby usinghttp2 on;.
Feature
sites-available/bubbly_default.conf, the catch-all default server, enabled once per machine.- Brotli, opt-in alongside gzip, in
directive/bubbly_brotli.conf. Off by default because withoutlibnginx-mod-http-brotli-filterinstalled,brotli on;stops Nginx loading any config at all. directive/bubbly_real-ip.conf, for deployments behind a proxy or CDN, where otherwise every log line and every rate limit sees only the proxy.- One PHP upstream per supported branch in
conf.d/php_sockets.conf, selectable per site, so several PHP versions can be served from one machine. - Per-site rate limit zones keyed on
$server_name$binary_remote_addralongside the machine-wide ones, so one site cannot spend another's budget. - A landing page, published to GitHub Pages.
Improvement
- Rewrite the README around the six-step install, with a Requirements table, a Configuration table giving advice per change, and sections on proxies, rate limiting and PHP versions.
- Offer the unreferenced includes commented out at the bottom of
bubbly_https.conf, so most configuration is uncommenting a line and reloading. - Trim the rate limit zones to one of each kind, sized by how long a state lives rather than by traffic volume, and name them for what they key on.
- Note throughout that limits never apply to a
returnresponse, and that HTTP/2 collapses a page load to one connection. - Move the
expiresmap toconf.d/expires-map.confand modernise its types. - Refresh
mime.types. - Document every
[OPTION],[DEFAULT]and[WARNING]in prose rather than in shorthand, and delete the comments that had gone stale.
Supporting
- Add
.github/workflows/nginx.yml:nginx -tand a live probe of the templates onubuntu:26.04anddebian:13, covering SNI certificate selection, the default server's refusals, ACME passthrough, per-site PHP upstream selection, rate limit isolation, Brotli and gzip negotiation, and the three per-vhost TLS behaviours; plus drift checks on duplicated values and a check that the default PHP socket matches the platform's real pool. - Add CI for Markdown, shell, JSON, CSS, JS and HTML, and a security workflow.
- Add Dependabot.
- Replace Code Climate and Codacy with qlty.
- Update the Qualys SSL Labs and Security Headers screenshots.
- Adopt the organisation's shared Code of Conduct, Security Policy and issue templates from eustasy/.github.