
Short answer: To serve an RSS feed from a Yii 2 application, add a controller action that reads the newest published posts with a query, renders a small view file that escapes every value with Html::encode, formats dates in UTC and sends the result as raw output with the content type application/rss+xml. Attach the HttpCache filter so repeat requests get a cheap 304 answer, test the address with curl and a validator, and then give the public URL to an auto-poster so every new post reaches your social channels.
Yii 2 powers a large number of business applications, portals and content sites, and many of them publish news, offers or listings that people outside the application want to follow. A feed is the standard way to let readers, aggregators and auto-posters watch that content without an account or an API key. This guide builds a complete Yii 2 RSS feed with caching and conditional requests, explains each decision and ends with the checks that keep the feed dependable once other systems rely on it.
A feed is worth adding whenever the application publishes content that people want to follow:
Without a feed, every announcement has to be copied to social networks by hand. If your content lives in a blog or a CMS instead, use the feed it already provides; see how to find your WordPress feed link. For other PHP approaches, see the CodeIgniter guide та the plain PHP and MySQL guide.
Give the feed a fixed public address. If pretty URLs are enabled in the urlManager component, add a rule in config/web.php that maps the address to the action. A fixed address such as /feed.xml means that you can keep it unchanged for years, even if you rebuild the rest of the site.
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
'feed.xml' => 'site/feed',
],
],Keep the public address of the site in the application parameters rather than reading it from the incoming request. Behind a proxy, the request may show an internal host name, and a feed full of internal addresses is useless to readers. Add a key such as siteUrl to config/params.php and read it with Yii::$app->params.
The action reads the newest published posts and renders the view. The condition on the publication time matters. Without it, a post scheduled for tomorrow would appear in the feed today, and an auto-poster would share it before it exists on the website. The example assumes a table named post with columns for the id, title, slug, summary, status and a publication time stored as a Unix timestamp.
use yii\db\Query;
use yii\web\Controller;
use yii\web\Response;
class SiteController extends Controller
{
public function actionFeed()
{
$posts = (new Query())
->select(['id', 'title', 'slug', 'summary', 'published_at'])
->from('post')
->where(['status' => 'published'])
->andWhere(['<=', 'published_at', time()])
->orderBy(['published_at' => SORT_DESC])
->limit(20)
->all();
$response = Yii::$app->response;
$response->format = Response::FORMAT_RAW;
$response->headers->set('Content-Type', 'application/rss+xml; charset=UTF-8');
$this->layout = false;
return $this->render('feed', [
'posts' => $posts,
'siteUrl' => rtrim(Yii::$app->params['siteUrl'], '/'),
]);
}
}Two lines deserve attention. Setting the format to raw stops Yii from converting the string, and disabling the layout stops the framework from wrapping the feed in the HTML of the site. Both are common causes of broken feeds in Yii applications.
Create views/site/feed.php. The view contains only the feed. Every value goes through Html::encode, which escapes the characters that are special in XML. Print the XML declaration from PHP, because a literal question mark and bracket at the start of a PHP file can be mistaken for a PHP tag.
<?php use yii\helpers\Html; ?>
<?= '<?xml version="1.0" encoding="UTF-8"?>' ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title><?= Html::encode('Example News') ?></title>
<link><?= Html::encode($siteUrl) ?>/</link>
<description><?= Html::encode('The latest posts from Example News') ?></description>
<language>en</language>
<atom:link href="<?= Html::encode($siteUrl) ?>/feed.xml" rel="self" type="application/rss+xml"/>
<?php foreach ($posts as $post): ?>
<item>
<title><?= Html::encode($post['title']) ?></title>
<link><?= Html::encode($siteUrl . '/' . $post['slug'] . '/') ?></link>
<guid isPermaLink="false">post-<?= (int)$post['id'] ?></guid>
<pubDate><?= gmdate(DATE_RSS, (int)$post['published_at']) ?></pubDate>
<description><?= Html::encode($post['summary']) ?></description>
</item>
<?php endforeach; ?>
</channel>
</rss>Open /feed.xml in a browser or with curl. You should see well-formed XML with at most twenty items, newest first.
Auto-posters, readers and search engines request a feed often, and almost always nothing has changed. Yii 2 has a filter for exactly this case. Attach the HttpCache filter from the yii filters namespace to the controller, tell it how to find the time of the latest change and what to put in the ETag, and the framework answers repeat requests with a 304 before the action runs.
use yii\filters\HttpCache;
public function behaviors()
{
return [
[
'class' => HttpCache::class,
'only' => ['feed'],
'cacheControlHeader' => 'public, max-age=300',
'lastModified' => function ($action, $params) {
return (new Query())->from('post')
->where(['status' => 'published'])
->andWhere(['<=', 'published_at', time()])
->max('published_at');
},
'etagSeed' => function ($action, $params) {
return (string) (new Query())->from('post')
->where(['status' => 'published'])
->andWhere(['<=', 'published_at', time()])
->max('published_at');
},
],
];
}The filter sends Last-Modified, ETag and Cache-Control headers, and compares them with If-Modified-Since and If-None-Match on the next request. The query for the newest publication time is much cheaper than building the whole feed. If you edit old posts and want the feed to change, store an updated time and use that value instead. The background to these techniques is in the guide to ETags and conditional GET та the guide to feed caching. Do not cache for hours, because a long cache delays every new post by the same amount.
| Feed field | Source in the example | Rule to keep |
|---|---|---|
| title | The title column | Plain text, escaped, no markup |
| link | Site address plus slug | Absolute address that returns 200 directly |
| guid | The row id with a prefix | Never changes, even if the slug changes |
| pubDate | The publication timestamp in UTC | English day and month names, never in the future |
| description | The summary column | One or two sentences that make sense alone |
The identifier is the most important decision. An auto-poster remembers which items it has already shared by their GUID. If the value changes when someone edits a slug or republishes a post, the same story is shared again. Using the database id with a fixed prefix avoids that. The problem and its fixes are described in why a feed auto-posts the same article twice.
RSS dates follow RFC 822, with English abbreviations for days and months. The PHP function gmdate with the DATE_RSS constant always prints English names and a zero offset, whatever the language set in the Yii configuration. Do not use the formatter component of the framework for feeds, because it follows the application language; a site set to another language would print month names that readers cannot parse. If you see date warnings in a validator, read the guide to RSS date format errors.
Several Yii features can quietly damage a feed. If you use an access control filter, allow the feed action for guests, otherwise an auto-poster receives a redirect to the login page instead of XML. The debug module and the Gii tool append HTML to responses in development mode, so always test with debug switched off, as on the live server. Any text printed before the XML declaration, even a blank line in a view or a stray space at the start of a PHP file, makes some validators reject the feed. Finally, check that cookies and sessions are not needed: a feed should answer the same way for every visitor. More causes of broken feeds are collected in what to check when a feed stops updating.
An auto-poster is more useful when each item carries a picture. Add an enclosure element with the address, the byte length and the type of the image, or a media content element if you prefer that namespace. Use an absolute https address and a file of reasonable size. Without an image, many networks show a plain text card. How tools read these elements is explained in the guide to enclosure tags. If the summary contains HTML, wrap it in a CDATA block or escape it, as described in the CDATA guide.
There is a longer checklist in the feed validation guide and a short curl routine in the guide to debugging a feed with curl.
PostRSS has been publishing RSS and Atom feeds to social networks since 2014. Paste the address of your feed route, 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.
Yii 2 needs only a URL rule, a short controller action and one view to serve a dependable RSS feed: select published items dated in the past, escape every value with Html::encode, use a stable GUID, format dates in English UTC, build links from a configured public address and return raw XML with the right content type. Attach the HttpCache filter, make sure the feed is reachable by guests, test it with curl and a validator, keep the address unchanged and connect it to an auto-poster. After that, every post you publish can reach your social channels automatically.
Not in the core framework. The simplest approach is a controller action and a plain view that prints the XML and escapes each value with Html::encode. Extensions exist, but the view is easy to read and to test.
The default format converts the returned string for a browser page. The raw format sends the string unchanged, so the XML reaches the reader exactly as written, with the content type you set.
It adds Last-Modified, ETag and Cache-Control headers and answers conditional requests with a 304 before the action runs. That saves bandwidth and processing when auto-posters check the feed often.
Between ten and twenty of the newest published items is enough. An auto-poster only needs recent items, and a smaller file is faster to download and parse.
Give every item a stable identifier and use it as the GUID. Avoid editing the publication date of items that are already public, because some tools treat a changed date as a new item.
Yes. PostRSS reads any valid RSS or Atom address that is reachable without a login. Paste the address, connect your channels and new items are shared as soon as they appear.
What changed in the networks, what broke, and how to fix it before it costs you reach.
Створено Internet Solutions — командою PostRSS. Кожен продукт заощаджує ваш час по-своєму.