RSS у 66 соціальних мереж: Facebook, Instagram, X, LinkedIn, Telegram та інші Блог Партнерська програма Контакти
Увійти Почати безкоштовно
Updated: 2026-10-06
Symfony RSS Feed: Build It with Twig and Auto-Post New Posts

Short answer: Symfony has no ready-made RSS component, but a Symfony RSS feed is one controller and one Twig template. Load your latest published posts with Doctrine, render them in an rss.xml.twig template with absolute URLs from url() and RFC 822 dates, return the response with the application/rss+xml content type, and cache it for a few minutes. Validate it once and any RSS auto-poster can share every new article for you.

Symfony powers a large share of serious PHP applications: company websites, news portals, documentation hubs, e-commerce back offices and plenty of blogs built on top of a custom data model. Because the framework gives you building blocks rather than a finished CMS, a feed is something you add yourself. The good news is that Symfony already has everything you need: routing, Doctrine, Twig, HTTP caching and a test client. This guide shows a clean way to build the feed, the details that most often break feeds in Symfony projects, and how to use the feed to automate social sharing.

What a Symfony RSS feed has to deliver

It helps to agree on the job before writing code. A feed that works well for readers and automation tools:

  • lists the most recent public items, newest first, usually 20 to 50 of them;
  • gives every item a title, an absolute link, a short description, a publication date and a permanent unique ID (the guid);
  • describes the channel with a title, a link to the site and a short description;
  • is served with an RSS or XML content type, in UTF-8, without redirects;
  • stays stable over time, so tools that read it never mistake an old item for a new one.

Images, categories and full content are optional extras, but they make a real difference when the feed is used for social posting, because they decide what the post looks like. We will add them after the basic version works.

Step 1: a controller that loads the posts

Start with a controller action. The example assumes a Doctrine entity Post with title, slug, summary, publishedAt та published fields, and a route named blog_show for the article page. Adjust the names to your own model.

namespace AppController;

use AppRepositoryPostRepository;
use SymfonyBundleFrameworkBundleControllerAbstractController;
use SymfonyComponentHttpFoundationResponse;
use SymfonyComponentRoutingAttributeRoute;

class FeedController extends AbstractController
{
    #[Route('/rss.xml', name: 'rss_feed', methods: ['GET'])]
    public function rss(PostRepository $posts): Response
    {
        $items = $posts->findBy(
            ['published' => true],
            ['publishedAt' => 'DESC'],
            30
        );

        $response = $this->render('feed/rss.xml.twig', [
            'posts' => $items,
        ]);
        $response->headers->set('Content-Type', 'application/rss+xml; charset=UTF-8');

        return $response;
    }
}

Two details matter here. The query filters on the published flag, so drafts never leak into the feed, and it sorts newest first with a hard limit. If your posts can be scheduled for a future date, add a condition on publishedAt <= now in a repository method as well; otherwise a post scheduled for next week appears in the feed today.

Step 2: the Twig template

Create templates/feed/rss.xml.twig. Twig escapes output automatically, and with Symfony’s default settings the escaping strategy follows the template file name, so a title that contains an ampersand or angle brackets is written as valid XML instead of breaking the document.

<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
  <title>Example Blog</title>
  <link>{{ url('homepage') }}</link>
  <description>New articles from Example</description>
  <language>en</language>
  <atom:link href="{{ url('rss_feed') }}" rel="self" type="application/rss+xml"/>
  {% for post in posts %}
  <item>
    <title>{{ post.title }}</title>
    <link>{{ url('blog_show', {slug: post.slug}) }}</link>
    <guid isPermaLink="true">{{ url('blog_show', {slug: post.slug}) }}</guid>
    <pubDate>{{ post.publishedAt|date('r', 'UTC') }}</pubDate>
    <description>{{ post.summary }}</description>
  </item>
  {% endfor %}
</channel>
</rss>

Notice three things. The url() function produces absolute URLs, while path() would produce relative ones that break inside feeds. The date('r', 'UTC') filter outputs the RFC 2822 style date that RSS expects, in a fixed timezone, which avoids surprises when the server and the database disagree about local time. And the XML declaration must be the very first line of the output, with no blank line or space before it, or strict parsers reject the whole document.

If a summary can contain HTML, decide deliberately how to handle it. You can strip tags with striptags for a clean plain-text description, or wrap prepared HTML in a CDATA section. Do not mark it raw without a CDATA wrapper: a single unescaped character then invalidates the feed. Our article on special characters and encoding problems in RSS feeds covers the typical symptoms.

Option: generate the XML with a feed library

The Twig approach gives you full control and needs no extra package. If you would rather not maintain the XML structure by hand, the laminas/laminas-feed package can write RSS and Atom from PHP objects: you create a feed writer, set the channel fields, add entries with links, dates and descriptions, and export the result as RSS. It handles escaping and date formatting for you and is a sensible choice when you need both RSS and Atom from the same data.

Whichever route you choose, keep the feed-building code in one place, for example a small FeedBuilder service. The main feed, category feeds and any test can then share exactly the same logic, and a later change to the item format happens in a single file.

Absolute URLs behind a reverse proxy

The most common Symfony feed bug has nothing to do with XML. The links come out as http://localhost, http://127.0.0.1:8080 or plain http:// instead of your public HTTPS domain. Symfony builds absolute URLs from the current request, and behind Nginx, a load balancer or a container platform the application may see an internal address.

  • Configure trusted proxies. In config/packages/framework.yaml, set trusted_proxies to the addresses of your proxy and trusted_headers to the forwarded headers it sends, such as x-forwarded-for, x-forwarded-proto та x-forwarded-host. Symfony then generates URLs with the scheme and host visitors actually use. The Symfony documentation on load balancers and reverse proxies explains the exact options for each setup.
  • Set a default URI for commands. If you build feeds or static exports in a console command, there is no request to read the host from. Set framework.router.default_uri to your public address so url() still produces correct links.
  • Check the result. Open the live feed and search for http://, localhost and any internal hostname. None of them should appear.

Relative links are the other half of this problem. They work on your HTML pages but break once an item is read elsewhere, as described in our guide to relative URLs and broken links in RSS feeds.

Images, categories and feed discovery

Richer items produce better social posts. Once the basic feed validates, consider these additions:

  • An image per item. Додати <enclosure url="..." length="..." type="image/jpeg"/> with an absolute URL to the featured image. If you use LiipImagineBundle, generate a mid-sized version so the file is not several megabytes. Many auto-posting tools use the enclosure as the post image.
  • Categories. One <category> element per tag lets tools turn categories into hashtags or filter items by topic.
  • Open Graph tags. Make sure each article page has og:title, og:description та og:image. Tools that find no image in the feed often fall back to the page’s Open Graph image.
  • Autodiscovery. In base.html.twig, add <link rel="alternate" type="application/rss+xml" title="Example Blog" href="{{ url('rss_feed') }}"> so browsers, readers and tools can find the feed from any page.

For the meaning of each element, the RSS 2.0 specification is short and readable, and it is the best reference when a validator warning is unclear.

Category and tag feeds

A single site-wide feed is enough to start. As the site grows, separate feeds become valuable: a reader who follows tutorials may not want release notes, and an auto-posting setup often sends different topics to different channels. In Symfony this is one more route with a parameter:

#[Route('/category/{slug}/rss.xml', name: 'category_feed', methods: ['GET'])]
public function category(Category $category, PostRepository $posts): Response
{
    $items = $posts->findBy(
        ['published' => true, 'category' => $category],
        ['publishedAt' => 'DESC'],
        30
    );
    // render the same template with a category-specific channel title
}

Symfony resolves the Category entity from the slug and returns a 404 automatically when it does not exist, which is the behaviour you want: an empty but valid feed for a mistyped URL would hide the error. Keep the GUID of an article identical in every feed it appears in, give each feed a descriptive channel title, and add a discovery link on each category page.

HTTP caching and conditional requests

A feed is requested far more often than most pages. Feed readers, aggregators and auto-posting services poll it on a schedule, sometimes every few minutes, around the clock. A controller that runs a heavy query on every hit becomes noticeable load.

  • Keep the query small. Thirty items is plenty, and you only need the fields the template uses.
  • Set cache headers. Mark the response public with a short max-age, for example five minutes, using $response->setPublic() та $response->setMaxAge(300) or the #[Cache] attribute. A reverse proxy or the Symfony HTTP cache can then answer most requests without touching PHP.
  • Answer 304 when nothing changed. Set Last-Modified to the date of the newest item with $response->setLastModified(), then return early when $response->isNotModified($request) is true. Well-behaved clients download the full feed only when there is something new.
  • Clear the cache on publish if speed matters. If posts must appear in the feed the moment they go live, purge the cached feed from the code that publishes them.

Caching interacts with auto-posting timing, so keep the lifetime short. Our explanation of why cached feeds make auto-posted content lag shows how the delays add up.

Test and validate the feed

A small functional test protects every tool that depends on the feed. With Symfony’s WebTestCase, request /rss.xml, assert a successful response and the RSS content type, then parse the body with simplexml_load_string() and check that the first item has a title, an absolute link and a date. It takes a few minutes to write and catches most regressions, such as a template change that breaks the XML or a refactor that drops the published filter.

Before connecting the feed to automation, also run these manual checks on the live site:

  1. Fetch the headers with curl -I https://example.com/rss.xml. Expect HTTP 200, an RSS or XML content type and no redirects.
  2. Paste the URL into a feed validator and fix every error, not just the first one.
  3. Open a few item links in a private browser window to confirm they are public.
  4. Publish a test article on a staging copy and confirm it appears at the top of the staging feed, not the production one.
ProblemTypical cause in SymfonyFix
Links use http or an internal hostTrusted proxies not configuredSet trusted_proxies and trusted_headers
“XML declaration allowed only at start”Blank line or BOM before the declarationStart the template with the declaration; check included files
Feed downloads instead of displayingContent type missing or wrongSet application/rss+xml; charset=UTF-8
Broken characters or invalid XMLSummary output with raw and no CDATAKeep autoescaping or wrap HTML in CDATA
Same post shared twiceGUID changes when a slug is editedUse a permanent URL or ID as the GUID
Scheduled posts visible earlyQuery ignores publishedAtFilter on publishedAt <= now

If a post is shared more than once, the guide on duplicate GUID fixes explains how auto-posting tools decide what is new.

Connect the feed to social auto-posting

With a valid feed in place, sharing new articles no longer depends on someone remembering to do it. Add the feed URL to an auto-posting service, connect the social accounts and channels you use, choose how each post is built from the title, description and link, and pick posting hours if you want them. The service checks the feed regularly and publishes each new item once.

Symfony sites often split their content: product news to a professional network, technical posts to Mastodon or Bluesky, release notes to a Telegram channel or a Discord server. Category feeds make that easy, because each target can follow exactly the feed that suits it. More setups like this are collected under technical guides and the broader RSS-стрічка topic.

How PostRSS works with a Symfony feed

PostRSS reads any RSS or Atom feed, including one served by a Symfony controller, and publishes each new item to the networks you connect. It checks feeds as often as every minute, takes the image from the feed item or the page’s Open Graph tags, and offers keyword filters, hashtags from item categories and UTM parameters. Developers can also use the PostRSS REST API v1 and outgoing webhooks, and PostRSS has been running since 2014. See the features page for the full list of 66 networks and the pricing page for what each plan includes.

Related reading

The bottom line

A Symfony RSS feed takes one controller, one Twig template and a handful of careful details: absolute HTTPS links through trusted proxies, RFC 2822 dates in a fixed timezone, stable GUIDs, the right content type and a short HTTP cache. Add images and categories, put a discovery link in the base template, cover it with a functional test and validate it once. From then on the feed works quietly in the background, and every new article reaches readers, newsletters and your social channels without manual sharing.

FAQ

Does Symfony have a built-in RSS feed component?

No. Symfony does not ship a feed component, but a feed is simply a route that returns XML. A controller with a Twig template, or a library such as laminas-feed, is all you need.

Why do the links in my Symfony feed use http instead of https?

Symfony builds absolute URLs from the incoming request. Behind a reverse proxy, configure trusted proxies and forwarded headers so the application sees the original scheme and host.

Which date format should I use in the Twig template?

Use the date filter with the r format and a fixed timezone, for example UTC. It produces the RFC 2822 style date that RSS readers and auto-posting tools expect.

How often will tools request my Symfony feed?

Feed readers and auto-posting services often check every few minutes to every hour. A public cache of a few minutes and Last-Modified support keep the load low.

Can I auto-post new Symfony articles to social media?

Yes. Once the feed is public and valid, an RSS auto-posting service can watch it and publish each new article to the social networks and channels you connect.

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 слів і публікує кожну в 58+ соцмережах. Перші 3 статті безкоштовно Перевірка стану сайту Site AI Audit SEO, швидкість, SSL, безпека та налаштування пошти в одному звіті — за пріоритетом виправлень. Перший аудит безкоштовно Глибокий SEO-аудит Site SEO AI Audit Повний SEO-обхід за 7 напрямами, включно з видимістю в ШІ-пошуку, з виправленнями за впливом. Перший аудит безкоштовно RSS і товарні фіди RSS Feed Creator Створюйте RSS з будь-якої вебсторінки, а також товарні фіди для Google і Meta, що оновлюються самі. Безкоштовний план Розробка сайтів і SEO Internet Solutions Сайти, інтернет-магазини та індивідуальні системи — проєктуємо, створюємо й підтримуємо самі. З 2011 року
PostRSS — платформа автоматизації RSS-стрічок і автопостингу
Огляд конфіденційності

Цей вебсайт використовує файли cookie, щоб ми могли забезпечити вам максимально зручний користувацький досвід. Інформація про cookie зберігається у вашому браузері та виконує такі функції, як розпізнавання вас під час повернення на наш сайт, а також допомагає нашій команді зрозуміти, які розділи сайту ви вважаєте найцікавішими та найкориснішими.