Multisite cross-query singular post types



Hi there,

for a WordPress Multisite project I need to be able to query single posts (custom post types) from another site in the network as it would be content of the current site:

  • Site A has 20 products
  • Site B has 5 products

On both sites I need to get the 25 total products, what is already solved through custom queries and @foreach usage in the archive pages. However the big issue I have is that I can’t get the single posts (products) loading on the other sites without getting 404 errors.
Do you know a possibility to hook into a single post query to load it from the other site or to modify sage so that it accepts post from another site?

Beside the pre_get_posts() hook which also doesn’t seem to solve the 404 I tried to use the template_redirect action like this:

add_action('template_redirect', function() {
global $wp_query;
if($wp_query->is_main_query && !is_admin()){
$wp_query->is_404 = false;

This avoids the 404 but loads index.blade.php instead of the needed single-products.blade.php and also doesn’t set the correct body_classes.

Looking forward for some hints. Thanks a lot in advance,



How does it “have” them? Are the IDs for these products stored on the page somehow (i.e. through a meta field(s))? Or is the “page” in question a category/tag/taxonomy index?

Can you show us the code you’re using to do this?

If you’re trying to collect posts from other blogs in a Multi-site network, you could use switch_to_blog() to collect all the posts you need (possibly in a controller?) before sending them to your Blade. It’s a little unclear to me what exactly you’re trying to do here, though.



Thank you for you answer, @alwaysblank.
Sorry, my post was misleading as I used the term “page” instead of “site”. In both cases I mean a single site in the Multisite network. I corrected it in the post above.

I’m heavily using Controller to build custom queries such as this one:

    public function products($add_args = []) {
    $args = array(
        'post_type'  => 'products',
        'post_status' => 'publish',
        'posts_per_page'  => -1,
        'orderby' => 'menu_order',
        'order' => 'ASC',
        'en_fallback' => true // Fallback to EN

    $args = array_merge($args, $add_args);
    $products = new \WP_Query($args);

    // Add fallback link to objects
    foreach ($products->posts as $product) {
        $product->link = Polylang::get_permalink($product);
        $product->title = get_the_title($product->ID);
        $product->markets = SingleProducts::get_product_markets($product->ID);
        $product->technical_specifications = SingleProducts::get_product_taxonomy( $product->ID, 'technical_specifications' );
        $product->technologies = SingleProducts::get_product_taxonomy( $product->ID, 'technologies' );
        $product->key_strengths = SingleProducts::get_product_taxonomy( $product->ID, 'product_key_strengths' );
        $product->options = SingleProducts::get_product_taxonomy( $product->ID, 'product_options' );
        $product->categories = SingleProducts::get_product_taxonomy($product->ID);
        $product->main_category = isset($product->categories[0]) ? $product->categories[0] : [];

        // Add a string of all filter slugs
        $product->filters = implode(' ', array_map(function($filter) {
                if(is_object($filter)) {
                    return $filter->slug;
                } else {
                    return $filter;
            }, array_merge(


    return $products->posts;

The query arg en_fallbackis used within a pre_get_post filter which allows me to have English as fallback language with Polylang.

And this is working just fine as I can use $products in my blade templates to loop through the products.

As explained before I need to mix/merge products from both pages and to not only show them as links to the other page, but to have them queried as they would be part of the page to which they actually do not belong. For this I can of cause adjust the function above to loop through each site via switch_to_blog() but that still does not solve the issue that I need to use single_products.blade.php to recognize that there is a post to show instead of going to 404.

So let’s say I’m on Site A and open the URL of a product which is saved on Site B I end up on 404.
So I’m wondering how I could hijack that behavior to get Sage recognizing that there is a product to show with all its functionalities.

So opening the product url as explained above should recognize “hey, there is somthing to show” and open the correct template single-products.blade.php with the body_classes needed to to use JS routes, etc.

I hope that makes it more clear and again I’m looking forward to get some hints.

Thanks in advance,



I was able to solve the single query issue by using:
Using a pre_ge_post action like this

add_action('pre_get_posts', function( $query ) {
if ( !is_admin() && $query->is_main_query() ) {
    if(in_array($query->get('post_type'), ['products'])) {
        $query->set('multisite', '1');
        $query->set('sites__in', [1, 2]);

I’m now able to query the single post on the other site. Huurray :heart_eyes:

However the last issue I now have is that I somehow need to switch to the other site via switch_to_blog() withing my Controller in SingleProducts.php as it currently tries to get all the fields from the current site. I guess the Controller runs before the while(have_posts()) the_post() starts.

So the question is now: Is there a way to let Controller switch to another site or is there a possibillity to rebuild the Controller variables within the_post()?

Thank you again