Embed Privacy – a privacy-friendly two-click solution for embeds

Two days ago I wrote about Complianz, the plugin I have used to implement a cookie banner. In this blog post, I’ve mentioned that I have removed any cookies from this blog and that I am using a two-click solution for any embed. This is where the Embed Privacy plugin comes into play.

What does Embed Privacy do?

If you want to embed some external resource in WordPress, you often simply paste the URL to this resource, like a YouTube video, into the editor, and WordPress will automatically embed it using the oEmbed format. This is really handy, but it also means that as soon as someone visits this page, the external resource will be loaded, and personal data might flow to this external service. To comply with the GDPR, CCPA, and similar regulations, this is often not allowed. You need consent from the person to show the embedded resource and (potentially) send data to this external service. You may get this consent through a category of your cookie banner, or you let them approve the embed. This is what Embed Privacy allows you to do. It automatically replaces many embeds on your site with a “two-click solution”, meaning that the first time someone sees an external embed, they are asked if they want to view it. Here is an example of a YouTube embed from a recent blog post:

Screenshot of the YouTube embed replaced by the Embed Privacy plugin.
This is just a screenshot of how it looks like. Clicking on this image does do anything. Find the video in the “How to permanently delete a contact in Mailchimp” blog post.

To display the video, you can either click on that “overlay”, or you can choose to always display YouTube embeds on the page. There are also links to open the privacy policy of the embedding service and a link to open the external resource. Embed Privacy also offers an option to download the “placeholder” image from the external service, if this is available. Those placeholder images are stored/cached into the folder wp-content/uploads/embed-privacy/thumbnails/ on your server, so those as well do not load anything from the external server by the person visiting your page, making it a privacy-friendly option.

Why do I use Embed Privacy?

As mentioned earlier, I do not like to have a cookie banner on websites, if there are other alternatives. And in my opinion, Embed Privacy is the perfect plugin to implement two-click solutions for many embeds. If you need to embed a service that it does not handle already, you can add your own services. I’ll probably write a blog post on how to do that in January.

Another reason I like this plugin is that fact that it is developed and maintained by the German developer Matthias Kittsteiner under the Epiphyt brand. I may, or may not, write about another of his plugins. Maybe as soon as tomorrow. ๐Ÿ˜‰

Conclusion

If you are like me, and you want to use a two-click solution for embeds, why not give Embed Privacy a try? Are you already using it or do you use a similar solution, then please share your thoughts in a comment.

Contact Form 7, CF7 Apps and Flamingo – three plugins for simple forms

It’s still surprising that Contact Form 7 has so many installations, but it is the third most installed plugin on the WordPress Plugin Directory. It is rather “technical”, since you have to build your forms using shortcodes. Only integrating it into a page also works with a block nowadays.

What do the plugins do?

The Contact Form 7 plugin is a form plugin, so it’s used to provide a form on a website that sends an email. It usually sends an email to the owner of the website, but it can also send a second mail, that is usually used to send a copy to the person that filled out the form. Mails are sent using the wp_mail() function, so it needs a web server that is configured correctly.

To prevent form spam, you can use Contact Form 7 with Akismet, Turnstile from Cloudflare and reCAPTCHA. If you want a more privacy-friendly alternative and one that is not using an external service, you can add “honeypot fields” to your form. The plugin I use here is now called CF7 Apps (previously it was called Contact Form 7 Honeypot). It does offer more that just honeypot fields, but this is what I mainly use it for. You can add multiple honeypot fields, and in many cases, this is enough to prevent most form spam.

Another thing that Contact Form 7 does not do by default is storing the mails it sends. You can either use one of the generic “mail logging” plugins, that would log any mail sent by WordPress, or you can use the Flamingo plugin, which was created by the developer of Contact Form 7. It’s a very minimalistic plugin that shows you an “address book” and a list of mails that have been sent with the forms from Contact Form 7.

Why do I use these plugins?

I use Contact Form 7 in combination with CF7 Apps on two websites that I maintain. On one of them I also use Flamingo. Since both of these pages only need simple forms and I know how to use Contact Form 7 to create usable and accessible forms, installing a “more mature” forms plugin feels like an overkill to me.

Conclusion

If you can handle editing a form using shortcodes and only need simple forms without things like multiple page forms, conditional fields, etc., then Contact Frm 7 might be all you need. Many themes even come with some better default styles, since it’s so widely used. And there are also many plugins adding additional functionality to Contact Form 7, like the other two plugins I’ve presented in this blog post.

Do you also use Contact Form 7? Maybe with other additional plugins? Or do you use a larger form plugin? Then please share your thoughs in a comment.

Complianz – a good option for a free cookie consent banner

I don’t like cookie banners, but who really does, right? On this blog, I’ve removed everything that would need any “consent management tool” and switched to either cookieless solutions or two-click options. More on that in the following blog posts.

But on some site, you do need a consent tool. Then you have the option to choose between a wide range of different tools, both free and premium. Many large sites even use complete external services, that might not even come in the form of a WordPress plugin. Some solutions that are WordPress plugins are either exclusively premium or offer a free version, that is so limited, that it can’t really be used. This is where I’ve found the Complianz plugin as one that is indeed usable in its free version.

What does Complianz do?

Well, it’s a cookie consent banner, so it tries to help you to have your site only set cookies, when they are either necessary or consent was given to set them. It can also block external embed, like videos from YouTube, Vimeo and other streaming portals or embeds of Google Maps, Twitter, Instagram, and so on. It also records the consent given, to comply with regulations like the GDPR.

It offers a “cookie scanner” that runs on your blog, and not through a (paid) external service, trying to find all the cookies your site is setting. You can then put those cookies in different categories or add custom ones. It also creates a dynamic “Cookie Policy” page listing all the cookies in the different categories.

There is also a premium version of the plugin that offers more advanced things like statistics, A/B testing, geo IP consent, more documents, and more. But for a simple website that only cares about allowing visitors to accept certain cookies, those features are not really necessary in my opinion.

Why do I use Complianz?

From all the different cookie consent plugins I have tested, this one serves my needs best in the free version. If I don’t have a budget for premium solutions, Complianz is the plugin I would use. If I need to use one of these solutions in the first place.

Conclusion

If I can decide on the services used on a website, I would always try to chose only those, who are privacy-friendly and not set any cookies or load external data. But if I do have to use such services, I will use a plugin like Complianz, that is free and runs on the website itself, and not a paid external service, to implement a cookie consent banner.

Classic Editor and Classic Widgets – two “legacy plugins” for older WordPress installations

Those two plugins might come as a surprise for some of you. I’m using the Block Editor, since it was still in a beta state and only available through the Gutenberg plugin. So I am not the usual user of the Classic Editor plugin. But I do have two sites I maintain where I’m using it.

What do the plugins do?

Both Classic Editor and the Classic Widgets plugin are used to preserve the “legacy components” from older WordPress versions that would nowadays use the Block Editor interface. The Classic Editor plugin allows you to either “deactivate Gutenberg” and use the old TinyMCE editor for your content, or you can decide on a per content element basis which editor you want to use.

The Classic Widgets plugin on the other hand brings back the old “widget admin area”, that was replaced with the “Gutenberg UI” in WordPress 5.8 and which now allows you to use any block in any widget area.

Why do I use both plugins?

The Classic Editor plugin is used on the podcast website I host. The podcast plugin I use there (this one will be presented in another blog post) does not fully work in the Block Editor. All “meta boxes” do work, but the editor crashed, at least for existing episodes.

I also use the plugin on a website of an acquaintance. That acquaintance is not a “technical person” and the new UI in the Block Editor for some plugins make it harder to create and edit content.

The Classic Widgets plugin is used on this very blog. I still use a theme that was written around 15 years ago, and that is using some widgets and widget areas that don’t work well with the new “Block Editor Widget Area”. As soon as I finally replaced the theme with an FSE one, I can remove this legacy plugin as well.

Conclusion

Even though I am a big advocate for the Site and Block Editor, I do see cases where plugins like Classic Editor or Classic Widgets are necessary. It should be our goal to make these plugins obsolete, especially if we develop plugins ourselves, but the reality is, that they will still be around for a couple of years.

Are you also using the Classic Editor? And do you it because you don’t like the Block Editor, or also because you have to deal with legacy websites?

Block Editor: Reverse Columns on Mobile – a small block options plugin

The plugin I share today has two extremes. It is the plugin with one of the longest name, “Block Editor: Reverse Columns on Mobile“, but the least amount of code. The PHP code only loads some JavaScript and CSS. All in all, it has some 250 lines of code combined.

I use it on one site that tries to use “no code”. The content and design is almost exclusively build with the Site and Block Editor.

What does the plugin do?

As the name says: it reverses columns on mobile. Let’s imagine you have a page with multiple “Media & Text” block. For a nice layout, you place the image on the left for the first one, on the right for the next one, then again on the left and to on. If you now view the page one mobile, by default, those blocks would always place the left column above the right one. So you would end up with something like this: image, text, text, image, image, text, text, image, …

This doesn’t really look nice on mobile. Instead, you probably want to have the image always above the text or always below the text. You could fix this by manually adding a custom CSS class to every other block and then use some custom CSS in your theme (or the Site Editor global styles), or, you can simply use this plugin, that adds a checkbox to the core/columns, core/group (flex layouts), and core/media-text block.

Why do I use the plugin

As mentioned earlier, I do use it on a site that tries not to use custom plugin and/or theme code. It should also allow anyone editing the content to easily use this functionality. For my personal blog and websites, I would probably just introduce a custom CSS class that I would add to such blocks. But why always “reinvent the wheel”, when there is a plugin that just does what it’s supposed to do, and nothing more. The CSS even takes care of RTL (right to left) languages/designs and different ways to arrange elements next to each other. I would probably miss some of those cases in my own custom CSS class.

Conclusion

Even though some people argue that having fewer plugins is better, I really like plugins like this one, that solves one thing the exact way, I would have implemented it myself.

Have you also found a plugin that improves small things in the Block or Site Editor? Then please share them in a comment with us.

BackWPup – my go-to plugin for backups

I can’t remember when I discovered BackWPup and for how long I’ve been using it. Back then, it was soley developed by Daniel Hรผsken. When he realized that he does not have the time to work on the plugin just by himself anymore, he joined Inpsyde, the agency I currently work at (we are now called Syde), and brought the plugin with him. A little more than two year ago, the plugin was acquired my WP Media, the company behind plugins like WP Rocket, Imagify, and others. Even though many didn’t like the new UI WP Media introduced, I still use the plugin on many of my website.

What does BackWPup do?

It’s a backup plugin, so it is used to back up your site. In the free version, you can send backups to different targets. It supports:

  • Email (which really only works for database backups)
  • Microsoft Azure
  • S3 providers
  • Dropbox
  • Rackspace
  • SugarSync
  • an external FTP server
  • and the same server (which is not a good place for a backup)

In the premium version, it can also back up to more targets:

  • Google Drive
  • Amazon Glacier
  • HiDrive
  • OneDrive

In the past, restores were only available in the premium version, but they are now made available in the free version as well. But since I know how to restore a backup from files, I never used the restore functionality, as I don’t really know what it would all do. A recent version also added a migration feature, which I have never tested.

Why I use BackWPup?

There are many backup plugins out there. One reason I might (still) use BackWPup might be, that it was the one I first discovered. But I also like one of it’s core features: being able to set up multiple backup jobs. Many other backup plugins only have one job (one set of settings) which can run on a schedule. With BackWPup, you can set up as many backup jobs as you want. I have one that runs daily and that backs up the database. Another one does a full file system backup. For each job, you can decide where the files should be stored. So, you could have the database stored on an external service and sent to you by mail (if it’s really small), while the files are stored on an external storage that can hold a lot of data.

What are the alternatives I’ve tried?

On some websites, mostly those I do not maintain alone, we use UpdraftPlus (the free version). While it offers an easier UI and had a restore functionality in the free version for years (maybe even from the very beginning), I don’t like the limitation to only one backup job. It also only backs up the folders in wp-content by default and stores them in multiple ZIP files you need to download and extract separately. I’ve heard that the premium version can back up your whole installation, but since BackWPup also does that, there is no reason for me to switch, since I also don’t need a restore functionality. The only thing it offers in the free version that would be nice in BackWPup is the support for Google Drive.

Conclusion

Since I want to have multiple backup jobs and don’t need a plugin with a restore functionality, I have chosen BackWPup as my primary backup plugin many years ago and am still using it. Restoring from the (single) ZIP file is rather easy, if you work on the terminal anyways. If you use Local, you can even set up a local environment from the ZIP file directly. So if you are still looking for a (plugin) backup solution, give BackWPup a try.

Antispam Bee – The essential spam blocking plugin for every blog

This is the first blog post of my “plugin advent calendar“. In the next 24 days, I want to cover plugins I use on my personal websites and websites I help maintain. I hope that I’ll be able to publish each of them at midnight every day.

Since I want to cover them in alphabetical order (with two exceptions), the first one had to cover one of the plugins I see as essential for any blog: Antispam Bee.

This plugin was developed by Sergej Mรผller and is now maintained by the Pluginkollektiv. Since I’m a member of that group as well, I also help to maintain it.

What does Antispam Bee do?

It blocks comment spam. It’s as simple as that. And it does it really really well. It has many different tactics to recognize spam comments and only from time to time, I have to moderate a pending comment as spam, since some spam comments are written manually by real people using comment content that makes sense to the specific blog post.

Does Antispam Bee also block other types of spam, like form submissions or sign-ups, you might ask? No, it currently does not. This was never a focus of the plugin. It only helps with comment spam, but here it does shine.

But this on only partly true. The Pluginkolletiv, so me as well, is working on a new version. This new version would also only work for comments out of the box. But it will offer an “API” that can be used for other things than comments. You use the “Contact Form 7” plugin for example to add a comment form to your blog? There might be a (third-party) plugin to integrate Antispam Bee with Contact Form 7 in the future.

Work on that new version was started some years ago, and since introducing a new version for such a successful plugin comes with a lot of risks, we really want to make sure it works for existing sites – Antispam Bee has currently 700,000+ active installations, and we don’t want to break any of those or make the spam protection less efficient. I’ve spent some time in my last holidays to test the spam protection efficiency of the new “work in progress” version with around 300,000 comments from my blog and the new version captured every single comment the current version did, just in a different (and even better) way. So I am very positive that 2026 will be the year that Antispam Bee 3 will finally be released. ๐Ÿคž

Why I use Antispam Bee?

As I welcome comments on my blog posts – and I would love to see more of you commenting ๐Ÿ˜‰ – I do get a lot of spam comments as well. Right now, I have almost 300,000 spam comments in my database (just for my English) blog, compared to a mere 634 comments that are not spam. You could say, I really need a plugin like Antispam Bee.

But why do I use it and not another plugin. I had been using a different anti-spam plugin for my English blog, as it worked better for comment spam in English many years ago. This was, probably, due to the fact that Antispam Bee was developed by a German developer for his blog and he got more comments in German. But many years ago, I replaced that other plugin with Antispam Bee on my English blog as well. Some of you might use Akismet, which comes pre-installed with WordPress, but unlike Antispam Bee, it is not privacy-friendly, and we Germans have a reputation, that we care a lot about privacy, and so do I.

Conclusion

If you do allow comments on your blog or website, and you do get comment spam, give Antispam Bee a try. Just install it and activate it. The default settings work very well for most sites and is not using any advanced techniques that are not as privacy-friendly. If you already use Antispam Bee, and you like it, why not give it a review on WordPress.org today?

A little status update on my blog

If you are an active reader of my blog, or look at the timestamp of my latest blog post, you’ll recognize that I have not published a blog post in more than 14 weeks. This has different reasons, the biggest one that I became a father in September. Those of you who also care for someone will know, that this takes away a lot of your available time – and it can also be exhausting.

Am I going to continue blogging?

Absolutely! I have committed myself to #project26, but since I only managed to publish 13 posts this year and with just one month left, this might be hard to achieve. But I do have a plan – and I hope it’s going to work out. ๐Ÿ˜…

A plugin advent calendar

Just as last year, I will try to have another advent calendar in December. Last year, I focused on “web technologies”, while in previous years I had “normal topics”. Those topics, however, take a lot of time to write, since I often also write code examples. So to make it a little easier this year, I will pick up the advent calendar idea from KrautPress. Last year, they have published blog posts, each covering one plugin per blog post in their “Advent of Plugins“.

I’ve spent the last week collecting a list of plugin for my advent calendar. Every plugin is used on one of my site, or on a site I help maintain. Some blog posts will cover more than one plugin, since I also use some “add-on plugins” to some larger plugins.

How will the blog continue next year?

I still want to blog regularly. I was told, that as a parent you will have more free time, the older the child gets. Let’s see if this is going to true. But even if I try to publish 26 blog posts, I might not post here until later next year, since my blog posts often a topic that come up at my day job, and as I take a long parental leave period, I might not have anything to blog about. But I do have an idea for a new blog, which will be about a different topic. You might have guessed it already, I want to blog about things that are relevant for parents. ๐Ÿ˜Š

Conclusion

So, starting next Monday, you will be able to read about the first plugin I use regularly on my sites. I will order them alphabetically. Maybe you can guess which one I will cover first. ๐Ÿ˜‰

Fixing slow WooCommerce logins: 15 million order notes and two comments

In the same project I blogged about last week, I was tasked to optimize the performance of the website. One of the major issues was the login performance. Any time I would log in, I had to wait around one minute before I could see the dashboard. But what was wrong with the site, that it was so slow?

An enormous database

When I’ve started analyzing the site, I first had to set it up locally. This alone took me several days! The database was so large, I literally had to upgrade the SSD in my laptop, before I could even think about importing it. The database dump was “only” around 40 GB in size, but after the import, I used up around 100 GB on my SSD. Since I wanted to optimize it and have backups, I copied the whole database twice in the end, as I also did an HPOS migration. So after spending hours installing a new SSD, copying my whole system (a Windows/Linux dual boot), fixing a broken bootloader and running the database import, I was finally ready to start analyzing the page.

Finding the performance bottleneck for the login

After my first login, the Query Monitor plugin showed me two queries that took extremely long. This is what they looked like:

SELECT COUNT(*)
FROM   wp_comments
WHERE  user_id = 123456
AND    comment_approved = 1

Two of those queries took around 30 seconds each. This increased the login time by one full minute. Query Monitor also gave me the info which “Caller” executed those queries. It was the Akismet::get_user_comments_approved function.

The reason for those queries

The shop was using Akismet to fight comment spam. In the “At a Glance” widget on the dashboard, it shows the number of comments that are in the spam queue, and it shows how many are in moderation. In the “Activity” widget, it also shows “Recent Comments”. For any comment that is shown in this list, and that was made by a user/customer of the site, it would get the number of approved comments. The joke is: this value is added to the dashboard widget, but then hidden with a display:none inline style, so it doesn’t even get shown. Those queries still run. Fortunately, it was only two users, and not all five that would be listed, otherwise the login would have been slowed down by two and a half minutes.

Analyzing the issue

Now that we know which query is causing the performance issue, and we know why this query is happening, how can we fix it? Let’s take a look at why this query is so slow.

The shop was a multisite WordPress installation. In one of the shops, we had around 76,600 customers (the other shop had almost twice as much) with more than 83,000 subscriptions resulting in more than 1,442,000 orders. Yes, that’s way more than one million orders, and this is just for one of the sites. So you could say, that this is quite a large shop. Every order has some “meta information”. One of this information is “order notes”, and those are stored as comments in the same table as regular comments. In the comments table, we had a total 15,348,283 comments! Those were the types:

Entriescomment_type
677comment
13876120order_note
859541user_membership_note

Now, when we look at the query causing this slow login, we can recognize that if filters the comments by the user_id and comment_approved columns. Looking at the structure of the table, we have an index comment_approved_date_gmt that can be used for the comment_approved column, but we do not have an index on the user_id column. This means, that MySQL needs to look at all the more than 15 million comments, to find the ones for the user_id we have in the query. And it does that for every single user_id we have. This might take a while.

Fixing the issue

As we now know the reason for that slow query, we can try to find a solution to solve it. There are three solutions that might work:

Solution 1: Delete the comments

This might be a bit too radical, but for this shop it would even work. Interestingly, the two comments slowing down the site were made on the “Checkout” page. That’s not a page you would usually allow comments on. Deleting those to comments (or at least moving them to “Trash”) resolved the login issue, as they were no longer showing up in the widget under “Recent comments”.

Solution 2: Exclude comment types

In preparation of this blog post, I’ve found a filter that can be used to improve the query. For the query to count comments for a user_id, you can exclude comment types using a filter like this:

function my_excluded_comment_types($comment_types) {
	$comment_types[] = 'order_note';
	$comment_types[] = 'user_membership_note';

	return $comment_types;
}
add_filter( 'akismet_excluded_comment_types', 'my_excluded_comment_types' );

Since the order_note and user_membership_note types are the ones we really don’t need, we should exclude them. WooCommerce adds an index for the comment_type column in the wp_comments table, allowing MySQL to filter out some comment types very effectively.

This improves the 30-second query to around 0.015โ€“0.03 seconds only. Quite an improvement already. We only need to make sure to exclude all the comment types we don’t need. Depending on the plugins you are using, there might be other comment types you want to exclude as well.

Solution 3: Add an index to the user_id column

As mentioned earlier, the issue with this query is that we are trying to filter by user_id, but this column does not have an index. Adding one can also resolve the issue. To add an index, run this query on the database (e.g. with phpMyAdmin, Adminer, the WP-CLI or some other tools):

ALTER TABLE wp_comments ADD INDEX `my_idx_user_id` (`user_id`);

If you are using a “prefix” other than “wp_“, change the query accordingly. Be aware that adding an index to a table might take a while. For those 15 million entries, it took 30โ€“35 seconds to create the index. In this time, writing to the table might be slower or even blocked, so better run it at a time with lower activity in the shop.

After adding an index, the initial 30-second query now only takes 0.001 seconds (or even less, since that’s the lowest number I would get when analyzing queries), so it’s even more than 10 times faster as with the previous solution.

Conclusion

Performance issues on large websites and shops are a very different story than for small sites. At least my blog is far away from reaching millions of comments. ๐Ÿ˜

Finding the cause of a performance issue and how to fix it is also not always an easy and straightforward task. Using a tool like the Query Monitor plugin usually helps a lot to find those issues. But finding good solutions can be hard, especially when the component causing the issue does not offer an action or filter to solve it, or you are unable to fix it differently, like adding an index. For the shop mentioned in this blog post, I’ve ended up using solutions 1 and 3, while I had to use a custom WP-CLI command to add the index to the database, because I had no write access using tools like phpMyAdmin or Adminer.

Have you also experienced some “interesting” performance issues with large site or shops? Then please share them in a comment. Or maybe two, but please not a million comments. ๐Ÿ˜‚

Show only WooCommerce products in the default WordPress search

In a project, the design agency had created a theme without a search template (though manual searches were still possible). Now, the shop owners wanted to have a search in the header of the page, but it should only search for products.

Some themes have this implemented in the header search already, like the “default theme” Storefront. Others have the regular WordPress search, which would always search for all post types. Let’s see, how we can change this.

Find only products

In WordPress, you can add ?s=something to any URL and this would search the WordPress site for any public post types, that does not have exclude_from_search set to true in its registration settings. But if we only want searches to find products, we can achieve this in different ways.

Use a hidden field to set the post type

If we look into the source code of the header in the Storefront theme, we will find the following HTML code:

<form role="search" method="get" class="woocommerce-product-search" action="https://woocommerce.ddev.site/">
	<label class="screen-reader-text" for="woocommerce-product-search-field-0">Search for:</label>
	<input type="search" id="woocommerce-product-search-field-0" class="search-field" placeholder="Search products&hellip;" value="" name="s" />
	<button type="submit" value="Search" class="">Search</button>
	<input type="hidden" name="post_type" value="product" />
</form>

As the placeholder text is indicating, the search field is only for products. This is achieved by adding a hidden field setting the post_type to product for this search. When submitted, the search would open a URL like https://example.com/?s=something&post_type=product. This would then only show products and use the archive-product.php template, if available in the theme. This would list the search results in the same way as in the shop (in most themes).

Redirect the normal search to the shop

Even with this modification in place, it’s still possible to search for any other post type, by adding the search parameter to any non-shop URL, as mentioned in the beginning. If we really want to restrict normal search and only allow product searches, we could redirect any normal search to the shop search. This can be done with a snippet like this:

function product_search_only_redirect_search_to_products() {
	if ( is_admin() ) {
		return;
	}

	if ( ! is_search() ) {
		return;
	}

	// We are already at the right search result page.
	if ( is_shop() || is_tax( get_object_taxonomies( 'product' ) ) ) {
		return;
	}

	$search_query = get_search_query();
	$shop_url     = wc_get_page_permalink( 'shop' );
	$redirect_url = add_query_arg( 's', rawurlencode( $search_query ), $shop_url );
	wp_safe_redirect( $redirect_url );
	exit();
}
add_action( 'template_redirect', 'product_search_only_redirect_search_to_products' );

In this callback function to the template_redirect action, redirect any frontend search, that is not already done on the shop or a taxonomy archive page. This prevents any normal search. If we now search on the page, we get redirected to a URL like https://example.com/shop/?s=something and would only see products.

Bonus tips

I’ve opted for the second option, as the shop theme didn’t have a generic search template (in fact it had one, but it would just print an empty page with only the header and footer). After this implementation, we found two small issues – not with this solution, but generally with how the search worked.

Remove empty search terms from the template header

I also added a search field to the “filter drop-downs”. This had the effect, that every time one of the filters would be used, the page would submit, sending an empty search term. This would result in a URL like https://example.com/shop/?s= title on that page like this: Search results: โ€œโ€ instead of something like Shop. To fix this, I’ve added the following snippets:

function product_search_only_clear_empty_search_from_request( $wp ) {
	if ( isset( $_GET['s'] ) && empty( trim( $_GET['s'] ) ) ) {
		unset( $wp->query_vars['s'] );
	}
}
add_action( 'parse_request', 'product_search_only_clear_empty_search_from_request' );

Prevent the search redirecting to a single product

The second issue was recognized by the shop owner. Any time there would be only one product as a result of a shop search, it would not show this result with the “product archive” template, but redirect to that single product. And since we redirected all searches to the shop search, this would be the case for any search that would only find one product, which could be confusing. Fortunately, by digging into the source code of WooCommerce to find the place where this is happening, I’ve found a filter to disable that redirect. All I had to do was adding one more line of code:

add_filter( 'woocommerce_redirect_single_search_result', '__return_false' );

That’s it! Now the search would always show a list of products, or just a single one, but not automatically redirect to that one product.

Conclusion

I’m not really a WooCommerce expert, quite the opposite. But since WooCommerce Core is using the same techniques as WordPress itself, I can usually find ways to customize it in the way I need it to be.

If you want this on your WooCommerce installation, you can find the code as a plugin in a GIST, where you can also download it as a ZIP and install it on your site.

If you have any other tips around searches in WooCommerce, please leave a comment.