Yes. A CMS feed can expose draft metadata even when the draft pages themselves return a login wall, because the feed and the page are generated by separate code paths that enforce access rules independently. A page request checks post status before rendering; a feed, sitemap, or API endpoint only stays clean if its own query filters out non-public content, and history shows those filters have failed in WordPress core, WordPress plugins, and Drupal contrib alike.
The distinction matters because "draft is private" describes the page's behavior, not the database's. The draft's title, author, date, and taxonomy live in the same tables as published content, and any generator that queries those tables too loosely will publish fragments of it. The practical question is not whether your drafts are marked private, but whether every public output on the site — RSS, JSON API, sitemap — applies the same status check the page does.
Official resource:
- Review WordPress's REST API authentication rules for non-public content — Readers can verify that draft and private posts require authenticated requests and how to lock down the REST API further.
Table of Contents
- How a "private" draft leaks through a public query
- Metadata leaks without the body leaking
- What a correctly configured system actually guarantees
- How to check your own site in fifteen minutes
- Frequently Asked Questions
How a "private" draft leaks through a public query
CMS access control is usually enforced per-request, not per-record. When the query behind a public response mishandles status filtering, hidden posts ride along. WordPress before 5.2.4 demonstrated this exactly: CVE-2019-17671, documented by WPScan, let any unauthenticated visitor view private and draft posts by appending `?static=1` to a URL, because WP_Query mishandled the "static" property and only access-checked the first post in the result set. The follow-on detail is what makes it a feed-and-metadata problem rather than a single-page bug.
According to the 0day.work proof of concept, ordinary query parameters — `order`, `orderby`, `m=YYYYMM` — reordered the results so that draft and password-protected posts, with their titles and dates, surfaced in a public response. No credentials, no exploit code, just URL parameters against a query that trusted its own ordering. Drupal has the same class of failure on record. The JSON:API module's advisory SA-CONTRIB-2018-081 describes filtered collection GET requests that did not fully check access, letting a remote unauthenticated attacker bypass access control; the fix had to add new filter-access hooks. In both platforms, the entity's own access rules were correct — the collection query around them was the hole.
Metadata leaks without the body leaking
Even a correctly patched CMS exposes more through machine-readable endpoints than most site owners assume. WordPress's anonymous REST endpoints — `/wp-json/wp/v2/posts`, `/media`, `/users` — hand out author IDs, usernames and slugs, publish dates, and media upload paths, as a walkthrough of wp-json data exposure lays out. Post bodies can stay private while the metadata around them maps your author roster and content cadence for anyone doing reconnaissance.
RSS feeds carry the same risk in a less obvious wrapper. A feed generator, or a plugin extending one, can inject author usernames, internal category names, contributor IDs, or custom-field data into public XML; Hostragons' RSS security guide notes that an exposed real admin username then feeds directly into brute-force attempts against the login form. This is the answer to a common false comfort: "the draft URL 404s, so we're fine." The draft URL is one output. The feed, the API index, and any plugin-added endpoint are others, each with its own idea of what to include.
What a correctly configured system actually guarantees
The defaults in current platforms are sound, and it helps to know what they promise. WordPress's REST API returns only published data anonymously; per the REST API Handbook FAQ, an anonymous request for `status=draft` is rejected outright — a `rest_invalid_param` error or a 401 — because non-public statuses require an authenticated user with edit capability. Core's native `wp-sitemap.xml` was likewise designed from its proposal onward to include only publicly viewable content, excluding draft, pending, and private posts. So on stock, patched WordPress, drafts do not appear in the sitemap or the REST index.
The exposure risk lives in the deviations: an unpatched core (any pre-5.2.4 install is still vulnerable to CVE-2019-17671), a plugin that registers its own feed or endpoint without repeating the status check, or a custom query that filters by taxonomy or date and forgets `post_status`. Some architectures remove the failure mode structurally. Contentful puts unpublished content behind a separate Content Preview API on its own host with its own token; a production delivery token cannot read drafts at all, a split Contentful states is explicitly meant to prevent accidental draft leaks. That design is worth copying in headless builds: preview credentials should be a different secret, never shipped to production clients.
How to check your own site in fifteen minutes
Test the outputs, not the settings screen. Create a throwaway draft with a distinctive title, then check every public surface for it while logged out: Two caveats on interpreting results. A clean check today only covers the plugins active today; a new feed-generating or API-extending plugin reopens the question, so retest after adding one.
And a CDN or security proxy answering in front of the CMS can mask what the origin actually serves — an error page from the edge tells you nothing about what the CMS would have returned, so verify against the origin when you can. The single highest-value habit remains the boring one: keep core and API modules patched. Both flagship incidents here — WordPress's `?static=1` draft exposure and Drupal's JSON:API access bypass — were fixed in routine security releases, and every site that applied them promptly was never exposed.
- Fetch `/feed/` (and category feeds like `/category/news/feed/`) and search the XML for the draft title.
- Request `/wp-json/wp/v2/posts?status=draft` anonymously; anything other than a 400/401 rejection is a problem.
- Open the sitemap index and confirm the draft's URL appears nowhere.
- Check `/wp-json/wp/v2/users` and your feed's `<dc:creator>` fields for real login usernames rather than display names.
- If you run Drupal with JSON:API, confirm the module is current — the SA-CONTRIB-2018-081 class of bug was in filtered collection requests, so test a filtered query, not just a node URL.
Frequently Asked Questions
Does password-protecting a post protect it better than draft status?
Not against query-level bugs. The 0day.work proof of concept for CVE-2019-17671 pulled password-protected posts into public responses through the same ordering parameters that exposed drafts.
Is disabling the REST API a good fix?
It's usually overkill — the block editor and many plugins depend on it, and per the WordPress REST API FAQ the API already refuses anonymous draft requests. Targeted steps like restricting the users endpoint address the actual metadata leaks.
Do drafts appear in Google because of the sitemap?
Not from WordPress core's sitemap — it was designed to exclude draft, pending, and private posts. If a draft URL is indexed, look for a leak elsewhere: a plugin feed, an old vulnerability window, or a link shared from a preview.




