true, 'cloudflare_enabled' => false, 'cloudflare_zone_id' => '', 'cloudflare_api_token' => '', 'sucuri_enabled' => false, 'sucuri_api_key' => '', 'sucuri_api_secret' => '', 'fastly_enabled' => false, 'fastly_service_id' => '', 'fastly_api_token' => '', 'akamai_enabled' => false, 'akamai_client_token' => '', 'akamai_access_token' => '', 'akamai_client_secret' => '', 'akamai_host' => '', 'sevalla_enabled' => false, 'sevalla_api_key' => '', 'sevalla_application_id' => '', 'cloudways_enabled' => false, 'flywheel_enabled' => false, ); } /** * Get all settings merged with defaults. * * @return array */ public static function get_settings() { return wp_parse_args( get_option(self::OPTION_KEY, array()), self::get_defaults() ); } /** * All setting keys that hold boolean toggles. * * @return array */ private static function get_bool_keys() { return array( 'cache_tags_enabled', 'cloudflare_enabled', 'sucuri_enabled', 'fastly_enabled', 'akamai_enabled', 'sevalla_enabled', 'cloudways_enabled', 'flywheel_enabled', ); } /** * Sanitize and save settings from $_POST. * * @param array $post POST data (defaults to $_POST). * @return bool True if saved. */ public static function save_from_post($post = null) { if ($post === null) { $post = $_POST; } // Only save if at least one edge-cache field is present in the payload. if (!isset($post['cache_tags_enabled'])) { return false; } $bool_keys = self::get_bool_keys(); $defaults = self::get_defaults(); $settings = array(); foreach ($defaults as $key => $default) { if (in_array($key, $bool_keys, true)) { $settings[$key] = !empty($post[$key]) && $post[$key] === '1'; } else { $settings[$key] = sanitize_text_field($post[$key] ?? ''); } } update_option(self::OPTION_KEY, $settings); return true; } /** * AJAX handler: save edge cache settings (standalone endpoint). */ public static function ajax_save() { if (!isset($_POST[self::NONCE_FIELD]) || !wp_verify_nonce($_POST[self::NONCE_FIELD], self::NONCE_ACTION)) { wp_send_json_error(array('message' => 'Security check failed')); return; } if (!Metasync::current_user_has_plugin_access()) { wp_send_json_error(array('message' => 'Insufficient permissions')); return; } self::save_from_post(); wp_send_json_success(array('message' => 'Edge cache settings saved')); } /** * Render the Edge Cache / CDN settings section. */ public static function render() { $settings = self::get_settings(); $show_sevalla = !class_exists('KinstaCache'); $show_cloudways = !empty($_SERVER['HTTP_X_VARNISH']) || !empty(get_option('metasync_cloudways_detected')); $show_flywheel = defined('FLYWHEEL_CONFIG_DIR'); ?>

Configure CDN credentials for cache-tag purging. When enabled, cache-tag headers are added to -processed pages so CDNs can purge by post ID.

Cache-Tag Headers

How it works:
Adds Cache-Tag, Surrogate-Key, and Edge-Cache-Tag headers to singular pages. These allow Cloudflare, Fastly, Akamai, and other CDNs to purge individual posts by tag instead of flushing the entire cache.

CDN Provider Credentials
'cloudflare_zone_id', 'label' => 'Zone ID', 'type' => 'text', 'placeholder' => 'e.g. 023e105f4ecef8ad9ca31a8372d0c353', 'hint' => 'Found in Cloudflare dashboard → Overview → Zone ID'), array('name' => 'cloudflare_api_token', 'label' => 'API Token', 'type' => 'password', 'hint' => 'Requires Cache Purge permission on the zone'), )); self::render_provider('sucuri', 'Sucuri WAF', 'purges Sucuri firewall cache', $settings, array( array('name' => 'sucuri_api_key', 'label' => 'API Key', 'type' => 'password', 'hint' => 'Found in Sucuri dashboard → Settings → API'), array('name' => 'sucuri_api_secret', 'label' => 'API Secret', 'type' => 'password'), )); self::render_provider('fastly', 'Fastly', 'purges via Surrogate-Key', $settings, array( array('name' => 'fastly_service_id', 'label' => 'Service ID', 'type' => 'text', 'hint' => 'Found in Fastly dashboard → Service details'), array('name' => 'fastly_api_token', 'label' => 'API Token', 'type' => 'password', 'hint' => 'Requires Purge all or Purge by key scope'), )); $has_more_after_akamai = $show_sevalla || $show_cloudways || $show_flywheel; self::render_provider('akamai', 'Akamai', 'purges via Edge-Cache-Tag', $settings, array( array('name' => 'akamai_client_token', 'label' => 'Client Token', 'type' => 'password'), array('name' => 'akamai_access_token', 'label' => 'Access Token', 'type' => 'password'), array('name' => 'akamai_client_secret', 'label' => 'Client Secret', 'type' => 'password'), array('name' => 'akamai_host', 'label' => 'Host', 'type' => 'text', 'placeholder' => 'e.g. akab-xxxxx.purge.akamaiapis.net', 'hint' => 'Found in Akamai Control Center → Identity & Access → API credentials'), ), !$has_more_after_akamai); if ($show_sevalla) { $has_more_after_sevalla = $show_cloudways || $show_flywheel; self::render_provider('sevalla', 'Kinsta / Sevalla', 'purges edge cache via v3 API (v2 fallback)', $settings, array( array('name' => 'sevalla_api_key', 'label' => 'Sevalla API Key', 'type' => 'password', 'hint' => 'Found in Sevalla dashboard → Company → API Keys'), array('name' => 'sevalla_application_id', 'label' => 'Application ID', 'type' => 'text', 'placeholder' => 'e.g. fb5e5168-4281-4bec-94c5-0d1584e9e657', 'hint' => 'UUID format — found in the site URL: my.sevalla.com/applications/{UUID}'), ), !$has_more_after_sevalla, 'Uses v3 edge cache purge API with v2 fallback. If KinstaCache mu-plugin is available, native purge is used instead.'); } if ($show_cloudways) { self::render_provider('cloudways', 'Cloudways', 'purges Varnish cache per URL', $settings, array(), !$show_flywheel, sprintf('Detected Cloudways Varnish. Enable to purge Varnish on each %s update.', Metasync::get_whitelabel_otto_name())); } if ($show_flywheel) { self::render_provider('flywheel', 'Flywheel', sprintf('full cache flush on %s updates', Metasync::get_whitelabel_otto_name()), $settings, array(), true, sprintf('Detected Flywheel hosting. Enable to clear cache on each %s update.', Metasync::get_whitelabel_otto_name())); } ?>
placeholder="" style="width: 100%; padding: 8px; border: 1px solid var(--dashboard-border); border-radius: 6px; background: var(--dashboard-card-bg); color: var(--dashboard-text-primary); box-sizing: border-box;" />

array(), 'code' => array('style' => array()))); ?>