Why a hidden product quietly stops selling
Catalog visibility is one small control in the Publish box of the product editor, and it is easy to set by accident — during an import, a bulk edit, or a temporary campaign that nobody switched back. Once a product is Hidden, nothing looks broken. The page loads, the price is right, and the orders simply stop.
- Browsing never reaches it. The product is missing from the shop page, from every category and tag archive, and from related-product blocks, so a shopper who does not already hold the link cannot stumble on it.
- On-site search skips it. A hidden product is excluded from WooCommerce search results, so even a shopper who types its exact name gets nothing back.
- Reports do not flag it. WooCommerce counts a hidden product as published and in stock. Its stock is held, its price is maintained, and no dashboard warns that it is invisible.
- Sometimes it is on purpose. Link-only offers, bundles built from other items and products still being prepared are hidden deliberately, which is exactly why the accidental ones hide so well among them.
How to find hidden products manually
WooCommerce stores catalog visibility as terms in the
product_visibility taxonomy: a hidden product carries both
exclude-from-catalog and exclude-from-search.
This WP-CLI query lists every published product that has both:
wp db query "SELECT p.ID, p.post_title FROM wp_posts p JOIN wp_term_relationships rc ON rc.object_id = p.ID JOIN wp_term_taxonomy tc ON tc.term_taxonomy_id = rc.term_taxonomy_id AND tc.taxonomy = 'product_visibility' JOIN wp_terms c ON c.term_id = tc.term_id AND c.slug = 'exclude-from-catalog' JOIN wp_term_relationships rs ON rs.object_id = p.ID JOIN wp_term_taxonomy ts ON ts.term_taxonomy_id = rs.term_taxonomy_id AND ts.taxonomy = 'product_visibility' JOIN wp_terms s ON s.term_id = ts.term_id AND s.slug = 'exclude-from-search' WHERE p.post_type = 'product' AND p.post_status = 'publish' ORDER BY p.ID;"
Adjust the table prefix if yours isn't wp_. A product with
only one of the two terms is Shop only or Search results only, not
Hidden.
The query answers the question once. It gives you a list of IDs, not the context to judge them — which of these were hidden last week by a bulk edit and which have been link-only offers for years. And nothing runs it again after the next import or plugin update, so a product that drops out of the catalog next month drops out silently.
How CatalogLift finds it
Every scan reads the catalog visibility of each published product in the synced catalog and reports the ones whose value is exactly Hidden, at medium severity. It does not touch drafts or private products, and it does not report Shop only or Search results only, because those products can still be reached one way or the other. Each finding names the product and carries the visibility field and its value as evidence, so you see at a glance what the scan saw.
Because hiding is a legitimate choice, this check reports rather than accuses, and the fix is a confirmation you make inside CatalogLift. There is no AI involved: you decide the product should sell, approve the change, and CatalogLift sets its catalog visibility back to Shop and search results in WooCommerce on that decision and never before it. The products you leave alone stay exactly as they are. The next scan verifies the change landed, and because CatalogLift keeps watching, a product hidden by a future import becomes a fresh finding instead of a silent gap in your sales.