Self-hosted on Cloudflare Workers. Your data stays in your own account, served from your own domain.
Built on serverless infrastructure — zero servers to maintain, no subscription fees.
Your emails and feeds live exclusively in your own Cloudflare account. No shared infrastructure, no data mining.
Your subscribe address and your feed's reading URL are independent, unguessable ids. Share a feed without leaking the inbox that feeds it — and an address a newsletter harvests can never be turned into your feed.
Cloudflare Workers, KV, and Email Routing all fall within the generous free tier. Deploy at zero cost.
Subscribe to newsletters using addresses on your own domain (e.g. apple@yourdomain.com). No lock-in.
Use Cloudflare Email Routing (no third-party) or ForwardEmail webhooks — whichever fits your setup.
Email attachments are stored in Cloudflare R2 and exposed as RSS enclosures — no extra hosting needed.
Each feed picks up the favicon of its newsletter's sender domain, so feeds are easy to tell apart in your reader and the admin UI.
Give a feed a lifetime and it deletes itself when the timer runs out — perfect as a disposable inbox for one-off sign-ups you don't want to keep around.
Deleting a feed fires RFC 8058 one-click unsubscribe requests to its newsletters, so the messages stop arriving at the now-dead address.
Point your whole domain at kill-the-news: anything that isn't a feed is forwarded to a fallback address instead of dropped, so your personal mail still gets through.
Optionally delegate admin authentication to Authelia, Authentik, or any reverse proxy that sets Remote-User.
Automate feeds and emails through a versioned REST API, documented with an OpenAPI 3.1 spec and a live interactive reference.
Every feed is served in all three formats — RSS 2.0, Atom, and JSON Feed — so it just works in NetNewsWire, Reeder, Feedly, NewsBlur and any other reader. Conditional requests (ETag / 304) keep polling cheap.
Export all your feeds as an OPML file and bulk-import them into any reader in one shot — easy onboarding, and no lock-in if you ever want to move.
kill-the-news detects "confirm your subscription" emails at ingestion and surfaces the link prominently in the admin — unlike kill-the-newsletter, where the confirm email lands buried in your feed reader and is easily missed.
If a newsletter already publishes RSS, Atom, or JSON Feed, kill-the-news spots it and points you to the original — subscribe at the source directly when you prefer.
From email delivery to your RSS reader in milliseconds, with no moving parts.
Create a feed in the admin UI and get a unique address like newsletter.42@yourdomain.com. Subscribe to any newsletter with it.
When a newsletter arrives, Cloudflare routes it to your Worker. It parses the content and stores it in KV — attachments go to R2.
Your feed is live at /rss/:feedId. Add it to any RSS client and never miss an issue.
A single setup script handles KV namespaces, secrets, and wrangler.toml generation.
# 1. Clone the repo $ git clone https://github.com/juherr/kill-the-news.git && cd kill-the-news # 2. Log in to Cloudflare $ npx wrangler login # 3. Run the interactive setup (creates KV, sets secrets, writes wrangler.toml) $ bash setup.sh # 4. Deploy to the edge $ npm run deploy
Then choose how emails reach your Worker:
Everything you need to self-host kill-the-news on your own Cloudflare account, step by step.
You need a free Cloudflare account with a domain managed by Cloudflare DNS, and Node.js ≥ 18.
Clone the repo and log in to Cloudflare via Wrangler. This opens a browser window to authorize the CLI.
$ git clone https://github.com/juherr/kill-the-news.git $ cd kill-the-news $ npm install $ npx wrangler login
The interactive setup script creates the KV namespace, sets the admin password secret, and writes wrangler.toml for you.
$ bash setup.sh # The script will ask for: # - Your domain (e.g. newsletters.example.com) # - An admin password (stored as a Wrangler secret) # - Whether to enable R2 for attachments (optional)
This generates wrangler.toml from the example template. Do not commit it — it is gitignored.
$ npm run deploy # Wrangler compiles the Worker, uploads it to Cloudflare, # and prints the Worker URL. Your admin UI will be live at: # https://<your-domain>/admin
Choose how incoming emails reach your Worker:
In the Cloudflare dashboard, go to Email → Email Routing and enable it on your domain. Add a catch-all rule with action Send to Worker pointing to your deployed Worker. No third-party service required.
Point ForwardEmail MX records at your domain. ForwardEmail parses incoming mail and POSTs a JSON payload to /api/inbound. Useful if you already use ForwardEmail.
To store email attachments and expose them as RSS enclosures, create an R2 bucket and bind it in wrangler.toml:
[[r2_buckets]] binding = "ATTACHMENT_BUCKET" bucket_name = "kill-the-news-attachments"
Attachments will be served at /files/:id/:filename and linked as <enclosure> elements in the RSS feed.
Protect your endpoints against abuse using Cloudflare WAF custom rate-limiting rules — no code changes required, pure infrastructure.
Go to Security → Security rules, click Create rule, choose Rate limiting rule, and create one rule per endpoint below.
⚠️ Free tier limitations: only 1 rate limiting rule allowed; period and block duration capped at 10 seconds. Prioritise the /api/inbound rule — it's the public-facing attack surface. Upgrade to a paid plan for full coverage.
Use cloudflare_ruleset with phase = "http_ratelimit" — see the snippet below.
Recommended limits:
| Setting | /api/inbound |
/admin* |
|---|---|---|
| Condition (URI Path) | wildcard /api/inbound/* |
wildcard /admin/* |
| Limit (recommended) | 60 req / min / IP | 20 req / min / IP |
| Limit (free tier) | 10 req / 10 s / IP | 20 req / 10 s / IP |
| Action (recommended) | Block (1 min) | Managed Challenge (5 min) |
| Action (free tier) | Block (10 s) | Managed Challenge (10 s) |
Terraform equivalent (supports method filtering and longer periods — requires a paid Cloudflare plan):
resource "cloudflare_ruleset" "rate_limiting" { zone_id = var.cloudflare_zone_id name = "kill-the-news rate limiting" kind = "zone" phase = "http_ratelimit" rules { description = "Rate limit /api/inbound" expression = "(http.request.uri.path eq \"/api/inbound\" and http.request.method eq \"POST\")" action = "block" ratelimit { characteristics = ["ip.src"] period = 60 requests_per_period = 60 mitigation_timeout = 60 } } rules { description = "Rate limit /admin" expression = "starts_with(http.request.uri.path, \"/admin\")" action = "managed_challenge" ratelimit { characteristics = ["ip.src"] period = 60 requests_per_period = 20 mitigation_timeout = 300 } } }
These rules run at the Cloudflare edge before the Worker is invoked — zero latency impact on normal traffic.
If ForwardEmail's delivery IPs ever trigger the /api/inbound limit, add them as an IP Access Rule with action Allow under Security → WAF → Tools.
The practical stuff — subscribing, privacy, troubleshooting, and how kill-the-news differs.
Create a feed in the admin UI and you get a unique address on your domain (e.g. newsletter.42@yourdomain.com) plus an RSS and an Atom feed. Any email sent to that address is turned into entries in those feeds.
Confirmation emails arrive as feed entries — open the entry in your reader and click the confirmation link. If a publisher requires a reply, subscribe with your normal inbox instead and set up a filter that auto-forwards its mail to your feed address.
Yes. Each feed URL carries an unguessable ID, it is served from your own domain on your own Cloudflare account, and the admin UI is password-protected. Treat the feed URL like a password — anyone who has it can read your newsletters.
Feeds honor an optional size and time-to-live cap so RSS readers stay happy — some readers choke on feeds that grow too large. When a limit is reached, the oldest entries (and their R2 attachments) are purged automatically.
Don't. Anyone with the URL can read your newsletters and even unsubscribe you. Share the project instead, so others can self-host and create their own feeds.
Send a test email to the feed address. If it shows up within a minute, the delay is on the newsletter publisher's side, not kill-the-news. Readers that support WebSub get near-instant push updates instead of waiting for the next poll.
kill-the-news is self-hosted on your own Cloudflare account: your data, your domain, RSS and Atom output, attachments served as enclosures, WebSub push updates — all running on the free tier.
It runs on Cloudflare's free tier (Workers + KV + R2) plus the cost of your domain. With Cloudflare Email Routing, no third-party service is required at all.
From the password-protected admin UI — open the Feeds tab and delete it there. Its entries and attachments are removed along with it.
Yes — optionally. When an R2 bucket is configured, email attachments are stored there and exposed as RSS/Atom enclosures, downloadable from each entry. It's off by default: if no R2 bucket is bound (or you set ATTACHMENTS_ENABLED = "false"), attachments are simply skipped and everything else works as usual. R2 usage is shown on the status page so you can stay within the 10 GB free tier.
Minimal dependencies, maximum portability — runs entirely on Cloudflare's global network.
kill-the-news is free and open source. If it saves you time, consider supporting its development.
Sponsor on GitHub