Skip to content

fix(smtp): preserve <br> line breaks in HTML emails#1620

Merged
binwiederhier merged 1 commit into
binwiederhier:mainfrom
uzkikh:fix/smtp-html-br-linebreaks
Feb 21, 2026
Merged

fix(smtp): preserve <br> line breaks in HTML emails#1620
binwiederhier merged 1 commit into
binwiederhier:mainfrom
uzkikh:fix/smtp-html-br-linebreaks

Conversation

@uzkikh
Copy link
Copy Markdown

@uzkikh uzkikh commented Feb 21, 2026

Context

HTML-only emails (e.g. from Synology DSM 7.3 Task Scheduler) use <br> tags for line breaks. The existing implementation passed the raw HTML body to bluemonday with AddSpaceWhenStrippingTag(true), which replaces every tag including <br> with a space. This caused all notification content to appear on a single unreadable line.

The ntfy notification before the fix

Task Scheduler has completed a scheduled task. Task: daily-backup Start time: Mon, 01 Jan 2026 02:00:00 +0000 Current status: 0 (Normal) From MyNAS

The ntfy notification after the fix

Task Scheduler has completed a scheduled task.

Task: daily-backup
Start time: Mon, 01 Jan 2026 02:00:00 +0000
Current status: 0 (Normal)

From MyNAS

Steps to reproduce

# Start ntfy with SMTP enabled
ntfy serve --smtp-server-listen :25 --smtp-server-domain example.com

# Send an HTML-only email with <br> line breaks
swaks \
  --server 127.0.0.1 --port 25 \
  --from sender@example.com \
  --to ntfy-mytopic@example.com \
  --header "Content-Type: text/html; charset=utf-8" \
  --body "Line one.<BR>Line two.<BR><BR>Line three."

# Check the notification — all text appears on one line:
#    "Line one. Line two.  Line three."

Root cause

The readHTMLMailBody() function used bluemonday.StrictPolicy().AddSpaceWhenStrippingTag(true) which replaces all tags including <br> with spaces, discarding line break semantics.

Fix

Convert <br> tags to newlines before passing to bluemonday. The regex covers all variants: <br>, <BR>, <br/>, <BR />.

Tests

  • Updated TestSmtpBackend_HTMLOnly_FromDiskStation and TestSmtpBackend_HTMLEmail to reflect correct output
  • Added TestSmtpBackend_HTMLEmail_BrTagsPreserved covering the Synology Task Scheduler email format

Related to #690

HTML-only emails (e.g. from Synology DSM 7.3 Task Scheduler) use <br>
tags for line breaks. The existing implementation passed the raw HTML
body to bluemonday with AddSpaceWhenStrippingTag(true), which replaced
every tag including <br> with a space, causing all content to appear
on a single unreadable line.

Fix: convert <br> tags to newlines before stripping HTML, so line break
semantics are preserved in the notification body.

Resolves the gap noted in binwiederhier#690 ("very sub-par" HTML support).
@binwiederhier
Copy link
Copy Markdown
Owner

This is a reasonable fix. Thank you @uzkikh and Cursor/Claude :-)

@binwiederhier binwiederhier merged commit d19b825 into binwiederhier:main Feb 21, 2026
alexlebens pushed a commit to alexlebens/infrastructure that referenced this pull request Mar 8, 2026
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [binwiederhier/ntfy](https://github.com/binwiederhier/ntfy) | minor | `2.17.0` → `2.18.0` |
| [binwiederhier/ntfy](https://ntfy.sh/) ([source](https://github.com/binwiederhier/ntfy)) | minor | `v2.17.0` → `v2.18.0` |

---

### Release Notes

<details>
<summary>binwiederhier/ntfy (binwiederhier/ntfy)</summary>

### [`v2.18.0`](https://github.com/binwiederhier/ntfy/releases/tag/v2.18.0)

[Compare Source](binwiederhier/ntfy@v2.17.0...v2.18.0)

This is the biggest release I've ever done on the server. It's 14,997 added lines of code, and 10,202 lines removed, all from one [pull request](binwiederhier/ntfy#1619) that adds [PostgreSQL support](https://docs.ntfy.sh/config/#postgresql-experimental).

The code was written by Cursor and Claude, but reviewed and heavily tested over 2-3 weeks by me. I created comparison documents, went through all queries multiple times and reviewed the logic over and over again. I also did load tests and manual regression tests, which took lots of evenings.

I'll not instantly switch ntfy.sh over. Instead, I'm kindly asking the community to test the Postgres support and report back to me if things are working (or not working). There is a [one-off migration tool](https://github.com/binwiederhier/ntfy/tree/main/tools/pgimport) (entirely written by AI) that you can use to migrate.

**Features:**

- Add experimental [PostgreSQL support](https://docs.ntfy.sh/config/#postgresql-experimental) as an alternative database backend (message cache, user manager, web push subscriptions) via `database-url` config option ([#&#8203;1114](https://github.com/binwiederhier/ntfy/issues/1114)/[#&#8203;1619](https://github.com/binwiederhier/ntfy/pull/1619), thanks to [@&#8203;brettinternet](https://github.com/brettinternet) for reporting)

**Bug fixes + maintenance:**

- Preserve `<br>` line breaks in HTML-only emails received via SMTP ([#&#8203;690](binwiederhier/ntfy#690), [#&#8203;1620](binwiederhier/ntfy#1620), thanks to [@&#8203;uzkikh](https://github.com/uzkikh) for the fix and to [@&#8203;teastrainer](https://github.com/teastrainer) for reporting)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My41OS4yIiwidXBkYXRlZEluVmVyIjoiNDMuNTkuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW1hZ2UiXX0=-->

Reviewed-on: https://gitea.alexlebens.dev/alexlebens/infrastructure/pulls/4523
Co-authored-by: Renovate Bot <renovate-bot@alexlebens.net>
Co-committed-by: Renovate Bot <renovate-bot@alexlebens.net>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants