Working with an assistant guide
Inspect What a WordPress Plugin Sends to External Services
List what a plugin says it sends, log its outbound requests on a staging site under named settings, and compare the two, knowing what a WordPress HTTP hook cannot see.
On this page
To find out what a WordPress plugin sends to outside services, write down what it says it sends, then log the outbound requests it makes on a staging copy of the site while you exercise each feature, and compare the two. Record which settings were on for each capture. A log is evidence of what happened during that capture only: a quiet log does not prove a plugin never sends anything, because many requests fire on schedules, on specific actions, or from the visitor’s browser.
The method below uses a small must-use plugin on the WordPress http_api_debug hook. It works for any plugin that makes its requests through the WordPress HTTP API, and it is worth running before you connect an assistant or install anything that holds site credentials.
Inventory stated data flows and enabled features
Collect what the plugin claims before you measure anything:
- Its readme and privacy policy. The WordPress Plugin Handbook recommends that plugin developers use
wp_add_privacy_policy_contentto disclose, among other things, whether a plugin shares personal data with third parties “to outside APIs/servers” (Privacy in the Plugin Handbook, checked September 27, 2026). Text added that way appears on the Privacy Policy Guide screen, under Settings → Privacy → Policy Guide on your site (wp_add_privacy_policy_content reference, checked September 27, 2026). - Any machine-readable statement. WP Visibility, for example, serves a public privacy manifest from the installed plugin at
/wp-json/wpvis/v1/privacy-manifest, andwp visibility privacyprints it. The manifest lists two outbound requests, both to wpvisibility.com (the update check and license activation), plus IndexNow submissions and Assist AI drafts (sent to the site’s own AI provider through the WordPress AI Client) when those modules are on. See What WP Visibility Stores, and What Uninstall Removes. - The features you have on. List every module or setting that might contact a service: licensing, updates, analytics, AI features, CDN or image services, search engine pings.
Turn that into a table of expected requests: host, trigger, and what the plugin says is sent. That table is what you test.
Log outbound requests on staging
Use a staging copy, never production: the log sees every outbound request the site makes through the HTTP API. Save this as wp-content/mu-plugins/outbound-log.php:
<?php
/**
* Plugin Name: Outbound request log (staging only)
*/
add_action( 'http_api_debug', function ( $response, $context, $class, $args, $url ) {
$parts = wp_parse_url( $url );
$query = array();
if ( ! empty( $parts['query'] ) ) {
parse_str( $parts['query'], $query );
}
$line = array(
gmdate( 'c' ),
$args['method'] ?? 'GET',
$parts['host'] ?? '',
$parts['path'] ?? '',
implode( ',', array_keys( $query ) ),
is_wp_error( $response ) ? $response->get_error_code() : wp_remote_retrieve_response_code( $response ),
wp_debug_backtrace_summary( null, 4 ),
);
file_put_contents( '/path/outside/webroot/outbound.log', implode( "\t", $line ) . "\n", FILE_APPEND | LOCK_EX );
}, 10, 5 );
WordPress fires http_api_debug after an HTTP API response is received and passes the response, the request arguments, and the URL (http_api_debug reference, checked September 27, 2026).
The log records the method, host, path, query parameter names, status, and a call stack summary: the function and class names that led to the request, which usually show which plugin made the call (wp_debug_backtrace_summary reference, checked September 27, 2026). It deliberately leaves out query values, headers, and bodies. Those can carry license keys, Application Passwords, or tokens, and a log file is an easy place to leak them. Write it to a path the web server does not serve. If you need to see request body fields for one specific request, add them temporarily for that host, redact them, and remove the code afterwards.
Capture a controlled set of requests
Run captures under named configurations, one at a time, and note the start and end time of each:
- Defaults. Plugin active, optional features off. Load the dashboard, a few admin screens, and several front-end pages.
- Updates. Go to Dashboard → Updates and click Check again.
- Licensing. Activate and deactivate the license, if the plugin has one.
- Each optional feature. Turn on one feature, use it the way you normally would (publish a post, run a report, trigger an AI action), and turn it off again.
- Scheduled tasks. Run due cron events with
wp cron event run --due-now(wp cron event run, checked September 27, 2026) and see what fires.
Your log will include WordPress core’s own requests too, such as update and translation checks to api.wordpress.org (wp_update_plugins reference, checked September 27, 2026). The call stack column usually separates those from the plugin under test.
Check the browser separately
The hook only sees requests made on the server through the WordPress HTTP API. Scripts, fonts, pixels, and embeds that a plugin adds to a page are fetched by the visitor’s browser, and never touch that hook. Open the browser’s developer tools, select the Network panel, load a few front-end pages as a logged-out visitor, and list every request to a host other than your own. Do the same on the admin screens the plugin adds.
Compare observations with documentation and coverage limits
Match each logged request to a row of your expected-requests table:
| What you find | What it means |
|---|---|
| Logged request matches a documented flow and trigger | Consistent with the documentation for this capture |
| Documented flow never appeared | The trigger did not happen, the feature was off, or a cached result or retry delay skipped the request; not a discrepancy yet |
| Request to a host the documentation does not mention | Ask the vendor, or read the code path shown in the call stack |
| Request fires on every page load | Note the cost and the data, even if documented |
| Browser request to a third party from a front-end page | Visitors’ data leaves the site; check your own privacy policy |
Be clear about what this method cannot see:
- Code that bypasses the HTTP API. A plugin that opens its own connection with cURL, sockets, or PHP stream functions does not pass through
http_api_debug. Searching the plugin’s code forcurl_,fsockopen,stream_socket_client, andfile_get_contentsfinds the common cases. - What happens after the request. You see that data left; you cannot see how the receiving service stores or uses it. That is a question for the vendor’s privacy policy.
- Conditions you did not trigger. Error reports, first-run setup, license expiry, and rarely used features may only fire in circumstances your captures did not include.
- Other services on the site. Hosting, CDN, and email providers have their own data flows outside WordPress.
Keep the result with the site’s records
Save the expected-requests table, the configurations you ran, the redacted log excerpts, and the network panel findings, with the plugin version and date. Repeat the capture after major plugin updates and whenever the vendor changes its privacy policy. Remove the logging plugin and delete the log when you finish.
If you are vetting a plugin that an assistant will operate, pair this with a check of what the assistant’s credential can reach: Build a WordPress Assistant Permission Matrix.
