WordPress powers over 40% of the web, and its familiarity makes it an attractive choice for anonymous publishing via Tor hidden services. Running WordPress on .onion allows journalists, activists, and privacy-focused publishers to maintain a content management system without revealing their hosting infrastructure. The setup requires careful configuration to prevent WordPress from leaking real server information through built-in features like pingbacks, update checks, and external HTTP requests. This guide covers the complete installation and configuration process.
Need this done for your project?
We implement, you ship. Async, documented, done in days.
WordPress requires Linux, nginx (web server), MySQL/MariaDB (database), and PHP (runtime) - the LEMP stack. Install all components from package manager. Critical configuration: nginx must listen only on localhost (127.0.0.1:80), MariaDB must bind to localhost only (bind-address = 127.0.0.1 in my.cnf), and PHP-FPM must listen on a local Unix socket or 127.0.0.1:9000. The Tor hidden service maps port 80 to localhost:80 where nginx listens. The nginx virtual host configuration for WordPress requires: root pointing to WordPress installation directory, PHP-FPM proxy configuration (fastcgi_pass pointing to PHP-FPM socket), try_files for WordPress's front-controller pattern (all non-file requests routed to index.php), and removal of identifying headers (server_tokens off; in nginx.conf). Create a dedicated MariaDB user with privileges only on the WordPress database (not all databases). Use a strong random password generated with openssl rand -base64 32.
WordPress Configuration for .onion Operation
After installing WordPress, critical wp-config.php settings for .onion operation: define('WP_HOME', 'http://youronion.onion'); define('WP_SITEURL', 'http://youronion.onion'); - these tell WordPress its URL is the .onion address. Without this, WordPress generates clearnet URLs in internal links. Add define('DISABLE_WP_CRON', true); to disable the built-in WordPress cron (wp-cron.php) which makes HTTP requests that could reveal server information. Instead, run WP-Cron via a real system cron job: */15 * * * * curl http://127.0.0.1/wp-cron.php?doing_wp_cron via localhost, never via the .onion address. Disable automatic updates: define('AUTOMATIC_UPDATER_DISABLED', true); - updates should be applied manually to control when external connections occur. Disable XML-RPC if not needed: add add_filter('xmlrpc_enabled', '__return_false'); to functions.php - XML-RPC is a common attack vector. Disable pingbacks and trackbacks in WordPress settings (Settings > Discussion > uncheck pingback options) to prevent WordPress from making outbound HTTP requests to other sites.
Preventing WordPress External HTTP Requests
WordPress makes numerous outbound HTTP requests by default: checking for plugin/theme updates, fetching translation files, checking the WordPress.org API, loading the Gravatar service for comment avatars, and making pingback requests to linked sites. Each of these requests exits to clearnet and reveals the server's IP address (or Tor exit node IP if Tor is used). To block all outbound WordPress HTTP requests: add define('WP_HTTP_BLOCK_EXTERNAL', true); and define('WP_ACCESSIBLE_HOSTS', ''); to wp-config.php. This blocks all external HTTP via WordPress's built-in HTTP API. Additionally, configure PHP-FPM to block outbound connections at the OS level by running PHP-FPM in a network namespace without clearnet access. This defense-in-depth ensures that even if a plugin makes direct PHP HTTP requests (bypassing WordPress's HTTP API), it cannot reach clearnet. Updates must then be done manually via FTP/SFTP or WP-CLI on the server - test updates in a staging environment first.
WordPress Plugins for .onion Publishing
Not all WordPress plugins work properly in a Tor-isolated environment. Avoid plugins that: require external API keys with HTTP callbacks (payment processors, email services that make external requests), use CDN-hosted assets (loads clearnet resources), or enable social sharing (embeds clearnet JavaScript). Recommended plugins for .onion WordPress: (1) Simple Post Scheduling (for scheduling posts without cron.php HTTP requests), (2) WP Super Cache or W3 Total Cache (for local page caching that reduces server load), (3) a local search plugin like Relevanssi instead of ElasticSearch (which would require a network connection), (4) Two-Factor Authentication plugin for WordPress admin security. For media handling: all images and media must be hosted locally. Disable WordPress's automatic image resizing through external services. The WordPress media library serves all files from the local server. Configure nginx to serve media files directly (without PHP) for better performance: location /wp-content/uploads { try_files $uri =404; } without PHP-FPM fallback.
Maintenance and Security for WordPress .onion Sites
WordPress security maintenance for .onion installations requires adapted procedures. Updates: download WordPress core/plugin updates manually (or via WP-CLI with Tor-proxied connections: WP_SOCKS5_PROXY=127.0.0.1:9050 wp plugin update --all), test in staging, then deploy to production. The automatic update mechanism is disabled (per earlier configuration), so this process must be scheduled manually. Database backups: use WP-CLI database export (wp db export backup.sql) and encrypt with GPG before storing. File backups: tar the entire WordPress installation directory. Brute force protection: install a login attempt limiting plugin or configure nginx to rate-limit requests to /wp-login.php and /wp-admin/. Two-factor authentication for all admin accounts. WordPress admin access: restrict /wp-admin to specific IP ranges if your admin IP is consistent, or add HTTP Basic Auth via nginx as an additional layer before WordPress's own authentication. Monitor WordPress error logs (in wp-content/debug.log when WP_DEBUG_LOG is enabled) for signs of compromise or plugin errors.