RSS uz 66 sociālajiem tīkliem: Facebook, Instagram, X, LinkedIn, Telegram un citiem Blogs Partner programma Kontakti
Pieslēgties Sākt bez maksas
Updated: 2026-10-20
CakePHP RSS Feed: Build It in a Controller and Auto-Post

Short answer: To serve an RSS feed from a CakePHP application, connect a route for /feed.xml to a controller action, read the newest published articles with the ORM, render a small PHP template that escapes every value with the h() helper, format dates in UTC and return the response with the type rss. Add an ETag and a short cache header 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 article reaches your social channels.

CakePHP is one of the longest-lived PHP frameworks, and many business sites, intranets and customer portals still run on it. Sooner or later such a site publishes something that people outside it want to follow: news, product updates, events or listings. 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 CakePHP 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.

When a CakePHP site needs a feed

A feed is worth adding whenever the application publishes content that people want to follow:

  • articles, news or announcements managed in an admin area;
  • products, offers or listings that change during the week;
  • events, courses or openings with a date;
  • any content that should also reach people who never visit the site.

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 frameworks, see the CodeIgniter guide.

The route

Add one route that maps the public address to a controller action. The route belongs in config/routes.php, inside the scope that handles public pages. Using 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.

$routes->scope('/', function (RouteBuilder $builder): void {
    $builder->connect('/feed.xml', ['controller' => 'Articles', 'action' => 'feed']);
});

Keep the public address of the site in configuration 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. A common choice is a key in config/app_local.php that you read with Configure::read.

The controller action

The action reads the newest published articles, builds an ETag from them, answers conditional requests and only then renders the template. The condition on the publication time matters. Without it, an article scheduled for tomorrow would appear in the feed today, and an auto-poster would share it before it exists on the website.

use Cake\Core\Configure;
use Cake\I18n\DateTime;

public function feed()
{
    $articles = $this->fetchTable('Articles')->find()
        ->select(['id', 'title', 'slug', 'summary', 'published_at'])
        ->where(['status' => 'published', 'published_at <=' => DateTime::now()])
        ->orderByDesc('published_at')
        ->limit(20)
        ->all();

    $seed = '';
    foreach ($articles as $article) {
        $seed .= $article->id . ':' . $article->published_at->getTimestamp() . ';';
    }

    $response = $this->response
        ->withType('rss')
        ->withEtag(md5($seed))
        ->withHeader('Cache-Control', 'public, max-age=300');
    if ($response->checkNotModified($this->request)) {
        return $response;
    }
    $this->setResponse($response);

    $this->viewBuilder()->disableAutoLayout()->setTemplate('feed');
    $this->set([
        'articles' => $articles,
        'siteUrl' => rtrim(Configure::read('App.siteUrl'), '/'),
    ]);
}

The example uses the CakePHP 5 method orderByDesc; in CakePHP 4 use order with a descending direction instead. The method checkNotModified compares the ETag with the If-None-Match header of the request and, when they match, turns the response into a 304 without a body. The ETag is built from the identifiers and publication times of the items, so it changes whenever a new article appears or an old one is replaced.

The template

Create templates/Articles/feed.php. The template contains only the feed. Every value goes through h(), 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.

<?= '<?xml version="1.0" encoding="UTF-8"?>' ?>

<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
    <title><?= h('Example News') ?></title>
    <link><?= h($siteUrl) ?>/</link>
    <description><?= h('The latest articles from Example News') ?></description>
    <language>en</language>
    <atom:link href="<?= h($siteUrl) ?>/feed.xml" rel="self" type="application/rss+xml"/>
<?php foreach ($articles as $article): ?>
    <item>
        <title><?= h($article->title) ?></title>
        <link><?= h($siteUrl . '/' . $article->slug . '/') ?></link>
        <guid isPermaLink="false">article-<?= (int)$article->id ?></guid>
        <pubDate><?= gmdate(DATE_RSS, $article->published_at->getTimestamp()) ?></pubDate>
        <description><?= h($article->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.

Where each field comes from

Feed fieldSource in the exampleRule to keep
titleThe title columnPlain text, escaped, no markup
linkSite address plus slugAbsolute address that returns 200 directly
guidThe row id with a prefixNever changes, even if the slug changes
pubDateThe publication time in UTCEnglish day and month names, never in the future
descriptionThe summary columnOne 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 an article, 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.

Dates in the English format

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 of the server or of the CakePHP application. Do not use the translated date formatting of the framework for feeds; 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.

Caching and speed

Auto-posters, readers and search engines request a feed often, and almost always nothing has changed. The action above handles that in two ways. It sends an ETag, and when the client returns the same value, the answer is a 304 with no body and no template rendering. It also sends a Cache-Control header that lets a proxy or a content delivery network keep the response for a few minutes. If the query is heavy, store the result with the CakePHP cache engine and clear it when an article is saved. The background to both techniques is in the guide to ETags and conditional GET un the guide to feed caching. Do not cache for hours, because a long cache delays every new post by the same amount.

Keeping the feed outside the login and the debug toolbar

Two CakePHP features can quietly damage a feed. The first is authentication: if your application requires a login for most actions, allow the feed action explicitly for visitors who are not signed in, otherwise an auto-poster receives a redirect to the login page instead of XML. The second is the debug toolbar, which appends HTML to responses when debug mode is on. Always test the feed with debug switched off, as on the live server, and make sure that nothing prints text before the XML declaration. A single blank line before the declaration is enough to make some validators reject the feed. More causes of broken feeds are collected in what to check when a feed stops updating.

Images and richer items

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.

Test the feed before you connect it

  1. Request the route with curl and look at the headers: the status should be 200, the content type should be an RSS type and an ETag should be present.
  2. Repeat the request with the ETag in an If-None-Match header and confirm that a 304 comes back.
  3. Paste the public address into the W3C Feed Validation Service and fix every error.
  4. Publish a test article and check that it appears at the top of the feed.
  5. Set a publication time in the future and check that the item stays hidden until then.

There is a longer checklist in the feed validation guide and a short curl routine in the guide to debugging a feed with curl.

Common mistakes to avoid

  • Unescaped text. One ampersand in a title can make the whole feed invalid. Always use h().
  • Future items. Filter by publication time so scheduled content stays hidden until its moment.
  • Unstable GUIDs. Use an identifier that never changes.
  • Output before the declaration. Blank lines or debug output before the XML declaration break validation.
  • A layout around the feed. Disable the automatic layout, or the feed is wrapped in page HTML.
  • Changing the feed address. Keep the route, or redirect the old one permanently.

How PostRSS works with a CakePHP feed

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.

Saistīta lasāmviela

The bottom line

CakePHP needs only a route, a short controller action and one template to serve a dependable RSS feed: select published items dated in the past, escape every value with h(), use a stable GUID, format dates in English UTC, build links from a configured public address and return the XML with the right type, an ETag and a short cache. Make sure the feed is reachable without a login, test it with curl and a validator, keep the address unchanged and connect it to an auto-poster. After that, every article you publish can reach your social channels automatically.

FAQ

Does CakePHP have a built-in RSS helper?

Recent versions do not ship one in the core, so the simplest approach is a plain template that prints the XML and escapes each value with h(). The result is easy to read and to test.

How do I return the feed with the right content type?

Call withType with the value rss on the response. CakePHP maps it to the RSS media type and adds the character set, so browsers and validators recognise the feed.

Why does my feed redirect to the login page?

The authentication component or middleware is protecting every action by default. Allow the feed action for visitors who are not signed in, and test it in a private browser window or with curl.

How many items should the feed contain?

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.

How do I stop old posts from being shared again?

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.

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, 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.

Vairāk mūsu komandas rīku

Izstrādājusi Internet Solutions — PostRSS komanda. Katrs produkts ietaupa laiku savā veidā.