Degoogling cost me my YouTube feed, so I made my own
Updated 16 August 2026
Contents
I dropped the YouTube app as part of degoogling my life. My self-hosted YouTube subscription feed shows one thing: new videos from the 10 to 15 channels I follow. No comments, no recommendations, no Shorts. I rebuilt that screen and nothing else.
A self-hosted YouTube subscription feed
The result is one chronological list. It merges videos from every channel I follow and sorts them newest first. I can add a channel by URL or @handle, and it stays on the list.
I ignore or avoid everything YouTube adds to the subscription feed. Building the one screen I use took less work than configuring the app to leave me alone.
YouTube RSS feeds without an API key
Each channel publishes a public RSS feed at feeds/videos.xml. The file uses Atom, a standard XML format for content feeds. A Cloudflare Worker reads it, and fast-xml-parser converts the XML into structured data. The Worker is a small program that runs on Cloudflare’s network.
This route avoids the YouTube Data application programming interface (API), Google’s official interface for software requests. It needs no quota, Google project, or token.
A handle or vanity URL can change, but a channel ID remains stable. The Worker reads the channel page once to find that ID. It saves the result and uses the ID for later feed requests.
Before the Worker fetches a user-supplied URL, it checks the destination. This guard prevents server-side request forgery (SSRF). Without it, someone could make the Worker fetch an unintended address.
How to filter YouTube Shorts from RSS
Shorts were the main thing I wanted gone, and one check does not catch them reliably. The first check looks for #shorts in the title. The second tests how the video URL redirects because Shorts and full videos resolve differently. A video must pass both checks to appear.
Playback without the tracking
Clicking a thumbnail loads the video through YouTube’s privacy-enhanced iframe player. An iframe places a page from one site inside another page. The embed does not load before that click, so the player cannot set cookies before playback.
This is not perfect privacy. Once loaded, the player writes an identifier to local storage and sends your IP address to Google. It is still much less intrusive than the app.
On iOS, the video starts muted with a “Tap for sound” overlay. Apple blocks one-tap sound on the web, so muted playback is the least-bad workaround. Videos that block embedding show an “open on YouTube” link instead of a dead frame.
Picture-in-picture came for free
Picture-in-picture was an unplanned bonus. Playback uses a normal web video element, so it receives the system controls. On iOS, the floating window follows me across apps. Audio also continues when the screen locks.
Fast and hard to break
The Worker starts all feed requests together. JavaScript’s Promise.allSettled waits for every result without rejecting the group when one request fails. Each request also has a timeout. A slow or dead channel therefore returns nothing instead of hanging the page.
The Cache API stores the merged feed on Cloudflare’s network for 15 minutes. Cloudflare KV, a key-value database, stores the channel list. The Worker builds the complete HTML page before sending it to the browser. I deploy it with Cloudflare’s wrangler command-line tool.