WP SSH (Remote Bridge)
Private WordPress remote management bridge plugin — authenticated, scoped REST actions instead of raw SSH access.
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/mssoftpc/wp-ssh/archive/refs/heads/master.zipWP SSH — Remote Management Bridge
A private, self-hosted WordPress plugin that lets you (or a trusted AI assistant like Claude) manage a site over an authenticated HTTPS endpoint instead of raw SSH / WP-CLI access.
This is not a public/multi-tenant tool. It is meant to be installed manually on sites you personally manage. Each install generates its own unique secret key and detects its own site automatically — nothing is shared across installs.
⚠️ Read this before installing. With write actions enabled, this plugin can write PHP files inside
wp-content, run arbitrary SQL, and activate/deactivate plugins. Anyone who obtains your secret key has meaningful control over the site. Keep the key private, use HTTPS only, and turn on Read-only mode or an IP allowlist when you don't need full write access.
Why this exists
Managing WordPress sites normally means either giving out full SSH/cPanel credentials, or doing everything by hand through wp-admin. This plugin exposes a small, whitelisted set of actions over a single REST endpoint, so a script (or an AI assistant with an HTTP tool) can:
- Read diagnostics, options, posts, media, plugins, themes, orders
- Make scoped changes (update a post, set a product attribute, clear cache, edit a file inside
wp-content) without needing shell access - Do it all with logging, rate-limiting, confirm-tokens on destructive actions, and automatic backups
Installation
- Download
wp-ssh.zip(or clone this repo intowp-content/plugins/wp-ssh/). - In wp-admin: Plugins → Add New → Upload Plugin, upload the zip, Activate.
- Go to Tools → WP SSH.
- Copy the auto-generated Secret key.
- Check Enable bridge, save settings.
That's it — the site is now reachable at:
POST https://yoursite.com/wp-json/wp-ssh/v1/execute
Authentication
Every request must include the secret key in a custom header:
X-WPSSH-Key: <your-secret-key>
Content-Type: application/json
Request body:
{
"action": "site_info",
"params": {}
}
Response:
{ "success": true, "data": { ... } }
or, on failure:
{ "code": "wp_ssh_bad_key", "message": "Invalid or missing secret key.", "data": { "status": 401 } }
Unauthenticated ping
GET /wp-json/wp-ssh/v1/ping returns { plugin, version, site, label } — useful to confirm the plugin is installed and to auto-detect which site you're talking to, without needing the key.
Security features
| Feature | What it does |
|---|---|
| Secret key | 64-char random key per install, compared with hash_equals(). |
| IP allowlist | Optional — restrict /execute to specific IPs. |
| Lockout | 5 failed key attempts from an IP → 15 minute lockout. |
| Read-only mode | One toggle blocks every write action; safe for pure browsing/diagnostics. |
| Confirm tokens | Permanent delete, restore, and non-SELECT SQL require "confirm": true in params. |
| Auto-backup | file_write keeps up to 3 rotating .wpssh-bak-N copies before overwriting; file_restore brings one back. |
| Activity log | Last 100 actions (time, IP, action, summary) shown in Tools → WP SSH. |
Example: curl
curl -X POST https://yoursite.com/wp-json/wp-ssh/v1/execute \
-H "X-WPSSH-Key: YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"action": "diagnostics", "params": {}}'
Available actions
Site & diagnostics
| Action | Params | Notes |
|---|---|---|
site_info |
– | Name, label, URL, WP/theme/WooCommerce version. |
diagnostics |
– | PHP/memory config, autoload options size, DB size + largest tables, transients/revisions counts, active caching plugins. |
health_check_status |
– | Last hourly self-check result (if enabled). |
health_check_run |
– | Run the health check now (DB connection, PHP fatals in debug.log, low disk space, bridge disabled). |
cron_list |
– | All scheduled WP-Cron events, soonest first. |
cron_run |
hook, args? |
Manually fire a cron hook now. |
Options
| Action | Params |
|---|---|
option_get |
name |
option_update |
name, value |
Posts & content
| Action | Params |
|---|---|
post_list |
post_type?, post_status?, limit?, search? |
post_get |
id |
post_create |
title, content?, excerpt?, post_type?, post_status?, slug?, meta? |
post_update |
id, title?, content?, excerpt?, status?, slug?, meta? |
post_delete |
id, force?, confirm? (required if force) |
term_list |
taxonomy? |
term_create |
name, taxonomy?, slug? |
menu_list |
– |
menu_item_add |
menu, title, object_id?, object_type?, url?, position?, parent_id? |
widget_add |
sidebar_id, id_base, instance |
Media
| Action | Params |
|---|---|
media_list |
limit? |
media_upload |
filename, content_base64, title? — jpg/jpeg/png/gif/webp/svg only |
Plugins & themes
| Action | Params |
|---|---|
plugin_list |
– |
plugin_activate |
file |
plugin_deactivate |
file |
theme_list |
– |
Files (scoped to wp-content/)
| Action | Params | Notes |
|---|---|---|
file_list |
path? |
Directory listing. |
file_read |
path |
Path is relative to wp-content. |
file_write |
path, content, skip_backup? |
Auto-backs up the existing file first. |
file_restore |
path, backup? (1-3, default 1), confirm |
Restores from a .wpssh-bak-N copy. |
All file paths are resolved with realpath() and rejected if they escape wp-content — no ../ traversal outside it.
Database
| Action | Params | Notes |
|---|---|---|
db_query |
sql, confirm? |
SELECT/SHOW/DESCRIBE/EXPLAIN run freely. Anything else needs Allow write queries enabled in settings and "confirm": true. |
search_replace |
search, replace, tables?, dry_run? |
Serialization-safe find/replace across the DB (won't corrupt PHP-serialized values), mirrors wp search-replace. |
cache_clear |
post_id?, all_elementor?, transients? |
Clears Elementor render/CSS cache, theme compiled-CSS options, object cache, and pings LiteSpeed/WP Super Cache/W3TC/WP Rocket if present. |
WooCommerce
| Action | Params | Notes |
|---|---|---|
order_list |
limit?, status?, search? |
Requires WooCommerce active. |
order_get |
id |
Full order detail incl. line items, address. |
order_update_status |
id, status, note? |
|
product_attribute_set |
product_id, taxonomy, term_ids[], is_variation? |
Sets a product attribute + terms in one call. |
Elementor
| Action | Params | Notes |
|---|---|---|
elementor_data_get |
post_id |
Raw _elementor_data JSON. |
elementor_data_set |
post_id, data |
Validates JSON, writes it, and automatically clears Elementor's CSS/element cache. |
template_export |
post_id |
Exports a page's Elementor data + page settings/template — for copying a design to another site. |
template_import |
title, elementor_data, post_id?, post_type?, post_status?, elementor_page_settings?, page_template? |
Creates/updates a post with imported Elementor data. The plugin never talks to other sites directly — you move the exported JSON from one site's response into another site's template_import call yourself. |
Settings reference (Tools → WP SSH)
- Site label — friendly name returned in
ping/site_info, handy when you manage several sites. - Enable bridge — master on/off switch.
- Read-only mode — blocks every write action regardless of other settings.
- Allow write queries — required (in addition to
confirm: true) for non-SELECTSQL viadb_query. - IP allowlist — one IP per line; empty = any IP (still requires the key).
- Health check emails — hourly self-check with an alert email if something looks wrong.
- Regenerate secret key — invalidates the old key immediately.
License
GPLv2 or later.