How FeedWordPress updates articles in Pugpig Express
This article explains how FeedWordPress (FWP) decides when to create or update an Express article from an RSS/Atom feed, and what an update changes.
Written by Frank Lockett
Updated at October 26th, 2026
This article explains how FeedWordPress (FWP) decides when to create or update an Express article from an RSS/Atom feed, and what an update changes. It also explains how Pugpig's own code changes that behaviour, and how to troubleshoot an article that hasn't updated.
It's based on the Kaldor fork of FeedWordPress
(kaldor-feedwordpress-server) and the Pugpig FeedWordPress
Utils extension
(pugpig-connectorwordpress-server/extensions/extras/pugpigfeedwordpress).
1. When feeds are checked
There are two ways a feed gets processed.
Scheduled updates
- FWP checks feeds on a schedule, either through WordPress cron or a
request to the site with
?update_feedwordpress=1. - A feed is checked when it's due. That happens when the time since its last check is longer than its update interval (TTL), plus a small random delay.
- Each check downloads the feed and processes every item in it, one at a time.
Force Update
- On the Syndication admin page you can tick feeds and choose Force Update, or use Force Update Now on a single feed.
- A Force Update treats every item in the feed as changed and applies it to the live article.
- It skips all of the checks described in section 3: the date check, the hash check, the "Manual editing" lock and the Pugpig hash check.
2. How an item is matched to an article
- Each feed item is matched to an existing article by its
guid: the
<guid>(RSS) or<id>(Atom) element. - If the guid isn't a valid URL (for example, it's just a number or
contains spaces), FWP stores it as
http://<site>/?guid=<md5 of the guid>. - If no article has that guid, the item is imported as a new article.
- If the same story appears in two feeds with different guids, it becomes two separate articles in Express.
3. How FWP decides whether an existing article needs updating
For each item that matches an existing article, FWP works out one of four outcomes:
- Nothing to do: the item hasn't changed.
- Update the live article: the changes are applied to the article readers see.
- Store as a revision only: the new version is saved in the article's revision history, but the live article does not change.
- New article: no match was found, so the item is imported.
Step 1: compare dates (when the feed has a date FWP can read)
FWP reads the item's last-modified date from the first of these elements it finds:
dc:modifieddcterms:modified- Atom
modified/updated
If none of these exist, it uses the published date
(pubDate, dc:date, published and
so on).
It then compares that date with the dates already stored for the article (the live article's modified date, plus the date of each revision):
- The date matches one already stored: nothing to do.
- The date is new and later than the live article's modified date: update the live article.
- The date is new but earlier than or equal to the live article's modified date: store as a revision only.
Step 2: compare a content hash (only when the feed has no date FWP can read)
FWP builds a hash of the item's title, link, content and
excerpt, and compares it with the hashes stored against the
article (the syndication_item_hash post meta).
- The hash is already stored: nothing to do.
- The hash is new: the item counts as changed. See the known issue in section 7 about how these changes are applied.
The hash does not include images, custom fields or anything else mapped to post meta. If a feed has no readable dates, a change to only the main image will never be detected.
Step 3: locks
Even when a change is detected, the update is blocked if either of these is set:
- Feed-level lock: the "freeze updates" setting on the feed subscription.
-
Article-level lock: the Manual
editing checkbox in the FeedWordPress box on the article's edit
screen (post meta
_syndication_freeze_updates = yes). This lets editors change a syndicated article in WordPress without FWP overwriting their edits.
Step 4: Pugpig hash check
If the feed includes a <hash> element for each
item, and the feed's settings store it in the
pugpig_fwp_hash post meta, Pugpig compares the two:
- Same hash: the update is rejected, and nothing changes.
- Different hash: the update continues as normal.
When an update is rejected, the plugin log shows:
content hash for <item> is still [<hash>] and so not updating post <ID>.
4. What an update changes
New articles
The article is created with:
- the title, content and excerpt;
- the author, categories and tags;
- the published and modified dates;
- the post status from the feed's settings;
- all post meta (see below).
Updates to the live article
When a change is applied to the live article:
- Title, content and excerpt: replaced with the feed's version. A revision is also saved, so the change shows in the revision history.
- Modified date: set to the item's modified date from the feed, not the time the update ran. If the feed has no readable date, the time the update ran is used.
- Categories, tags and other taxonomies: set again from the feed and the feed's settings, replacing the existing ones.
- Post meta mapped from the feed: deleted and added again with the new values (see below).
- Published date: not changed after the first import.
- Post status, comment status and ping status: not changed, so manual status changes (for example unpublishing) are kept.
- Slug: not changed.
Post meta
On every new article and every live update, FWP deletes and re-adds each meta key it generates for the item. These are:
-
Custom fields set up in the feed's settings
(Syndication → Posts & Links → Custom Post
Settings), including values pulled from the feed with
$(xpath)expressions. Main media, credits and other Pugpig fields are usually mapped here. -
Enclosures: the
enclosuremeta. -
FWP's own tracking fields:
syndication_source,syndication_source_uri,syndication_source_id,syndication_feed,syndication_feed_id,syndication_permalink,syndication_item_hash,rss:commentsandwfw:commentRSS.
Things to be aware of:
- Meta keys that FWP doesn't generate (for example, fields added by editors or other plugins) are left alone.
- If an item no longer has an enclosure, the old
enclosuremeta isn't removed. - Meta is only written when the live article is updated. If the item is stored as a revision only, blocked by a lock or rejected by the hash check, no meta changes are made.
Revision-only updates
- The new title, content and excerpt are saved as a revision, and the new hash is recorded.
- The live article, its meta and its terms don't change.
- Because the new date and hash are now recorded, later checks treat the item as already seen. The article stays as it is until the feed sends a newer date or someone runs a Force Update.
5. What Pugpig adds to FeedWordPress
- Future dates are capped at "now": if an item's modified date is in the future, the current time is used instead.
-
Hash with no date: if an item has a
<hash>but no modified date, the modified date is set to the current time, and the Pugpig hash check (section 3, step 4) decides whether to update. -
Title and excerpt decoding: HTML and XML entities
(for example
&and") are decoded in titles and excerpts. - Edition updates: after a feed check finishes, any editions that contain new or updated articles are re-curated (flatplan).
- Feed order: the order of items in the feed is recorded, so it can be used to sort content.
6. Feed requirements
To make sure updates are picked up reliably, a feed should:
Have a stable, unique guid for each item. If a guid changes, FWP treats the item as a new article.
Include a last-modified date for each item and change it whenever the item changes, including image-only changes.
-
Use date formats that FWP can read. FWP reads dates with PHP's
strtotime(), which only understands English day and month names.- ✅ ISO 8601:
2026-10-21T14:49:53+02:00 - ✅ RFC 822 (English):
Wed, 21 Oct 2026 14:49:53 +0200 - ❌ Localised:
Mi., 21 Okt. 2026 14:49:53 +0200
If a date can't be read, FWP falls back to the current time for new articles and to the content hash for updates (see section 7).
- ✅ ISO 8601:
7. Known issues
Hash-detected changes are stored as revisions, not applied live
When a feed has no date FWP can read, a change found by the content
hash is only applied to the live article if the
feed_items:freshness:reasons diagnostic is switched on.
Otherwise it's saved as a revision and the live article doesn't change.
This behaviour comes from upstream FeedWordPress
(syndicatedpost.class.php, freshness()).
Image-only changes aren't detected without dates
Main media isn't part of the content hash. If the feed has no readable dates, changing only the image produces no update at all.
Saving an article in WordPress can block later feed updates
Saving an article in wp-admin sets its modified date to the current time. If a later version of the item in the feed has an earlier modified date than that, it's stored as a revision only.
8. Troubleshooting an article that hasn't updated
Check the feed. Does the item's modified date change when the article changes? Is it in a format FWP can read (see section 6)?
-
Check the article's published and modified dates. For example:
https://<site>/wp-json/wp/v2/posts/<ID>?_fields=id,date,modified.- If the published date doesn't match the feed's
pubDate, or the modified date doesn't match the feed's modified date, FWP can't read the feed's dates.
- If the published date doesn't match the feed's
Check the revisions. Open the article's revisions in wp-admin. If the newest revision has the new content but the live article doesn't, the update was stored as a revision only.
Check the locks. Is Manual editing ticked on the article? Is the feed set to freeze updates?
Check the post meta. Look at
_syndication_freeze_updates,pugpig_fwp_hashandsyndication_item_hash.-
Turn on diagnostics. In Syndication → Diagnostics, turn on
feed_items:freshnessandsyndicated_posts, then wait for the next feed check. Look for:- "is an update of an existing post": FWP detected a change.
- "Updating existing post #…": the change was applied to the live article.
- "Storing alternate revision of existing post #…": the change was stored as a revision only.
Don't turn on
feed_items:freshness:reasonswhile investigating. It changes how hash-detected updates are applied (see section 7) and can hide the problem. Check the plugin log for
content hash for … is still, which means the Pugpig hash check rejected the update.Run a Force Update on the feed. This applies the latest version of every item. Only do this after you've collected the information above, because it overwrites the evidence.


