Throw 404 inside composer

I have some page templates that run some custom logic to pull data from custom tables, I want to throw 404 incase query returns nothing inside Composer. Is there a way to do this in a clean way?

1 Like

Is there a particular reason why you would want to throw a 404 vs just display a message along the lines of “There’s no content here that meets that criteria”? From what I understand, you generally want to avoid a site returning 404s unless it really needs to—I think search engine crawlers can treat 404s poorly in terms of penalizing your site, and (depending on the context) it can also be confusing to your users if it’s a query that doesn’t return anything now but might in the future.

I have paginated content, but I can’t prevent user from setting page number that does not exist in the URL, and ideally I would throw 404. Generally having 404 is better as you can notice that you are linking to non existing page if you rely on SEO tools.

I’m coming from Laravel background, where I would use findOrFail and throw 404 if nothing is found.

1 Like

I need this, too…I have a custom query var that i use within a specific template and that I use to pull data from outside of wp…I need to 404 if that external data does not exist…I guess within the view composer we are “too late” in terms of hook/action order to throw a 404?

I solved this for my case by outsourcing my external data fetching from my view provider into a service, injected by a serviceprovider so I can use it in my view provider as well as in a filter for “template_include” which is called before rendering any views, so I am able to intercept it and throw a 404 if the external data is missing…

@sammyaxe I think if your pagination value is available before rendering the views (i.e. through a query var) intercepting it through an earlier filter like “template_include” or “template_redirect” this could be a solution for you as well…

1 Like

I believe you would just need to call header("HTTP/1.0 404 Not Found"); and output the content you wish to have for your 404 page.

This is true, but if you output the same content on a page that is returning 200, a soft 404 (or crypto 404s as I found out looking for resources), it is worse than having a hard 404 which explicitly states that there is nothing found there. it could blow through your indexing budget and slow down indexing and ranking in search.

In the case of pagination as you mention you might look into redirecting to the root for any pages that don’t exist. This is actually how WordPress handles its paginated posts since like 5.5 (I found out because a site that used to load fine with custom pagination began redirecting to the first page because of these [47727] / #40773 or [47760] / #45337)

If it is truly a 404, like you do custom content routing for the page content, you might even be able to pull in the post content from your 404 page. Like this

if ($wp_query->is_404) {
    header("HTTP/1.0 404 Not Found");
    $page_title = $this->options['404_page_title'];
    // Override the current post to the 404 page
    $GLOBALS['post'] = get_page_by_title($page_title);
}

UPDATE: Just did a quick test, I was wondering if the header can be changed from a composer, if it were just using Views then it wouldn’t be an issue, but in Sage 10 it has some content in index.php for compatibility with plugins, unfortunately it seems that output will have already started by the time composers are called. I think you might be able to work around this with ob_start and ob_end_flush with these changes

Index: web/app/themes/sage/index.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/web/app/themes/sage/index.php b/web/app/themes/sage/index.php
--- a/web/app/themes/sage/index.php	
+++ b/web/app/themes/sage/index.php	(date 1666885403532)
@@ -1,3 +1,6 @@
+<?php
+ob_start();
+?>
 <!doctype html>
 <html <?php language_attributes(); ?>>
   <head>
@@ -22,3 +25,5 @@
     <?php wp_footer(); ?>
   </body>
 </html>
+<?php
+ob_end_flush();
2 Likes