Removing permalink base from all HivePress CPTs, taxonomies, etc

Hi

I recently hit a URL structure issue that I believe affects any HivePress site using a non-empty permalink front. I’ve arrived at what seems like a clean fix, but would like to confirm if this is the right fix? If it is, I believe this could help other HivePress users as well who have been facing the same issue.

The Issue
I wanted blog posts to live under /blog/ as /blog/%postname%/. So I set the WordPress permalink structure accordingly in Settings → Permalinks (as shown in the screenshot).

As soon as /blog/ became the permalink front, WordPress applied it to every HivePress URL as well (including categories, attributes, listings, etc.). The URLs turned into:

  • /blog/listing/{listing-name}/
  • /blog/listing-category/{category-name}/
  • /blog/vendor/{vendor-name}/
  • /blog/vendor-category/{category-name}/
  • /blog/{attribute-name}/{attribute} (attribute archives)
  • /blog/tag/{tag-name}

I wanted HivePress URLs to stay at the root (/listing/…, /listing-category/…, etc.) while keeping /blog/ as the front for only the blog posts.

Here’s the code that I came up with. It solves the problem, but I wanted to check if this is the right way and if the code is correct? If yes, it could definitely help others as well -

<?php

defined( 'ABSPATH' ) || exit;

/**
 * Strips the permalink front (e.g. /blog/) from every HivePress taxonomy,
 * including dynamically-registered attribute taxonomies.
 *
 * Hooks into WordPress core's register_taxonomy_args filter, which fires
 * inside register_taxonomy() itself — so it catches every code path,
 * including HivePress's Attribute component runtime registration.
 *
 * @param array  $args        Taxonomy registration arguments.
 * @param string $taxonomy    Taxonomy name.
 * @param array  $object_type Associated object types.
 * @return array
 */
function hev_strip_front_from_hivepress_taxonomy_args( $args, $taxonomy, $object_type ) {
	if ( 0 !== strpos( $taxonomy, 'hp_' ) ) {
		return $args;
	}

	if ( array_key_exists( 'rewrite', $args ) && false === $args['rewrite'] ) {
		return $args;
	}

	$rewrite = ( isset( $args['rewrite'] ) && is_array( $args['rewrite'] ) ) ? $args['rewrite'] : array();

	$rewrite['with_front'] = false;
	$args['rewrite']       = $rewrite;

	return $args;
}
add_filter( 'register_taxonomy_args', 'hev_strip_front_from_hivepress_taxonomy_args', 10, 3 );

/**
 * Strips the permalink front (e.g. /blog/) from every HivePress post type.
 *
 * @param array  $args      Post type registration arguments.
 * @param string $post_type Post type name.
 * @return array
 */
function hev_strip_front_from_hivepress_post_type_args( $args, $post_type ) {
	if ( 0 !== strpos( $post_type, 'hp_' ) ) {
		return $args;
	}

	if ( array_key_exists( 'rewrite', $args ) && false === $args['rewrite'] ) {
		return $args;
	}

	$rewrite = ( isset( $args['rewrite'] ) && is_array( $args['rewrite'] ) ) ? $args['rewrite'] : array();

	$rewrite['with_front'] = false;
	$args['rewrite']       = $rewrite;

	return $args;
}
add_filter( 'register_post_type_args', 'hev_strip_front_from_hivepress_post_type_args', 10, 2 );

If this approach is confirmed correct, I’m happy to help document it for other users… this seems like a common scenario for many HivePress sites that want a blog section at /blog/.

Thanks in advance for any guidance.

Hi,

This is actually how WordPress works by default. The setting is called “Permalink Structure” in Settings > Permalinks, and WordPress applies it site-wide to all post types, it’s not a specific URL structure just for the Posts post type. Listings are a custom post type, so if you add a static word to the permalink structure, it will appear in all links across your site.

This isn’t a HivePress-specific issue, but rather a WordPress permalink behavior. What you’d need is a solution that adds the URL prefix only to the Posts post type while excluding all other pages.

I found a topic with a potential solution that might help: https://stackoverflow.com/questions/7285532/url-prefix-for-posts-wordpress

Before trying the solution in that link, you’ll need to remove the “blog” prefix from the Permalink Structure setting, then apply the code snippet to add it back only for Posts.

Hope this helps

Hi @kseniia

Thanks for the detailed explanation - that makes sense, and I completely understand that this is WordPress core behavior rather than something specific to HivePress.

Based on that, we took a slightly different approach by keeping /blog/ in the global structure and removing the front only for HivePress post types and taxonomies using the code we shared.

Could you please confirm if this is the right and safe way to handle it, and whether the code looks good for long-term use? Just want to be sure we’re not missing anything.

If this method is sound, I’d be happy to document it for other users since this seems like a fairly common requirement (separating blog URLs under /blog/ while keeping marketplace URLs at root).

Appreciate your guidance on this.

Thanks again :+1:

1 Like

Hi @Prady,

This topic seemed interesting and was something I was interested in trying to implement myself. I’ve used a few different AIs to try and improve the solutions proposed so far, and here’s what I’ve come up with:

/**
 * Blog Prefix Rewrite Rules
 *
 * Adds a /blog/ prefix to all standard post URLs without touching
 * the global permalink structure or affecting CPTs, pages, or taxonomies.
 *
 * Usage: Drop into a Code Snippets plugin or functions.php.
 * After activating, visit Settings → Permalinks and Save Changes to flush rules.
 */


/* -----------------------------------------------------------------------
 * 1. REWRITE RULES
 *    Prepend /blog/ rules so they win over WordPress defaults.
 *    Order matters — more specific patterns must come first.
 * -------------------------------------------------------------------- */

add_action( 'generate_rewrite_rules', 'safe_blog_prefix_rewrite_rules' );

function safe_blog_prefix_rewrite_rules( $wp_rewrite ) {
    $new_rules = array(

        // Feed variants (must come before the generic slug rule)
        'blog/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$'
            => 'index.php?name=$matches[1]&feed=$matches[2]',
        'blog/([^/]+)/(feed|rdf|rss|rss2|atom)/?$'
            => 'index.php?name=$matches[1]&feed=$matches[2]',

        // Pagination
        'blog/([^/]+)/page/?([0-9]{1,})/?$'
            => 'index.php?name=$matches[1]&paged=$matches[2]',

        // Comment pagination
        'blog/([^/]+)/comment-page-([0-9]{1,})/?$'
            => 'index.php?name=$matches[1]&cpage=$matches[2]',

        // oEmbed endpoint
        'blog/([^/]+)/embed/?$'
            => 'index.php?name=$matches[1]&embed=true',

        // Canonical post URL — keep this last so the rules above take priority
        'blog/([^/]+)/?$'
            => 'index.php?name=$matches[1]',
    );

    // Prepend so our rules are evaluated before WordPress core rules
    $wp_rewrite->rules = $new_rules + $wp_rewrite->rules;
}


/* -----------------------------------------------------------------------
 * 2. PERMALINK FILTER
 *    Rewrite the URL WordPress generates for standard posts.
 *    Hooks both post_link and pre_post_link to cover all code paths:
 *      - pre_post_link fires early and handles %postname%-based structures
 *      - post_link catches everything else (get_permalink, the_permalink, etc.)
 * -------------------------------------------------------------------- */

add_filter( 'pre_post_link', 'safe_blog_prefix_links', 10, 2 );
add_filter( 'post_link',     'safe_blog_prefix_links', 10, 2 );

function safe_blog_prefix_links( $post_link, $post ) {
    // Guard: only rewrite standard 'post' type; leave pages, CPTs, attachments alone
    if ( isset( $post->post_type ) && $post->post_type === 'post' ) {
        return home_url( '/blog/' . $post->post_name . '/' );
    }
    return $post_link;
}

I hope this helps!

Cheers,
Chris :victory_hand:

1 Like

Sure, if the code snippet affects only HivePress post types and taxonomies (you can match these by “hp_” prefix), it should be fine, but please note that the “blog” URL part may still appear for account pages, or regular WordPress pages.

Hey Chris, thanks so much for jumping in on this, really appreciate you taking the time to explore it! :folded_hands:

It’s actually pretty cool, we’ve both been working through this with AI assistance and ended up with completely different approaches. Yours keeps the global permalink as /%postname%/ and adds /blog/ only to posts via custom rewrite rules. Mine goes the opposite direction, it sets the permalink front to /blog/%postname%/ globally and uses WordPress’s register_taxonomy_args and register_post_type_args filters to strip with_front from HivePress objects (scoped to the hp_ prefix so it leaves everything else alone). Same destination, two very different paths to get there.

Honestly, I think the more interesting question now is which direction the HivePress team would recommend. This problem has to be common - any HivePress site that wants a proper /blog/ section alongside listings hits it. If we can land on an officially-recommended approach here, it’d be a great reference for the community going forward.

Really grateful you’re working through this with me, @ChrisB :blush:

Cheers!

P.S. @ihor would love your take on which direction makes more sense here. Either approach, or something different entirely!

1 Like

If both code snippets work, I recommend using one that adds “/blog/” to posts only and keeps global permalink structure without a prefix because excluding all the CPTs and pages (including future possible CPTs, for example if you install another plugin) may require more efforts and maintenance than specifically targeting blog post URLs.

Hope this helps

1 Like

Perfect, thank you :raising_hands:

1 Like