diff --git a/docs/releases.md b/docs/releases.md
index 4131a89eef94da423f2b7074fc3b7600bcddef98..e4f78db9f1c431495cdb3bf9b57a537a803fbaa5 100644
--- a/docs/releases.md
+++ b/docs/releases.md
@@ -13,6 +13,10 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
 
 ## ntfy server v1.25.0 (UNRELEASED)
 
+**Bugs**:
+
+* Respect Firebase "quota exceeded" response for topics, block Firebase publishing for user for 10min ([#289](https://github.com/binwiederhier/ntfy/issues/289))
+
 **Maintenance:**
 
 * Upgrade Firebase Admin SDK to 4.x ([#274](https://github.com/binwiederhier/ntfy/issues/274))
diff --git a/server/config.go b/server/config.go
index 7f15aedcd71c2f54d2fe0444ad05fcb60627a839..60d3cdf99b6fa6065ae3a35d28c26351b12c9312 100644
--- a/server/config.go
+++ b/server/config.go
@@ -6,16 +6,16 @@ import (
 
 // Defines default config settings (excluding limits, see below)
 const (
-	DefaultListenHTTP                        = ":80"
-	DefaultCacheDuration                     = 12 * time.Hour
-	DefaultKeepaliveInterval                 = 45 * time.Second // Not too frequently to save battery (Android read timeout used to be 77s!)
-	DefaultManagerInterval                   = time.Minute
-	DefaultAtSenderInterval                  = 10 * time.Second
-	DefaultMinDelay                          = 10 * time.Second
-	DefaultMaxDelay                          = 3 * 24 * time.Hour
-	DefaultFirebaseKeepaliveInterval         = 3 * time.Hour    // ~control topic (Android), not too frequently to save battery
-	DefaultFirebasePollInterval              = 20 * time.Minute // ~poll topic (iOS), max. 2-3 times per hour (see docs)
-	DefaultFirebaseQuotaLimitPenaltyDuration = 10 * time.Minute
+	DefaultListenHTTP                           = ":80"
+	DefaultCacheDuration                        = 12 * time.Hour
+	DefaultKeepaliveInterval                    = 45 * time.Second // Not too frequently to save battery (Android read timeout used to be 77s!)
+	DefaultManagerInterval                      = time.Minute
+	DefaultDelayedSenderInterval                = 10 * time.Second
+	DefaultMinDelay                             = 10 * time.Second
+	DefaultMaxDelay                             = 3 * 24 * time.Hour
+	DefaultFirebaseKeepaliveInterval            = 3 * time.Hour    // ~control topic (Android), not too frequently to save battery
+	DefaultFirebasePollInterval                 = 20 * time.Minute // ~poll topic (iOS), max. 2-3 times per hour (see docs)
+	DefaultFirebaseQuotaExceededPenaltyDuration = 10 * time.Minute // Time that over-users are locked out of Firebase if it returns "quota exceeded"
 )
 
 // Defines all global and per-visitor limits
@@ -70,7 +70,7 @@ type Config struct {
 	DelayedSenderInterval                time.Duration
 	FirebaseKeepaliveInterval            time.Duration
 	FirebasePollInterval                 time.Duration
-	FirebaseQuotaLimitPenaltyDuration    time.Duration
+	FirebaseQuotaExceededPenaltyDuration time.Duration
 	UpstreamBaseURL                      string
 	SMTPSenderAddr                       string
 	SMTPSenderUser                       string
@@ -120,10 +120,10 @@ func NewConfig() *Config {
 		MessageLimit:                         DefaultMessageLengthLimit,
 		MinDelay:                             DefaultMinDelay,
 		MaxDelay:                             DefaultMaxDelay,
-		DelayedSenderInterval:                DefaultAtSenderInterval,
+		DelayedSenderInterval:                DefaultDelayedSenderInterval,
 		FirebaseKeepaliveInterval:            DefaultFirebaseKeepaliveInterval,
 		FirebasePollInterval:                 DefaultFirebasePollInterval,
-		FirebaseQuotaLimitPenaltyDuration:    DefaultFirebaseQuotaLimitPenaltyDuration,
+		FirebaseQuotaExceededPenaltyDuration: DefaultFirebaseQuotaExceededPenaltyDuration,
 		TotalTopicLimit:                      DefaultTotalTopicLimit,
 		VisitorSubscriptionLimit:             DefaultVisitorSubscriptionLimit,
 		VisitorAttachmentTotalSizeLimit:      DefaultVisitorAttachmentTotalSizeLimit,
diff --git a/server/errors.go b/server/errors.go
index 2fa883fa63b38ce628127aebc43fea3c4a7278f4..32c1b3b9814f12e833cd520e3ac5f15ded966796 100644
--- a/server/errors.go
+++ b/server/errors.go
@@ -59,7 +59,6 @@ var (
 	errHTTPTooManyRequestsLimitSubscriptions         = &errHTTP{42903, http.StatusTooManyRequests, "limit reached: too many active subscriptions, please be nice", "https://ntfy.sh/docs/publish/#limitations"}
 	errHTTPTooManyRequestsLimitTotalTopics           = &errHTTP{42904, http.StatusTooManyRequests, "limit reached: the total number of topics on the server has been reached, please contact the admin", "https://ntfy.sh/docs/publish/#limitations"}
 	errHTTPTooManyRequestsAttachmentBandwidthLimit   = &errHTTP{42905, http.StatusTooManyRequests, "too many requests: daily bandwidth limit reached", "https://ntfy.sh/docs/publish/#limitations"}
-	errHTTPTooManyRequestsFirebaseQuotaReached       = &errHTTP{42906, http.StatusTooManyRequests, "too many requests: Firebase quota for topic reached", "https://ntfy.sh/docs/publish/#limitations"}
 	errHTTPInternalError                             = &errHTTP{50001, http.StatusInternalServerError, "internal server error", ""}
 	errHTTPInternalErrorInvalidFilePath              = &errHTTP{50002, http.StatusInternalServerError, "internal server error: invalid file path", ""}
 )
diff --git a/server/smtp_server.go b/server/smtp_server.go
index a5b6f85075abe20aa76e3a86b57b801cc3450346..7812371e82220138b14722e79ade6922be120269 100644
--- a/server/smtp_server.go
+++ b/server/smtp_server.go
@@ -146,6 +146,7 @@ func (s *smtpSession) publishMessage(m *message) error {
 	url := fmt.Sprintf("%s/%s", s.backend.config.BaseURL, m.Topic)
 	req, err := http.NewRequest("PUT", url, strings.NewReader(m.Message))
 	req.RemoteAddr = s.remoteAddr // rate limiting!!
+	req.Header.Set("X-Forwarded-For", s.remoteAddr)
 	if err != nil {
 		return err
 	}
diff --git a/server/visitor.go b/server/visitor.go
index 1bbc4e00d488f3438811107112d9e9ca80a4aa66..5a8e186be539a9a1c35064b949c6c1b09287a7e9 100644
--- a/server/visitor.go
+++ b/server/visitor.go
@@ -73,7 +73,7 @@ func (v *visitor) FirebaseAllowed() error {
 func (v *visitor) FirebaseTemporarilyDeny() {
 	v.mu.Lock()
 	defer v.mu.Unlock()
-	v.firebase = time.Now().Add(v.config.FirebaseQuotaLimitPenaltyDuration)
+	v.firebase = time.Now().Add(v.config.FirebaseQuotaExceededPenaltyDuration)
 }
 
 func (v *visitor) EmailAllowed() error {