Implementing RSS Feeds in Remix with Cloudflare Pages and D1

2025-05-05

Learn how to implement an efficient RSS feed in a Remix application running on Cloudflare Pages with D1 database, featuring SQLite optimizations, ETag implementation, and Cloudflare Cache Rules.

When I talk about RSS, I feel like I'm an old guy. Maybe I am. But I like RSS, and I still use it in 2025.

That's why I did not want to compromise how much effor I put into RSS feed on this blog. I wanted to explore how much I can optimise delivering RSS in the tech stack I've chosen. When implementing RSS in a Remix application running on Cloudflare Pages with D1 database, several performance considerations come into play to ensure optimal delivery and caching of the feed.

Optimize with Database Indexes, as usual

My Remix application follows a hybrid approach where content is authored as markdown files in the repository but synced to Cloudflare D1 (SQLite) for efficient querying. The RSS feed needs to pull the most recent posts from D1 and generate a valid XML feed.

One of the most critical performance aspects of the RSS feed is efficiently querying the database. Since RSS feeds typically display the most recent posts, I need to optimize the query that fetches these posts.

The core query pattern for our RSS feed is:

SELECT slug, title, description, post_date as date, tags
FROM posts
WHERE post_type = 'blog'
ORDER BY post_date DESC
LIMIT 15

This fetches the 15 most recent blog posts, ordered by publication date. To optimize this query, I added a composite index in SQLite. In one of my migration files, I included:

-- Adding an index to optimize post listing by type and date
CREATE INDEX IF NOT EXISTS idx_posts_type_date
ON posts(post_type, post_date DESC);

This index dramatically improves the query performance since:

  1. It covers the WHERE post_type = ? filter
  2. It includes the sort order (post_date DESC)
  3. It supports the common access pattern of "recent posts by type"

For a small dataset, this may seem like overkill, but as the content grows, this index ensures that RSS feed generation remains fast regardless of the total number of posts.

ETag sounds basic yet powerful enough

ETags provide a mechanism for validating cached resources, allowing clients to check if their cached version is still valid without downloading the entire resource again. I implemented ETag support in the RSS feed to minimize bandwidth usage and improve load times.

Here's how the ETag implementation works:

const contentHash = await generateETag(rssXml);
const ifNoneMatch = request.headers.get('If-None-Match');
if (ifNoneMatch === contentHash) {
  return new Response(null, {
    status: 304, // Not Modified
    headers: {
      "ETag": contentHash,
    }
  });
}

The generateETag function creates a hash of the feed content:

async function generateETag(content: string): Promise<string> {
  const encoder = new TextEncoder();
  const data = encoder.encode(content);
  const hashBuffer = await crypto.subtle.digest('SHA-256', data);

  // Convert hash to hex string
  const hashArray = Array.from(new Uint8Array(hashBuffer));
  const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');

  // Using first 20 characters for a shorter but still unique ETag
  return `"${hashHex.substring(0, 20)}"`;
}

When a feed reader requests the RSS feed:

  1. If it's the first request, the full feed is returned with an ETag
  2. On subsequent requests, the feed reader sends the ETag in the If-None-Match header
  3. If the feed hasn't changed, a 304 Not Modified response is returned (no body)
  4. If the feed has changed, the new feed content is returned with a new ETag

This dramatically reduces bandwidth usage for frequently polled RSS feeds while ensuring readers get updates as soon as content changes.

Leverage CDN

To further optimize delivery, I configured Cloudflare Cache Rules specifically for the RSS feed. These rules are defined in Terraform, making them version-controlled and easily deployable:

# Rule for RSS feed
rules {
  action = "set_cache_settings"
  action_parameters {
    edge_ttl {
      mode    = "override_origin"
      default = 86400 # 24 hours in seconds
      status_code_ttl {
        status_code = 200
        value       = 86400
      }
    }
    browser_ttl {
      mode    = "override_origin"
      default = 3600 # 1 hour in seconds
    }
    cache = true
    serve_stale {
      disable_stale_while_updating = false
    }
    respect_strong_etags = true
  }
  expression  = "(starts_with(http.request.uri.path, \"/rss.xml\"))"
  description = "Cache RSS feed with optimized settings"
  enabled     = true
}

There are couple of key aspects of this configuration. This configures 24hrs for Edge TTL, making sure that the content is cached at the Cloudflare's datacenters, while configuring shorter 1hr for Browser/Clients TTL, which is a general strategy in most of content-based use cases. I enabled Serve Stale, so that stale RSS content is served while the cache is being updated, which is mostly fine in RSS feeds. When you want Cloudflare Cache Rules to respect your own implemented ETag, you should also enable respect_strong_etags, although this is not guaranteed and what we as users cannot have a full control. Finally, I configure to cache only when 200 OK, so that it does not accidentally cache 4xx/5xx cases when the Remix app returns errors in case of deploying software bugs or backend Cloudflare D1 is down.

Although I cannot have a full control over cache behaviours in every edge cases, I'm mostly satisfied with Cloudflare's Cache Rules and it works okay.

Conclusion

Who knew that something as "ancient" as RSS feeds (by web standards, anyway) could benefit so much from modern edge computing?

The truth is, RSS may be one of the web's older standards, but it's still incredibly useful—and there's something delightfully ironic about using Cloudflare's cutting-edge infrastructure to serve a technology from the era of dial-up modems. It's like watching a hipster DJ spin vinyl records through a state-of-the-art sound system.

All of techniques introduced are nothing special - leveraging database indexes, using web standards like E-Tag, and leveraging CDN's cache control by carefully designing Cache-Control between Edges and Browsers. But by combining all of simple techniques, you can achieve high cache-hit ratio for our lovely RSS.

If you're still using RSS feed like me, let me know how you build your own RSS feed, and which clients you use to read feeds. My favourite one is NetNewsWire, which is a brilliantly simple yet powerful tool.

Programmer. Generalist. Open-minded amateur.

Ken Wagatsuma is a Site Reliability Engineer based in the UK. He is passionate about managing complex production applications that solve real-world problems. Keen on Performance Engineering and Databases.