Delivery Modes
Choose between send-once failover and send-all broadcast to control how notifications reach your ntfy servers.
Both modes use durable delivery for normal notifications: the proxy saves the formatted notification before sending and keeps failed work in a FIFO lane dedicated to that context. Error-topic diagnostics use the same server mode but are one-shot, best-effort sends outside the lane, so a failed diagnostic cannot block later alerts.
send-once
Tries the primary server first. If it fails (network error, non-2xx response), the next server in the list is tried.
This continues until one server succeeds or all have been exhausted. When a fallback server delivers the notification, a warning is prepended to the message body.
The prepended warning uses this format:
⚠️ Primary server "primary" was unreachable. This notification was delivered via "secondary".
The server names in the warning match the names from your config, not fixed labels.
Best for:
- Redundancy setups — You want notifications delivered reliably, but only once.
- Multiple ntfy instances — A primary NAS server with a cloud backup.
send-all
Sends the notification to every server in the context's servers list simultaneously. All servers receive the same notification in parallel.
The durable notification remains saved until every configured server succeeds. Successful servers and message stages are checkpointed, so a retry contacts only the unfinished work.
Best for:
- Synchronized instances — You want the same notification on multiple devices or servers.
- Multi-site deployments — Each site runs its own ntfy server and should receive all notifications.
Server Failover
In send-once mode, failover is automatic and ordered:
- The
primary_serveris tried first. - If it fails, the remaining servers from the
serverslist (excluding the primary) are tried in order. - This continues until one succeeds or all fail.
The response indicates whether a fallback was used and which servers succeeded or failed. In send-all mode, there is no failover — every server is contacted regardless of individual failures.
IP Forwarding
The proxy sets the X-Forwarded-For header on outgoing requests to ntfy servers using the original client IP from Cloudflare's cf-connecting-ip header. This means your ntfy server sees the real client IP, not the Cloudflare edge IP.
This applies to all delivery modes.
ntfy Headers
The proxy sets these ntfy headers on outgoing requests based on the interpreter's notification output. Interpreters transform the incoming payload and return a notification object, and the format stage maps those fields to headers.
| Header | Notification Field | Description |
|---|---|---|
X-Title | title | Message title displayed prominently in the notification. |
X-Priority | priority | Priority level from 1 (min) to 5 (max). Controls alert urgency and phone vibration. |
X-Tags | tags | Comma-separated tags. Emoji shortcodes (e.g., warning) render as icons. |
X-Markdown | markdown | Set to true to enable Markdown rendering in the message body. |
X-Icon | icon | URL of a custom notification icon. |
X-Actions | actions | Action buttons (view URL, broadcast, HTTP request). |
X-Attach | attach | URL of a file to download and attach to the notification. |
X-Filename | filename | Filename for an attachment sent as binary in the request body. |
info
The ntfy-json interpreter supports all of these fields directly from the JSON payload. Other interpreters (like synology, seerr, statuspage) set a subset automatically based on the parsed content.
warning
The proxy builds outgoing headers from the interpreter's notification object. Incoming request headers — including ntfy-specific ones like X-Click, X-Delay, X-Email, X-Call, and X-Template — are not forwarded to ntfy servers. Only the 8 headers listed above are set. For the full list of ntfy-supported headers, see the ntfy publishing docs.
If no interpreter sets a title, the proxy falls back to the context's name field. For full documentation on ntfy headers, see the ntfy publishing docs.
Attachments
When a notification carries a binary attachment, delivery happens in two stages per server:
- Stage 1 — POST each text message part with ntfy headers.
- Stage 2 — PUT the binary attachment with the
X-Filenameheader.
The plain-text interpreter preserves a binary request body, such as an image, as the attachment. Because it has no text part, each server gets only the PUT. Error topics likewise send error-debug.json as an attachment-only PUT, but those diagnostics are best-effort rather than durable.
Attachment Message Count
The number of messages you receive depends on the delivery mode. For example, an error topic notification with mode set to send-all and two ntfy servers configured arrives as two messages, one error-debug.json attachment per server.
Visitor details from show_visitor_info are appended to the text body rather than sent as a separate message, so they never add to the count.
info
Customizations made via headers are not applied to the attachment message. This prevents duplicate high-priority vibrations (from X-Priority: 5) on the attachment message.
Smart Message Splitting
Messages exceeding 4000 UTF-8 bytes are automatically split into numbered parts. Each part gets a title suffix indicating its position (e.g., "(1/3)", "(2/3)", "(3/3)").
Splitting respects UTF-8 character boundaries so multi-byte characters are never broken mid-sequence. The split threshold aligns with ntfy's recommended message size limits.
Response Format
The worker returns a JSON response after delivery. The default response includes only the status:
{
"status": "success"
}
If a fallback server was used, the status is partial, because the primary server failed, and a fallback_note field is included:
{
"status": "partial",
"fallback_note": "Delivered via fallback server \"secondary\""
}
When show_response_output is enabled, the response includes full details:
{
"status": "success",
"context": "GitHub Status",
"interpreter": "statuspage",
"servers": [
{ "name": "primary", "status": 200, "success": true, "stages": ["stage-1: ok"] }
],
"message": {
"title": "[githubstatus.com] Elevated Error Rates",
"body_size": 342,
"parts": 1,
"has_attachment": false
}
}
Possible status values:
"success"— All servers returned 2xx. HTTP status is 200."partial"— Some servers succeeded and some failed, as when a fallback delivered asend-oncenotification. HTTP status is 200."queued"— The notification is safely stored but has not completed its delivery mode. HTTP status is 202, and the next notification for the same context triggers another attempt."failed"— Delivery produced no successful server result and was not retained by the durable flow. HTTP status is 502."error"— A pipeline error occurred (authentication, routing, interpretation, etc.). HTTP status varies: 400, 403, 404, 405, 422, or 500."ignored"— The interpreter intentionally suppressed the notification (returnednull). HTTP status is 200.