diff --git a/docs/publish.md b/docs/publish.md
index a068b913..460fcd35 100644
--- a/docs/publish.md
+++ b/docs/publish.md
@@ -2941,11 +2941,17 @@ format is:
 ntfy-$topic@ntfy.sh
 ```
 
-If [access control](config.md#access-control) is enabled, and the target topic does not support anonymous writes, e-mail publishing won't work without providing an authorized access token. That will change the format of the e-mail's recipient address to
+If [access control](config.md#access-control) is enabled, and the target topic does not support anonymous writes, e-mail publishing won't work
+without providing an authorized access token or using SMTP AUTH PLAIN. 
+
+If you use [access tokens](#access-tokens), that will change the format of the e-mail's recipient address to
 ```
 ntfy-$topic+$token@ntfy.sh
 ```
 
+To use [username/password](https://docs.ntfy.sh/publish/#username-password), you can use SMTP PLAIN auth when authenticating
+to the ntfy server.
+
 As of today, e-mail publishing only supports adding a [message title](#message-title) (the e-mail subject). Tags, priority,
 delay and other features are not supported (yet). Here's an example that will publish a message with the 
 title `You've Got Mail` to topic `sometopic` (see [ntfy.sh/sometopic](https://ntfy.sh/sometopic)):
diff --git a/docs/releases.md b/docs/releases.md
index dc674a11..2bf29272 100644
--- a/docs/releases.md
+++ b/docs/releases.md
@@ -1373,6 +1373,23 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
 
 ## Not released yet
 
+### ntfy server v2.12.0 (UNRELEASED)
+
+**Features:**
+
+* Add username/password auth to email publishing ([#1164](https://github.com/binwiederhier/ntfy/pull/1164), thanks to [@bishtawi](https://github.com/bishtawi))
+
+**Bug fixes + maintenance:**
+
+* Add `Date` header to outgoing emails to avoid rejection ([#1141](https://github.com/binwiederhier/ntfy/pull/1141), thanks to [pcouy](https://github.com/pcouy))
+
+**Documentation:**
+
+* Various docs updates ([](https://github.com/binwiederhier/ntfy/pull/1161), thanks to [@OneWeekNotice](https://github.com/OneWeekNotice))
+* Typo in config docs ([#1177](https://github.com/binwiederhier/ntfy/pull/1177), thanks to [@hoho4190](https://github.com/hoho4190))
+* Typo in CLI docs ([#1172](https://github.com/binwiederhier/ntfy/pull/1172), thanks to [@anirvan](https://github.com/anirvan))
+* Correction about MacroDroid ([](https://github.com/binwiederhier/ntfy/pull/1137), thanks to [@ShlomoCode](https://github.com/ShlomoCode))
+
 ### ntfy Android app v1.16.1 (UNRELEASED)
 
 **Features:**
diff --git a/server/server_test.go b/server/server_test.go
index ef9157cb..75379f8f 100644
--- a/server/server_test.go
+++ b/server/server_test.go
@@ -1277,6 +1277,7 @@ func TestServer_PublishEmailNoMailer_Fail(t *testing.T) {
 func TestServer_PublishAndExpungeTopicAfter16Hours(t *testing.T) {
 	t.Parallel()
 	s := newTestServer(t, newTestConfig(t))
+	defer s.messageCache.Close()
 
 	subFn := func(v *visitor, msg *message) error {
 		return nil
@@ -1288,13 +1289,22 @@ func TestServer_PublishAndExpungeTopicAfter16Hours(t *testing.T) {
 	})
 	require.Equal(t, 200, response.Code)
 	waitFor(t, func() bool {
+		s.mu.Lock()
+		tp, exists := s.topics["mytopic"]
+		s.mu.Unlock()
+		if !exists {
+			return false
+		}
 		// .lastAccess set in t.Publish() -> t.Keepalive() in Goroutine
-		s.topics["mytopic"].mu.RLock()
-		defer s.topics["mytopic"].mu.RUnlock()
-		return s.topics["mytopic"].lastAccess.Unix() >= time.Now().Unix()-2 &&
-			s.topics["mytopic"].lastAccess.Unix() <= time.Now().Unix()+2
+		tp.mu.RLock()
+		defer tp.mu.RUnlock()
+		return tp.lastAccess.Unix() >= time.Now().Unix()-2 &&
+			tp.lastAccess.Unix() <= time.Now().Unix()+2
 	})
 
+	// Hack!
+	time.Sleep(time.Second)
+
 	// Topic won't get pruned
 	s.execManager()
 	require.NotNil(t, s.topics["mytopic"])
diff --git a/server/smtp_sender.go b/server/smtp_sender.go
index 6aa3c574..0f798030 100644
--- a/server/smtp_sender.go
+++ b/server/smtp_sender.go
@@ -110,7 +110,7 @@ func formatMail(baseURL, senderIP, from, to string, m *message) (string, error)
 	if trailer != "" {
 		message += "\n\n" + trailer
 	}
-	date := time.Unix(m.Time, 0).Format(time.RFC1123Z)
+	date := time.Unix(m.Time, 0).UTC().Format(time.RFC1123Z)
 	subject = mime.BEncoding.Encode("utf-8", subject)
 	body := `From: "{shortTopicURL}" <{from}>
 To: {to}
diff --git a/server/smtp_sender_test.go b/server/smtp_sender_test.go
index 363881a2..782c0f97 100644
--- a/server/smtp_sender_test.go
+++ b/server/smtp_sender_test.go
@@ -15,6 +15,7 @@ func TestFormatMail_Basic(t *testing.T) {
 	})
 	expected := `From: "ntfy.sh/alerts" <ntfy@ntfy.sh>
 To: phil@example.com
+Date: Fri, 24 Dec 2021 21:43:24 +0000
 Subject: A simple message
 Content-Type: text/plain; charset="utf-8"
 
@@ -36,6 +37,7 @@ func TestFormatMail_JustEmojis(t *testing.T) {
 	})
 	expected := `From: "ntfy.sh/alerts" <ntfy@ntfy.sh>
 To: phil@example.com
+Date: Fri, 24 Dec 2021 21:43:24 +0000
 Subject: =?utf-8?b?8J+YgCBBIHNpbXBsZSBtZXNzYWdl?=
 Content-Type: text/plain; charset="utf-8"
 
@@ -57,6 +59,7 @@ func TestFormatMail_JustOtherTags(t *testing.T) {
 	})
 	expected := `From: "ntfy.sh/alerts" <ntfy@ntfy.sh>
 To: phil@example.com
+Date: Fri, 24 Dec 2021 21:43:24 +0000
 Subject: A simple message
 Content-Type: text/plain; charset="utf-8"
 
@@ -80,6 +83,7 @@ func TestFormatMail_JustPriority(t *testing.T) {
 	})
 	expected := `From: "ntfy.sh/alerts" <ntfy@ntfy.sh>
 To: phil@example.com
+Date: Fri, 24 Dec 2021 21:43:24 +0000
 Subject: A simple message
 Content-Type: text/plain; charset="utf-8"
 
@@ -103,6 +107,7 @@ func TestFormatMail_UTF8Subject(t *testing.T) {
 	})
 	expected := `From: "ntfy.sh/alerts" <ntfy@ntfy.sh>
 To: phil@example.com
+Date: Fri, 24 Dec 2021 21:43:24 +0000
 Subject: =?utf-8?b?IDo6IEEgbm90IHNvIHNpbXBsZSB0aXRsZSDDtsOkw7zDnyDCoUhvbGEsIHNl?= =?utf-8?b?w7FvciE=?=
 Content-Type: text/plain; charset="utf-8"
 
@@ -126,6 +131,7 @@ func TestFormatMail_WithAllTheThings(t *testing.T) {
 	})
 	expected := `From: "ntfy.sh/alerts" <ntfy@ntfy.sh>
 To: phil@example.com
+Date: Fri, 24 Dec 2021 21:43:24 +0000
 Subject: =?utf-8?b?4pqg77iPIPCfkoAgT2ggbm8g8J+ZiCBUaGlzIGlzIGEgbWVzc2FnZSBhY3Jv?= =?utf-8?b?c3MgbXVsdGlwbGUgbGluZXM=?=
 Content-Type: text/plain; charset="utf-8"
 
diff --git a/server/smtp_server.go b/server/smtp_server.go
index dc34eb95..6efd5230 100644
--- a/server/smtp_server.go
+++ b/server/smtp_server.go
@@ -70,19 +70,18 @@ func (b *smtpBackend) Counts() (total int64, success int64, failure int64) {
 
 // smtpSession is returned after EHLO.
 type smtpSession struct {
-	backend    *smtpBackend
-	conn       *smtp.Conn
-	topic      string
-	token      string
-	mu         sync.Mutex
-	basic_auth string
+	backend   *smtpBackend
+	conn      *smtp.Conn
+	topic     string
+	token     string // If email address contains token, e.g. topic+token@domain
+	basicAuth string // If SMTP AUTH PLAIN was used
+	mu        sync.Mutex
 }
 
 func (s *smtpSession) AuthPlain(username, password string) error {
 	logem(s.conn).Field("smtp_username", username).Debug("AUTH PLAIN (with username %s)", username)
-	basic_auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password)))
 	s.mu.Lock()
-	s.basic_auth = basic_auth
+	s.basicAuth = base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password)))
 	s.mu.Unlock()
 	return nil
 }
@@ -203,8 +202,8 @@ func (s *smtpSession) publishMessage(m *message) error {
 	}
 	if s.token != "" {
 		req.Header.Add("Authorization", "Bearer "+s.token)
-	} else if s.basic_auth != "" {
-		req.Header.Add("Authorization", "Basic "+s.basic_auth)
+	} else if s.basicAuth != "" {
+		req.Header.Add("Authorization", "Basic "+s.basicAuth)
 	}
 	rr := httptest.NewRecorder()
 	s.backend.handler(rr, req)
@@ -222,7 +221,7 @@ func (s *smtpSession) Reset() {
 
 func (s *smtpSession) Logout() error {
 	s.mu.Lock()
-	s.basic_auth = ""
+	s.basicAuth = ""
 	s.mu.Unlock()
 	return nil
 }
diff --git a/server/topic_test.go b/server/topic_test.go
index 8de16111..0376c942 100644
--- a/server/topic_test.go
+++ b/server/topic_test.go
@@ -10,8 +10,6 @@ import (
 )
 
 func TestTopic_CancelSubscribersExceptUser(t *testing.T) {
-	t.Parallel()
-
 	subFn := func(v *visitor, msg *message) error {
 		return nil
 	}