FacetWP’s URL construction

When loading a listing template page for the first time, the page’s URL consists only of a base URL. For example, the URL of the Recipes demo page is:

https://facetwp.com/demo/recipes-demo/

When a user starts interacting with facets on that page, FacetWP adds variables to the page’s URL. Everything added after the base URL is called the “query string”.

Directly upon clicking a choice in a facet, a query variable representing that choice is added to the URL. Each additional facet interaction will update that facet’s query variable, or it will add a new one if it’s the first interaction with that facet. The whole set of query variables is preceded by a question mark.

For example, selecting “Cake” in the Recipes demo’s “Categories” facet adds the following to the base URL:

?_recipe_categories=cake

Even Pager facets and Sort facets append their current selection to the URL. For example, this is page 2 of a listing:

?_paged=2

And this is the URL after sorting by “Title a-z” with a Sort facet with the name “sortby”:

?_sortby=title_a_z

The only exception is the “load more” pager type of the Pager facet, which intentionally does not update its URL variables.

Multiple facets used

When selecting one or more choices for a second facet, query variables for that facet will be added to the URL too and separated from the previous ones with an ampersand: &.

For example, in the Recipes demo, after choosing “Cake” in the “Categories” facet, and “Almond” in the “Flavors” facet, the URL will look like this:

?_recipe_categories=cake&_flavors=almond

And the URL of page 2 of a listing that is sorted by title would look like this:

?_sortby=title&_paged=2

Multiple choices selected in a facet

If a facet has multiple choices selected, for example a Checkboxes facet, the choices will be separated in the URL with an encoded comma: %2C. For example, selecting two categories in the Recipes Demo’s “Flavors” facet will result in:

?_flavors=almond%2Clemon

Query variable construction

Each query variable – one for each facet – contains the following four items:

  • The prefix, which is a single underscore: _.
  • The facet’s “name”, e.g. flavors.
  • An = sign.
  • The facet’s choice(s) the user has made, e.g. almond%2Clemon, where multiple choices are separated by an encoded comma %2C.

FacetWP’s URL prefix

FacetWP’s query variables use an underscore _ prefix to prevent conflicts with WordPress and other plugins. WordPress has many reserved terms and reserved query variables, and unexpected things would happen if FacetWP would use them.

In the past FacetWP had a “URL prefix” setting, which let you choose between _ and fwp_. This setting was removed from the UI in version 3.5.3 for clean installs. If you have an older website with a FacetWP install that existed before this version (which was released on April 21, 2020), you will still see this setting.

Changing the URL prefix

Users sometimes ask how to change the _ prefix. In versions newer than v3.5.3, the _ prefix is fixed. It’s not possible anymore to change it in FacetWP’s settings.

If you absolutely need to change the prefix, for example back to fwp_ in a legacy site with a newer FacetWP install, the only way to change the setting is directly in the database.

First, make sure to back up your database. In the database, go to the wp_options table and find the facetwp_settings option. The value of that option contains a long JSON string. Towards the end of it, you’ll see the “prefix” setting, which you can change and then save.

Facet names in the URL

The first part of the query variable for each facet (after the _ prefix), is the facet’s “name” (yellow highlighted in this example):

?_recipe_categories=cake

In a facet’s settings, the “name” is what is shown in the gray input field:

A facet name field
A facet’s name field

The facet’s name is a technical name that is automatically generated from the facet’s “Label” field when it is first entered. But you can easily change the name, for example to shorten or simplify it when it is shown in the URL variable.

The auto-generated or customized name is sanitized on auto-generation or after changing and saving. Special characters are removed, and spaces and dashes are replaced by underscores.

Issues with certain facet names

One thing to keep in mind when naming your facets, is that WordPress has many reserved terms and reserved query variables. Giving your facet one of these reserved names will cause unexpected behavior.

For example, we have had users with a facet named “name”, which is a reserved term. Changing the name to “first_name” (and re-indexing afterward) fixed their issues.

Facet choices in the URL

As explained above, each choice a user selects in a facet adds that choice to the facet’s URL variable. For example, a facet with the name “flavors” with two selected choices will generate the following query string:

?_flavors=almond%2Clemon

A facet with two choicesIn this example, “almond” and “lemon” are the (technical) names of the two values selected. If the facet’s source is a taxonomy, this name is the term slug. If it is a custom field, it is the sanitized value entered in that field.

Sanitized and hashed facet choices

Sometimes you may encounter puzzling URL strings that look like this:

?_flavors=almond%2C12361053c4e8dd156950643ae742a789

A facet with a choice that will be hashedThis is caused by FacetWP’s internal function that automatically sanitizes facet choices that contain potentially URL-unsafe characters.

The above query string is the result of a facet named “flavors”, with two choices selected:

almond and lemon & lime

In this example, the ampersand & in the second choice is the cause of that choice being converted into a 128-bit (md5) hashed value, resulting in a string of alphanumerical characters in the URL (yellow highlighted). Additionally, because there are two choices in this example, the hashed choice is preceded by an encoded comma: the %2C part of the string.

Prevent hashing of facet choices

If you want to prevent hashing of your facet’s choices, be aware of the following rules for the source fields and terms of your facets’ choices:

  • Allowed are: alphabetical (a-z) and numerical (0-9) characters, underscores _, dashes - and dots ..
  • Any other type of character, including commas , and ampersands &, will trigger the facet choice to be hashed in the URL.
  • Spaces will be replaced by dashes -.
  • Multiple consecutive dashes will be replaced with a single dash.
  • The maximum number of characters is 50, above that the choice will be truncated.

If you can’t prevent users from breaking the above rules when entering values, or if manually fixing all source fields/terms is too much work (or impractical because of imported content), an alternative solution is to use the facetwp_index_row hook to replace the offending characters before the sanitizer function runs.

The following code replaces commas , and ampersands & with dashes -, when the choices are indexed. Make sure to re-index after adding this code to your (child) theme’s functions.php:

add_filter( 'facetwp_index_row', function( $params, $class ) {
  if ( 'your_facet_name' == $params['facet_name'] ) { // replace 'your_facet_name' with the name of your facet
    $value = $params['facet_value'];
    $value = str_replace( ',', '-', $value );
    $value = str_replace( '&', '-', $value );
    $params['facet_value'] = $value;
  }
  return $params;
}, 10, 2 );

The main reason for FacetWP’s query string is to enable the user to bookmark, forward or link to pages with specific facet combinations already pre-selected.

When a URL with a query string is opened for the first time, FacetWP uses its FWP.loadFromHash() function to load the post listing and facets directly from the URL’s query string, resulting in a page with all relevant facets reflecting the choices contained in their query variables.

We are occasionally asked if FacetWP supports “pretty” URLs, for example /make/audi/model/a4/ instead of ?_make=audi&_model=a4.

The answer is no, FacetWP only supports permalinks with URL parameters. “Pretty” permalinks quickly lose their appeal when multiple facet selections are made and/or multiple facets are combined. In which case there is no parent-child relationship between the subsequent sections of the URL and pretty permalinks would not make sense anymore.

Pretty permalinks would also be bad for SEO, since search engines would see many different URLs with the same content.

Preserve facet selections across pages

FacetWP automatically generates a query string when a user interact with the facets. The whole URL including the query string acts like a permalink: it allows users to bookmark, link to, or forward the page URL with the facets choices pre-selected.

However, when navigating to other pages on the site, the facet selections are lost. Fortunately, it’s possible to preserve the facets’ state across pages, by using cookies. See our tutorial to learn how to accomplish this.

In this context it is also good to know that it is possible to pre-select facet choices for specific URLs.

Disable FacetWP’s query string

Sometimes we get asked if it’s possible to turn off FacetWP’s URL variables.

One reason mentioned for this is that it would be better for SEO. Another reason is that turning them off restores the functionality of the browser’s “Back” button. With the query variables in place, clicking the “Back” button leads to previous filtering results. Without them, the previous page is loaded.

Although we recommend against it, it is possible to completely disable the query variables. FacetWP does not need them for facets to function properly.

However, be aware that when you opt to turn query variables off, users will lose the ability to use the URL (with pre-selected facet choices) for bookmarking, linking, and forwarding. Also using the browser’s “Back” button will not restore the state of the previous facet selections anymore.

To disable the query string, add the following code to your (child) theme’s functions.php:

add_action( 'wp_footer', function() {
?>
<script>
document.addEventListener('facetwp-refresh', function() {
    if (! FWP.loaded) {
        FWP.setHash = function() { /* empty */ }
    }
});
</script>
<?php
}, 100 );

FacetWP’s URL and SEO

A question that comes up occasionally is if FacetWP’s URL affects SEO in any way.

Disclaimer: we are not SEO experts, but there are a few logical answers:

Facets themselves are invisible to search engines. So search engine spiders will not “click” on facet choices and generate all kinds of URLs with query variables that get indexed. So there is no risk of creating “duplicate content” this way (which would be bad for SEO). The exception would be if you have links in your site to URLs with filtered facet combinations, for example in a menu or post content. Those links would get indexed and will show up in search engine results and Google Analytics.

Further, WordPress itself, and SEO plugins like Yoast SEO and RankMath, add “canonical link elements” to the page, which tell search engines that similar URLs are actually the same, preventing duplicate content issues. For pages with facets, the canonical URL is the same for every FacetWP URL, regardless of what facet choices are selected (including pagination and sorting). So there is no issue with duplicate content this way.

Set FacetWP URLs with query strings to “noindex” with Yoast SEO

If you want to make absolutely sure that URLs with FacetWP query variables are not indexed, and you are using the Yoast SEO plugin, you can use the wpseo_robots filter to change the robots meta tag from index to noindex for pages with facet choices in the URL.

Add the following code to your (child) theme’s functions.php:

add_filter( 'wpseo_robots', function( $robots ) {
  if ( function_exists( 'FWP' ) && ! FWP()->request->is_refresh && ! empty( FWP()->request->url_vars ) ) {
    return str_replace( 'index', 'noindex', $robots );
  }
  return $robots;
} );

The above code changes the robots meta tag only for directly loaded URLs with a FacetWP query string containing facet choices, including FacetWP pagination and sorting.

Worth mentioning is that Yoast SEO automatically removes the canonical link tag when the robots meta tag is set to “noindex”, because it is not necessary for that situation.

FacetWP pagination and SEO

If you are using AJAX-based pagination on your page, like FacetWP’s Pager facet, be aware that the individual posts on the subsequent paged pages, and the paged pages themselves, will not be seen and indexed by search engines.

This is not necessarily problematic, as long as all individual posts are reachable for the search engine spider through other archive pages on the site, like post-type archives and term archives. Another way to make sure all pages are found is by implementing a sitemap and pointing to it in your robots.txt file.

Another approach could be to give search engines “non-JavaScript” pagination links to follow (anchor links with href attributes) and hide those with JavaScript. This way, the user will see FacetWP’s AJAX-based pagination, and the search engine spider will have access to all linked paged pages through the links of the hidden non-JavaScript pagination.

WordPress itself and most themes come with their own built-in pagination functions you can use for this. Or you could add pagination with a plugin like WP Page-Navi, with which you can customize the WordPress pagination so that all “paged/x” pages are individually linked (not only with “prev”, “next” or “last” links). In this way, the search engine spider will have direct “one-click” access to all paged pages, even if there are many.

It used to be recommended to add a noindex robots meta tag to these paged pages, to prevent them from showing up in search engine results. That is no longer the case, as explained in this article of Yoast SEO about pagination best practices.

Functions and objects to get and manipulate the URL

When you are customizing FacetWP, be aware of the following built-in JavaScript objects and functions to get and manipulate the URL.

Say you have this page URL:

https://facetwp.com/demo/recipes-demo/­?_recipe_categories=cake&_flavors=almond

You can use the following methods to get and manipulate the different parts this URL consists of:

Get the URI

To get the URI (Uniform Resource Identifier), which is the part of the URL without the domain name and the query variables, you can use the FWP_HTTP.uri variable:

add_action( 'wp_footer', function() {
?>
<script>
document.addEventListener('facetwp-loaded', function() {
    console.log(FWP_HTTP.uri);
});
</script>
<?php
}, 100 );

The above code will output the following string to the Console, without the beginning and trailing slashes:

demo/recipes-demo

Get the query variables

To get the query variables – which are generated after interaction with facetsas an object, you can use the FWP_HTTP.get variable:

add_action( 'wp_footer', function() {
?>
<script>
document.addEventListener('facetwp-loaded', function() {
    console.log(FWP_HTTP.get);
});
</script>
<?php
}, 100 );

The above code will output the following object to the Console, containing the page’s $_GET variables:

{_recipe_categories: "cake", _flavors: "almond"}

Get the query variables with PHP

If you need to get the query variables with PHP, the following variable contains them as an array, after directly loading a page URL with facet selections in its query string:

FWP()->request->url_vars

A usage example can be found in the above robots meta tag code example.

Get the query string

To get the query variables – which are generated after interaction with facetsas a string, you can use the FWP.buildQueryString() function, which builds and returns the URL query string:

add_action( 'wp_footer', function() {
?>
<script>
document.addEventListener('facetwp-loaded', function() {
    console.log(FWP.buildQueryString());
});
</script>
<?php
}, 100 );

The above code will output the following string to the Console, containing the page’s whole query string (without the preceding question mark):

_recipe_categories=cake&_flavors=almond

This function can be useful to determine if any facets are currently in use. An example would be to show/hide the post listing based on whether there are facet query variables in the URL.

See our code snippets for more examples.

Two other URL-related functions are FWP.setHash() and FWP.loadFromHash().

The first one can be used to update the query string, but also to disable it. The second one dynamically re-creates all facet selections when a URL with a query string populated with pre-selected facet choices is entered by a user:

FWP.setHash(); // Updates the permalink URL
FWP.loadFromHash(); // Populates facet data from URL data (happens on pageload)

See also