Headless WordPress menus: what to check before launch

Illustrated infographic summarizing: WordPress headless menus need an exposure check before launch

By Greg Nowak. Updated 1 September 2026.

A headless WordPress site should not need hard-coded navigation. If editors manage the pages in WordPress, letting them manage the primary and footer menus there usually makes sense too.

But once navigation is delivered as JSON, it becomes part of your public application contract. Menu labels, URLs and hierarchy can reveal planned services, campaign pages, account routes, partner areas or internal terminology—even when the linked pages themselves remain protected.

This does not make public menus inherently dangerous. It means the agency or delivery team should decide what is public, test that decision anonymously and record it before launch.

What changed in WordPress 6.8?

WordPress 6.8 introduced the rest_menu_read_access filter. The hook remains in the current WordPress developer reference and applies to the controllers for menus, menu items and menu locations.

By default, these resources are not available for anonymous reading. A developer can opt in and can use the request and controller passed to the filter to grant access selectively. This broad example from the WordPress developer note exposes all three menu-related resources:

add_filter( 'rest_menu_read_access', '__return_true' );

That line is convenient, but it is a policy decision disguised as a small implementation detail. Use it only when every menu resource and its public fields have been reviewed and are genuinely intended for anonymous access.

Start with the front end’s actual data requirement

Map the requests the production application makes. A typical front end may first resolve a location such as primary, obtain its menu ID and then request the associated items. Exposing only the location endpoint may therefore be too narrow, while exposing every menu and every item may be unnecessarily broad.

The right rule follows the fetch sequence. Identify the locations the application needs, the item collection it reads and any fields added by plugins or custom code. Then implement conditional logic in rest_menu_read_access around those specific requests. Keep that logic in a small custom or must-use plugin so it is not silently lost when the theme changes.

Launch decision What to verify Expected evidence
Public routes Menus, items and locations required by the production front end An explicit route list, not “the REST API is open”
Public content Labels, URLs, hierarchy, descriptions, metadata and custom fields A reviewed anonymous JSON response
Failure behaviour Restricted routes and unapproved menu IDs A repeatable 401 or 403 response
Browser access Production, preview and old development origins A documented CORS decision
Cache lifecycle CDN, host, application and framework caches Known TTLs and a tested purge path
Ownership Who approves new locations, fields and integrations A named owner and deployment note
A focused acceptance checklist for public menu data in a headless WordPress launch.

Test as an outsider, not as an administrator

Run the launch checks without WordPress cookies, authorization headers or preview credentials. Replace the host and menu ID below with production-like values:

curl -i https://cms.example.com/wp-json/wp/v2/menu-locations/primary
curl -i 'https://cms.example.com/wp-json/wp/v2/menu-items?menus=MENU_ID&context=view'
curl -i https://cms.example.com/wp-json/wp/v2/menus

Record the status code, response headers and body. Approved requests should return only content the business is comfortable publishing. Unapproved collections, locations and IDs should remain inaccessible. Repeat the tests against the real production configuration because hosting rules, plugins and deployment variables can differ from staging.

WordPress also supports the global _fields parameter, which lets the front end request a smaller response. For example, it can ask only for the item fields it renders. This improves payload size and makes the consumer’s needs clearer, but it is not access control: another caller can omit _fields. Review the complete anonymous response, not only the front end’s preferred query.

Review the menu as publishable content

A technical permission check is only half the job. Ask the content or operations owner to inspect the returned navigation shortly before launch. Look for staging domains, placeholder links, unpublished campaign names, obsolete support routes and internal descriptions. Check plugin-added metadata as carefully as the visible label.

Also test edits. Remove or rename a harmless item, publish the change and measure how long the old JSON survives. Navigation may be cached by WordPress, the host, a CDN, the front-end framework or several layers at once. The team needs a practical purge method before an urgent correction is required.

CORS does not make public menu data private

WordPress intentionally allows public REST endpoints to be accessed from any site by default; its REST documentation explains that WordPress relies on nonces for CSRF protection rather than strict origin verification. CORS mainly controls which browser scripts can read a cross-origin response. It does not stop direct requests, server-side clients or command-line tools.

Review CORS because overly broad or stale configuration is still poor operational hygiene, but do not use it as the confidentiality boundary for menu data. If navigation truly must remain private, keep the endpoint authenticated and fetch it through a trusted server-side component. Never place an Application Password or another reusable secret in browser JavaScript.

Leave the next team a clear rule

The most useful handover is short and testable: which menu locations are public, which routes support them, who may change the policy, how anonymous access is tested and how cached responses are purged. That turns a one-line filter into an owned part of the system rather than launch-day folklore.

If your agency needs an independent pair of eyes, Greg can review the anonymous REST surface, menu contents, cache path and deployment notes without turning the exercise into a heavyweight security programme. Talk to Greg about a practical pre-launch review.

Related on GrN.dk

Need help with this kind of work?

Plan a pre-launch review with Greg Get in touch with Greg.

Sources

Latest articles

PHP 8.2 security support ends on December 31, 2026. Here is how to audit, test, and migrate a mixed CMS estate without rushing production changes.

How Danish businesses can automate Gmail and Microsoft 365 with rapid sorting, limited permissions and human approval.

When WordPress jobs run late, check WP-Cron and queue capacity first. Diagnose triggers, handlers, and Action Scheduler without guesswork.

WordPress 7.1 makes speculative loading configurable. Here’s how to spot overlapping rules and test speed gains without adding hidden costs.

Multiple records for the same customer in HubSpot? Learn how CVR number matching, AI suggestions and human approval can help you clean up duplicates while keeping track of fields, associations and customer history.

Before a Google AI shopping pilot, check which products qualify, where your catalog data disagrees, and whether checkout reflects your delivery and return terms.

Check whether prompt caching reduces cost per completed task, accounting for cache writes, retries, review effort and the charges on your provider's bill.

A practical Drupal translation workflow for Danish service pages: German review, commercial approval, publication and keeping translations current after edits.

Build a weekly marketing report from GA4 and Google Ads with verified calculations, clear data caveats and a short AI draft to support your Monday meeting.

Before buying a GPU, test one real team workflow on existing hardware. A Linux pilot can show whether quality, memory, response times, and running costs add up.