As briefly discussed in the comments, WordPress is built to promote and provide several interfaces for it’s content such that it is possible to discover and access posts in a number of different ways. This includes (but is not necessarily limited to) things such as
- “Meta tags” embedded within every page’s markup to assist other sites, services, and crawlers in interpreting and navigating the site.
- The REST API which facilitates direct programmatic access to the data.
- WordPress’s core functionality of turning key/value pairs in the URL (specifically, the “querystring”/”search string”) directly into a query for posts.
- The actual face and UI of the site presented to end-users (all the navigation links and buttons and such).
Because of the diverse means by which content might be discovered and/or accessed, it’s not very efficient to try and restrict access by way of altering each interface. And there exists the possibility that a plugin or future core development could introduce additional interfaces for the content, which means you may need to keep an eye out for such changes in order to keep things on lockdown.
Roles & Capabilities
As with most coding endeavors, there are many ways to achieve the desired outcome. I suggest Roles & Capabilities because I feel leveraging that mechanism for this purpose makes the most canonical sense, and should be broadly compatible with well-written extensions. But this is not an assertion that there are not other, or even easier solutions available.
Luckily, WordPress’s Roles & Capabilities API provides a mechanism to solve this problem. By grouping users into Roles and telling WordPress what those Roles can access and how they can interact with those things (the Capabilities assigned to a Role, compared against the Capabilities which are assigned as requirements to content and interactions), WordPress will enforce these restrictions across all of it’s interfaces. If a user’s Roles lack the Capability to see some selection of posts, then these posts will be hidden from the user across all interfaces – no manner of navigation will betray their existence to the user, and a user’s attempt to access restricted content directly will be denied.
The immediate obstacles to using this approach to fulfill your needs are that WordPress does not ship with a robust UI for managing Roles and Capabilities, and out of the box the Roles and Capabilities mechanism does not have functionality to restrict Capabilities on a per-term basis (i.e. individual categories). But like much of WordPress, the Roles and Capabilities system is itself extensible.
So you will need to either find a plugin which may provide these things for you, or at least the latter issue must be addressed by custom code.
Premade Plugins
I don’t have any experience with such plugins so I cannot provide a well-informed suggestion, but I will say that most of the free plugins which I found in a brief search only provide an interface for the core ability to restrict a user’s management of a whole taxonomy – that is, whether they can add or edit or assign individual terms – and they do not implement the per-term access restriction which you are looking for. I do see some premium options providing such an implementation however, such as AAM and Restrict.
Custom Development
To code such a feature by hand would likely be somewhat convoluted owed to the learning curve associated with the Roles and Capabilities system, but broadly, I think it might be fairly directly accomplished by adding a list of Roles to terms’ meta-data, then comparing those roles against a user’s within a map_meta_cap
filter, producing either the current post’s read_post
meta capability or the do_not_allow
capability to enforce whether or not they can read the post. I think this would be somewhat hackish, but could likely be made a whole lot more robust by introducing new primitive and meta capabilities for terms.