APO refresh by prefix, any plans?

Hi there,

We’ve been evaluating APO for a WordPress website.

However one big problem is the fact that when a new content is published, only the article’s page is refreshed and all other listing pages are not.

We were using Varnish until now and developed an in house plugin to clear a lot of different paths either by prefix or by full URL. It seems like APO would benefit from such an approach as well.

Currently, clearing cache by prefix is an enterprise feature only, is there any plan to support this for PRO plans? We are having 3 pro plans would would be really interested to leverage APO, but with no possibility to clear the related pages by prefix when new content appears, we are unable to leverage this.

Here comes a snippet of everything we remove from the Varnish cache once a post gets updated:

	public function purge_post( $post_id ) {
	$valid_post_status = [ 'publish', 'private', 'trash' ];
	$this_post_status  = get_post_status( $post_id );

	// Not all post types are created equal.
	$invalid_post_type   = [ 'nav_menu_item', 'revision' ];
	$noarchive_post_type = [ 'post', 'page' ];
	$this_post_type      = get_post_type( $post_id );

	/**
	 * Determine the route for the rest API
	 * This will need to be revisted if WP updates the version.
	 * Future me: Consider an array? 4.7-?? use v2, and then adapt from there?
	 */
	if ( version_compare( get_bloginfo( 'version' ), '4.7', '>=' ) ) {
		$rest_api_route = 'wp/v2';
	}

	// array to collect all our URLs.
	$exact_urls_to_purge     = [];
	$urls_to_purge_by_prefix = [];

	// Verify we have a permalink and that we have a valid post status.
	if ( false === get_permalink( $post_id )
		|| ! in_array( $this_post_status, $valid_post_status, true )
		|| in_array( $this_post_type, $invalid_post_type, true ) ) {
		return;
	}

	// Post URL.
	$urls_to_purge_by_prefix[] = get_permalink( $post_id );

	/**
	 * JSON API Permalink for the post based on type
	 * We only want to do this if the rest_base exists
	 * But we apparently have to force it for posts and pages (seriously?)
	 */
	if ( isset( $rest_api_route ) ) {
		$post_type_object = get_post_type_object( $post_id );
		$rest_permalink   = false;
		if ( isset( $post_type_object->rest_base ) ) {
			$rest_permalink = get_rest_url() . $rest_api_route . '/' . $post_type_object->rest_base . '/' . $post_id . '/';
		} elseif ( 'post' === $this_post_type ) {
			$rest_permalink = get_rest_url() . $rest_api_route . '/posts/' . $post_id . '/';
		} elseif ( 'page' === $this_post_type ) {
			$rest_permalink = get_rest_url() . $rest_api_route . '/pages/' . $post_id . '/';
		}
	}

	if ( $rest_permalink ) {
		$urls_to_purge_by_prefix[] = $rest_permalink;
	}

	// Add in AMP permalink for offical WP AMP plugin:
	// https://wordpress.org/plugins/amp/
	if ( function_exists( 'amp_get_permalink' ) ) {
		$urls_to_purge_by_prefix[] = amp_get_permalink( $post_id );
	}

	// https://wordpress.org/plugins/accelerated-mobile-pages/
	if ( defined( 'AMPFORWP_AMP_QUERY_VAR' ) ) {
		$urls_to_purge_by_prefix[] = get_permalink( $post_id ) . 'amp/';
	}

	// Also clean URL for trashed post.
	if ( 'trash' === $this_post_status ) {
		$trashpost                 = get_permalink( $post_id );
		$trashpost                 = str_replace( '__trashed', '', $trashpost );
		$urls_to_purge_by_prefix[] = $trashpost;
		$urls_to_purge_by_prefix[] = $trashpost . 'feed/';
	}

	// Category purge based on Donnacha's work in WP Super Cache.
	$categories = get_the_category( $post_id );
	if ( $categories ) {
		foreach ( $categories as $cat ) {
			$urls_to_purge_by_prefix[] = get_category_link( $cat->term_id );
			$urls_to_purge_by_prefix[] = get_rest_url() . $rest_api_route . '/categories/' . $cat->term_id . '/';
		}
	}

	// Tag purge based on Donnacha's work in WP Super Cache.
	$tags = get_the_tags( $post_id );
	if ( $tags ) {
		$tag_base = get_site_option( 'tag_base' );
		if ( '' === $tag_base ) {
			$tag_base = '/tag/';
		}
		foreach ( $tags as $tag ) {
			$urls_to_purge_by_prefix[] = get_tag_link( $tag->term_id );
			$urls_to_purge_by_prefix[] = get_rest_url() . $rest_api_route . $tag_base . $tag->term_id . '/';
		}
	}
	// Custom Taxonomies: Only show if the taxonomy is public.
	$taxonomies = get_post_taxonomies( $post_id );
	if ( $taxonomies ) {
		foreach ( $taxonomies as $taxonomy ) {
			$features = (array) get_taxonomy( $taxonomy );
			if ( $features['public'] ) {
				$terms = wp_get_post_terms( $post_id, $taxonomy );
				foreach ( $terms as $term ) {
					$urls_to_purge_by_prefix[] = get_term_link( $term );
					$urls_to_purge_by_prefix[] = get_rest_url() . $rest_api_route . '/' . $term->taxonomy . '/' . $term->slug . '/';
				}
			}
		}
	}

	// If the post is a post, we have more things to flush
	// Pages and Woo Things don't need all this.
	if ( $this_post_type && 'post' === $this_post_type ) {
		// Author URLs:
		$author_id                 = get_post_field( 'post_author', $post_id );
		$urls_to_purge_by_prefix[] = get_author_posts_url( $author_id );
		$urls_to_purge_by_prefix[] = get_author_feed_link( $author_id );
		$urls_to_purge_by_prefix[] = get_rest_url() . $rest_api_route . '/users/' . $author_id . '/';

		// Feeds:
		$urls_to_purge_by_prefix[] = get_bloginfo_rss( 'rdf_url' );
		$urls_to_purge_by_prefix[] = get_bloginfo_rss( 'rss_url' );
		$urls_to_purge_by_prefix[] = get_bloginfo_rss( 'rss2_url' );
		$urls_to_purge_by_prefix[] = get_bloginfo_rss( 'atom_url' );
		$urls_to_purge_by_prefix[] = get_bloginfo_rss( 'comments_rss2_url' );
		$urls_to_purge_by_prefix[] = get_post_comments_feed_link( $post_id );
	}

	// Archives and their feeds.
	if ( $this_post_type && ! in_array( $this_post_type, $noarchive_post_type, true ) ) {
		$urls_to_purge_by_prefix[] = get_post_type_archive_link( get_post_type( $post_id ) );
		$urls_to_purge_by_prefix[] = get_post_type_archive_feed_link( get_post_type( $post_id ) );
	}

	// Home Pages and (if used) posts page.
	$exact_urls_to_purge[]     = get_rest_url();
	$exact_urls_to_purge[]     = get_home_url() . '/';
	$urls_to_purge_by_prefix[] = get_home_url() . '/page/';

	if ( 'page' === get_site_option( 'show_on_front' ) ) {
		// Ensure we have a page_for_posts setting to avoid empty URL.
		if ( get_site_option( 'page_for_posts' ) ) {
			$exact_urls_to_purge[] = get_permalink( get_site_option( 'page_for_posts' ) );
		}
	}

	// Remove query string as we purge by prefix anyway.
	$urls_to_purge_by_prefix = array_map(
		function ( $url ) {
			return strtok( $url, '?' );
		},
		$urls_to_purge_by_prefix
	);

	$this->urls_to_purge_by_prefix += apply_filters( 'clean_cache_post_urls_to_purge_by_prefix', $urls_to_purge_by_prefix );
	$this->exact_urls_to_purge     += apply_filters( 'clean_cache_post_exact_urls_to_purge', $exact_urls_to_purge );
}

public function execute_purge() {
	$urls_to_purge_by_prefix = array_unique( $this->urls_to_purge_by_prefix, SORT_REGULAR );
	foreach ( $urls_to_purge_by_prefix as $url ) {
		$this->purge_urls_starting_with( $url );
	}

	$exact_urls_to_purge = array_unique( $this->exact_urls_to_purge, SORT_REGULAR );
	foreach ( $exact_urls_to_purge as $url ) {
		$this->purge_url( $url );
	}
}
1 Like

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.