Make WordPress Core

Opened 4 weeks ago

Last modified 4 weeks ago

#65657 new defect (bug)

get_lastcommentmodified() includes notes, so an internal note changes the comments-feed Last-Modified/ETag

Reported by: dhrupo Owned by:
Priority: normal Milestone: Awaiting Review
Component: Comments Version: 6.9
Severity: normal Keywords: has-patch has-unit-tests has-screenshots
Cc: Focuses:

Description

get_lastcommentmodified() returns the date of the most recent approved comment, and WP::send_headers() uses it (max'd with get_lastpostmodified()) to build the Last-Modified and ETag headers for the comments feed (/comments/feed, ?feed=comments-rss2).

Its three queries do not exclude the internal note comment type:

SELECT comment_date_gmt FROM wp_comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1

Notes are stored as approved comments (comment_type = 'note'), so adding or resolving a note makes it the most recent comment. The public comments feed's Last-Modified and ETag then change, even though the feed body already excludes notes (see #64145 / #65613) and did not change.

Consequences:

  • Conditional GET (304 Not Modified) caching of the comments feed is defeated whenever notes are used — clients and CDNs re-fetch an identical feed.
  • The public Last-Modified header exposes the timing of internal editorial notes.

This is the same class of issue as #64145 ("notes need to be consistently excluded wherever comments or comment counts are shown"). wp_update_comment_count_now() already excludes notes via comment_type != 'note'.

Steps to reproduce

  1. On a site using Notes, capture the comments feed headers: curl -I 'http://example.test/?feed=comments-rss2'.
  2. Add an internal note to any post (or resolve/reopen one).
  3. Re-request the feed — Last-Modified and ETag have changed, though the feed body is unchanged.

See the attached before/after screenshots.

Proposed fix

Add AND comment_type != 'note' to the three get_lastcommentmodified() queries, matching the exclusion already used by wp_update_comment_count_now() and the comment feed queries.

Attachments (2)

lastcomment-before.png (201.3 KB ) - added by dhrupo 4 weeks ago.
BEFORE the fix: adding one internal note bumps the public comments feed's Last-Modified (Fri 01 Jan -> Sat 02 Jan) and ETag, even though the feed body excludes notes. Real headers captured via curl -I on /?feed=comments-rss2. See Trac #65657.
lastcomment-after.png (196.9 KB ) - added by dhrupo 4 weeks ago.
AFTER the fix: the same note leaves the comments feed's Last-Modified and ETag unchanged. Conditional-GET (304) caching keeps working and no note timing is exposed. See Trac #65657 / the PR.

Download all attachments as: .zip

Change History (3)

@dhrupo
4 weeks ago

BEFORE the fix: adding one internal note bumps the public comments feed's Last-Modified (Fri 01 Jan -> Sat 02 Jan) and ETag, even though the feed body excludes notes. Real headers captured via curl -I on /?feed=comments-rss2. See Trac #65657.

@dhrupo
4 weeks ago

AFTER the fix: the same note leaves the comments feed's Last-Modified and ETag unchanged. Conditional-GET (304) caching keeps working and no note timing is exposed. See Trac #65657 / the PR.

This ticket was mentioned in PR #12573 on WordPress/wordpress-develop by @dhrupo.


4 weeks ago
#1

## Summary

get_lastcommentmodified() returns the date of the most recent approved comment, and WP::send_headers() uses it (max'd with get_lastpostmodified()) to build the Last-Modified and ETag headers for the comments feed.

Its three queries did not exclude the internal note comment type:

SELECT comment_date_gmt FROM wp_comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1

Notes are stored as approved comments (comment_type = 'note'), so adding or resolving a note makes it the most recent comment. The public comments feed's Last-Modified/ETag then change, even though the feed body already excludes notes (#64145 / #65613) and did not change.

Consequences:

  • Conditional GET (304 Not Modified) caching of the comments feed is defeated whenever notes are used — clients and CDNs re-fetch an identical feed.
  • The public Last-Modified header exposes the timing of internal editorial notes.

## Reproduction

On a site using Notes:

curl -I '/?feed=comments-rss2'   # note Last-Modified + ETag
# add an internal note to any post
curl -I '/?feed=comments-rss2'   # Last-Modified + ETag have changed; feed body is unchanged

Verified live: adding one note moved Last-Modified from Fri, 01 Jan 2027 10:00:00 GMT to Sat, 02 Jan 2027 10:00:00 GMT and changed the ETag. See the before/after screenshots on the Trac ticket.

## Fix

Add AND comment_type != 'note' to the three get_lastcommentmodified() queries, matching the exclusion already used by wp_update_comment_count_now() and the comment feed queries.

## Testing

Added test_notes_are_excluded to Tests_Comment_GetLastCommentModified.

  • Before the fix, it fails: Failed asserting that <note timestamp> is identical to <regular-comment timestamp>.
  • After the fix, it passes.

Full comment group is green:

./vendor/bin/phpunit --group comment
OK (583 tests, 1428 assertions)

## Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 4.8
Used for: Auditing the notes-exclusion surfaces, reproducing the feed-header change, implementing the fix, and writing the regression test. All changes were reviewed by me.

Note: See TracTickets for help on using tickets.