RSS в 66 социальных сетей: Facebook, Instagram, X, LinkedIn, Telegram и другие Блог Партнерская программа Контакты
Войти Начать бесплатно
Updated: 2026-10-18
Azure Functions RSS Feed: Build It and Auto-Post Items

Short answer: To serve an RSS feed from Azure Functions, create an HTTP-triggered function with anonymous access, read the newest published items from a store such as Azure Table Storage, build RSS 2.0 XML with escaped text, absolute links and RFC 822 dates, and return it with the content type application/rss+xml and a short cache header. Remove the default api route prefix so the feed lives at a clean address, test it with curl and a validator, and then give that address to an auto-poster so every new item reaches your social channels.

Serverless hosting is a good fit for a feed. The file is small, requests are frequent but light, and you only need code that runs for a few milliseconds. Many teams that keep their data in Azure, such as product announcements, release notes or a small catalogue, discover that nothing publishes a feed for them. The portal has no switch for it and a storage account cannot generate XML by itself. This guide builds a complete Azure Functions RSS feed in Node.js with the version 4 programming model, explains why each line is there, and finishes with the checks that keep the feed reliable once an auto-poster depends on it.

When an Azure Functions RSS feed makes sense

A function is the right tool when your content lives in a service rather than in a content management system that already has a feed:

  • release notes, changelogs or status messages written by a team and stored in a table or a database;
  • listings, events or offers entered through an internal tool or a form;
  • items that arrive from other systems through queues or webhooks and need one public list;
  • a static website that needs a dynamic feed without running a whole server.

If your site already runs on WordPress or a static generator, use the feed that platform provides instead; see how to find your WordPress feed link. If your content has no feed and no CMS at all, the same function approach is often the shortest path. If you prefer a traditional server framework, the same ideas appear in the Express feed guide.

Create the project

Install the Azure Functions Core Tools and create a Node.js project. The version 4 model keeps the function definition in code, so there is no separate configuration file for each function.

func init rss-feed --worker-runtime node --language javascript --model V4
cd rss-feed
npm install @azure/data-tables
func new --template "HTTP trigger" --name feed

The data tables package is the official client for Azure Table Storage and for the table API of Cosmos DB. If your data sits somewhere else, replace only the query function in the example. Everything else, from escaping to headers, stays the same.

Shape the data for fast reads

Table Storage returns rows sorted by partition key and then by row key, always in ascending order. A feed needs the newest items first, so build the row key from a reverse timestamp. That way the natural order is already the order of the feed and the function never has to sort a large set.

// when saving a post
const publishedAt = new Date("2026-10-18T07:40:00Z");
const rowKey = String(9999999999999 - publishedAt.getTime()).padStart(13, "0");

await table.createEntity({
  partitionKey: "post",
  rowKey,
  postId: "a1b2c3",
  title: "Version 2.4 is out",
  slug: "version-2-4",
  summary: "What changed in the new release.",
  status: "published",
  publishedAt: publishedAt.toISOString(),
  imageUrl: "https://www.example.com/images/v24.png"
});

Keep the stable identifier in its own column. The row key encodes the date, so it changes if someone edits the date, and a changing identifier makes old items look new to every reader. The identifier column becomes the GUID of the item.

The function

Open src/functions/feed.js and replace its content with the code below. It reads the newest rows, skips drafts and items dated in the future, builds the XML and answers conditional requests with a 304 status when nothing changed.

const { app } = require("@azure/functions");
const { TableClient, odata } = require("@azure/data-tables");
const crypto = require("crypto");

const SITE_URL = process.env.SITE_URL;          // for example https://www.example.com
const table = TableClient.fromConnectionString(process.env.STORAGE_CONNECTION, "posts");

const esc = (value) => String(value ?? "")
  .replace(/&/g, "&")
  .replace(/</g, "&lt;")
  .replace(/>/g, "&gt;")
  .replace(/"/g, "&quot;");

async function latestPosts(limit = 20) {
  const now = new Date();
  const items = [];
  const rows = table.listEntities({
    queryOptions: { filter: odata`PartitionKey eq ${"post"} and status eq ${"published"}` }
  });
  for await (const row of rows) {
    if (new Date(row.publishedAt) > now) continue;   // scheduled, not public yet
    items.push(row);
    if (items.length >= limit) break;
  }
  return items;
}

function buildFeed(posts) {
  const items = posts.map((p) => `
    <item>
      <title>${esc(p.title)}</title>
      <link>${SITE_URL}/news/${esc(p.slug)}/</link>
      <guid isPermaLink="false">${esc(p.postId)}</guid>
      <pubDate>${new Date(p.publishedAt).toUTCString()}</pubDate>
      <description>${esc(p.summary)}</description>
    </item>`).join("");

  const built = posts.length ? new Date(posts[0].publishedAt).toUTCString() : new Date(0).toUTCString();
  return `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Example Product News</title>
    <link>${SITE_URL}/news/</link>
    <description>Release notes and announcements</description>
    <language>en</language>
    <lastBuildDate>${built}</lastBuildDate>
    <atom:link href="${SITE_URL}/feed.xml" rel="self" type="application/rss+xml"/>${items}
  </channel>
</rss>`;
}

app.http("feed", {
  methods: ["GET"],
  authLevel: "anonymous",
  route: "feed.xml",
  handler: async (request, context) => {
    const xml = buildFeed(await latestPosts());
    const etag = '"' + crypto.createHash("sha1").update(xml).digest("hex") + '"';
    const headers = {
      "Content-Type": "application/rss+xml; charset=utf-8",
      "Cache-Control": "public, max-age=300",
      "ETag": etag
    };
    if (request.headers.get("if-none-match") === etag) {
      return { status: 304, headers };
    }
    return { status: 200, headers, body: xml };
  }
});

Three details deserve attention. The escape helper replaces the ampersand first, otherwise the entities it creates would be escaped a second time. The date uses toUTCString(), which produces a valid RFC 822 style value such as Sun, 18 Oct 2026 07:40:00 GMT with the month names fixed in English whatever the server language. The GUID is marked with isPermaLink="false" because it is an identifier and not an address.

Remove the api prefix

By default every HTTP function is served under /api, so the feed would live at /api/feed.xml. A shorter address is easier to remember and to move later. Set an empty prefix in host.json:

{
  "version": "2.0",
  "extensions": {
    "http": {
      "routePrefix": ""
    }
  }
}

If the function app is published under its own domain, the feed is now at /feed.xml. If your website lives elsewhere, point a path on the main domain to the function app through a reverse proxy or a front door service, so the public address never exposes the hosting name. A stable address matters because auto-posters remember it; changing it later means reconnecting every channel.

Settings and security

SettingWhere to put itWhy
SITE_URLApplication settingsEvery link in the feed is built from this fixed public address
STORAGE_CONNECTIONApplication settings or a key vault referenceNever commit the connection string to the repository
authLevelFunction codeAnonymous, because an auto-poster cannot send a function key
Table permissionsStorage accountThe function needs read access only

Anonymous access sounds alarming, but a feed is public by definition. What matters is that the function only reads the published rows and never exposes drafts, internal notes or contact details. Keep private fields out of the entity or filter them out in buildFeed. Where possible, use a managed identity for the storage connection instead of a connection string, so that no secret exists at all.

Cold starts, caching and timeouts

On hosting plans that scale to zero, the first request after a quiet period starts a new instance and can take several seconds. An auto-poster that waits only a short time may record a timeout and try again later; the effects are explained in RSS feed timeouts. There are three practical remedies:

  • choose a plan that keeps at least one instance warm, if the feed matters to your business;
  • put a content delivery network or a front door in front of the function and let it cache the response for a few minutes, using the cache header the function already sends;
  • keep the code small. Load only the table client at start-up and avoid heavy libraries that slow the cold start.

Do not cache for hours. A long lifetime delays every share by the same amount, which is the effect described in RSS feed caching explained. The ETag handling in the example also lets the auto-poster ask whether anything changed instead of downloading the file again; the mechanism is explained in ETags and conditional GET.

An alternative: a timer and a blob

If your data changes rarely, you can avoid running code on every request. Use a timer trigger that rebuilds the XML every few minutes and writes it to a blob in a public container, or to the static website feature of the storage account. The same buildFeed function does the work, and the blob is served by storage with no cold start at all. The price is a small delay between a change and the new file, which is usually acceptable. Set the content type of the blob explicitly to application/rss+xml, because storage defaults to a generic type and some readers react badly to it; see the Content-Type guide.

Test the feed before connecting it

  1. Run func start locally and request http://localhost:7071/feed.xml with curl. Check for the 200 status, the XML content type, the cache header and the ETag. The method is explained in debugging an RSS feed with curl.
  2. Repeat the request with the If-None-Match header and confirm that the answer is 304.
  3. Deploy with func azure functionapp publish followed by the name of your app, and request the public address.
  4. Run the file through the W3C Feed Validation Service and fix every error.
  5. Add a draft and a post dated in the future, and confirm that neither appears.

If something looks wrong later, the checklist in RSS feed not updating helps you find the cause quickly. Relative links and missing images are the other common problems; see relative URLs in RSS feeds.

Common mistakes to avoid

  • Leaving the api prefix in place. You publish an address that is hard to move and easy to mistype.
  • Sorting in memory. A reverse timestamp row key keeps the query cheap as the table grows.
  • Unescaped text. One raw ampersand in a title can invalidate the whole file.
  • Using the row key as the GUID. If the date changes, the identifier changes and old posts are shared again; see why your feed auto-posts the same article twice.
  • Function keys on a public feed. An auto-poster cannot send them, so require none.
  • Building links from the request host. Behind a proxy this can leak an internal name. Use the fixed SITE_URL setting.

How PostRSS works with an Azure Functions feed

PostRSS has been publishing RSS and Atom feeds to social networks since 2014. Paste the address of your feed.xml function, connect your accounts and choose how each post is built from the title, the description or the page title. PostRSS checks the feed as often as every minute and shares each new item, with its image and link, to the channels you choose among 66 supported networks, messengers, team chats and blogs, including LinkedIn, X, Bluesky, Mastodon and Telegram. Keyword filters, UTM parameters and posting windows let you tune each channel, and webhooks connect PostRSS to your own systems. See the PostRSS features page and the plan limits on the pricing page. More developer guides are under the Technical Guide tag.

Связанное чтение

The bottom line

Azure Functions turns data in a table into a dependable RSS feed with about sixty lines of code. Store a stable identifier, use a reverse timestamp key so the newest rows come first, escape every value, format dates in English UTC, build links from a fixed site address, remove the api prefix and send a short cache header with an ETag. Test the result with curl and a validator, keep the address unchanged and connect it to an auto-poster. After that, every published item can reach your social channels without anyone copying a link.

FAQ

Do I need a paid plan to serve an RSS feed from Azure Functions?

The hosting plan and its price depend on your account and on how much traffic you expect. A feed is small and light, so even the lowest usage is usually enough, but check the current plan details in your own Azure portal before you rely on it.

Why does the feed return a 404 at the root address?

Functions are served under the api prefix by default. Either request the address with that prefix or set an empty route prefix in host.json, then restart the app.

Can I use a different database instead of Table Storage?

Yes. Replace the query function with a call to Cosmos DB, SQL or any other store. Keep the same rules: only published items, newest first, a limit of about twenty and a stable identifier for each item.

Should the function return the feed as Atom instead of RSS?

Both formats work with auto-posters. RSS 2.0 is simpler to write and almost universally supported, which is why this guide uses it.

How do I stop old posts from being shared again?

Give every item a stable GUID that never changes and avoid editing the publication date of items that are already public. Some tools treat a changed date as a new item.

Can I use this feed with PostRSS?

Yes. PostRSS reads any valid RSS or Atom address that is reachable without a login. Paste the address of the function, connect your channels and new items are shared as soon as they appear.

New guides, once a month

What changed in the networks, what broke, and how to fix it before it costs you reach.

We send a confirmation e-mail first. Unsubscribe any time.

Другие инструменты нашей команды

Создано Internet Solutions — командой PostRSS. Каждый продукт экономит ваше время по-своему.

ИИ-чат для сайтов Talkmio Ваш сайт отвечает посетителям круглосуточно на основе вашего контента и на их языке. Бесплатный план · без карты ИИ-ассистент Ask Mio Чат, код, дизайн, тексты и исследования. Mio выбирает лучшую модель для каждой задачи. Бесплатный план ИИ-автопилот для блога и соцсетей AI Blog Autopilot ИИ пишет SEO-статьи на 2 000–3 000 слов и публикует каждую в 60 соцсетях. Первые 3 статьи бесплатно Проверка состояния сайта Аудит ИИ сайта SEO, скорость, SSL, безопасность и настройки почты в одном отчёте — в порядке важности исправлений. Первый аудит бесплатно Глубокий SEO-аудит Аудит SEO ИИ сайта Полный SEO-обход по 7 направлениям, включая видимость в ИИ-поиске, с исправлениями по степени влияния. Первый аудит бесплатно RSS и товарные фиды Создатель RSS‑ленты Создавайте RSS из любой веб-страницы, а также товарные фиды для Google и Meta, которые обновляются сами. Бесплатный план Разработка сайтов и SEO Internet Solutions Сайты, интернет-магазины и индивидуальные системы — проектируем, создаём и поддерживаем сами. С 2011 года