How to Migrate from WordPress.com to WordPress.org
How to Migrate from WordPress.com to WordPress.org
Problem Statement & Impact
Transitioning a production website from the managed, closed-ecosystem architecture of WordPress.com to a self-hosted WordPress.org environment introduces critical architectural, infrastructural, and security challenges. While WordPress.com abstracts underlying server configurations, database operations, and system-level security, migrating to a self-hosted stack shifts complete operational responsibility to the site administrator. This transition exposes the infrastructure to the entire spectrum of web-based attack vectors, including automated brute-force attacks against administrative endpoints, SQL injection vulnerabilities in unverified third-party plugins, cross-site scripting (XSS), and distributed denial-of-service (DDoS) attempts.
The primary technical risk during this migration revolves around data integrity, asset preservation, and SEO continuity. Content loss, broken permalink structures, unmapped internal redirects, and database charset corruption can instantly degrade search engine crawl efficiency and result in severe ranking drops. Furthermore, failing to provision the destination server correctly—such as running suboptimal PHP memory limits, misconfigured database connection parameters, or missing PHP extensions—leads to HTTP 500 internal server errors, fatal memory exhaustion, and failed XML imports. From a business continuity perspective, an unmanaged or poorly executed migration causes extended downtime, loss of transactional data in e-commerce configurations, and compromised user credentials if transport layer security (TLS) or authentication mechanisms are misconfigured during the domain pointer updates.
Root Cause Analysis & Architecture
The architectural divergence between WordPress.com and WordPress.org stems from hosting paradigms. WordPress.com operates on a heavily modified, multi-tenant VIP architecture running inside a controlled containerized infrastructure where direct filesystem modifications, raw SQL queries, and arbitrary PHP code execution are strictly prohibited or filtered through enterprise layers. Conversely, self-hosted WordPress.org relies on a LAMP (Linux, Apache/Nginx, MySQL/MariaDB, PHP) or LEMP stack that executes directly on dedicated virtual private servers (VPS) or cloud instances.
Chat with an engineer on WhatsApp within 15 minutes.
During a migration, structural failures typically manifest due to three core technical bottlenecks:
- PHP Execution Limits: The WordPress.com XML export file often exceeds default server limits. Standard self-hosted PHP configurations default to a
post_max_sizeandupload_max_filesizeof 2MB to 32MB, alongside a 30-secondmax_execution_time. When an importer attempts to parse a multi-gigabyte XML payload containing base64-encoded attachments or thousands of posts, the script triggers a fatal PHP memory exhaustion error or an HTTP 413 (Payload Too Large) status code. - Database Schema & Collation Mismatch: WordPress.com databases utilize modern MySQL character sets, frequently
utf8mb4_unicode_520_ci. If the target server runs an older MySQL or MariaDB daemon with legacy defaults (e.g.,latin1_swedish_cior basicutf8mb3), importing serialized PHP objects within thewp_optionsorwp_postmetatables results in character truncation, corrupted option arrays, and broken plugin configurations. - Permalink Discrepancies and Media Path Breakage: WordPress.com often appends date-based or custom structures to site URLs, and its internal media CDN stores assets under specialized domains (e.g.,
*.files.wordpress.com). If media sideloading fails during the XML import phase, the newly generated posts on WordPress.org will retain absolute URLs pointing to the old WordPress.com infrastructure. Once the source site is decommissioned or privacy settings are modified, these references transform into dead links (HTTP 404).
Step-by-Step Implementation & Remediation Guide
Step 1: Provisioning and Hardening the Target LEMP/LAMP Stack
Before executing any migration procedures, deploy a dedicated Ubuntu 22.04 LTS server running Nginx, PHP 8.2-FPM, and MariaDB 10.6+. Secure the server environment at the OS level before initiating web applications.
# Update system packages and install core dependencies
sudo apt update && sudo apt upgrade -y
sudo apt install nginx mariadb-server php8.2-fpm php8.2-mysql php8.2-curl php8.2-gd php8.2-mbstring php8.2-xml php8.2-zip php8.2-intl unzip curl -y
# Secure the MariaDB installation
sudo mysql_secure_installation
Configure PHP runtime limits within /etc/php/8.2/fpm/php.ini to handle large database imports and media processing without throwing execution errors. Modify the following parameters:
memory_limit = 512M
upload_max_filesize = 256M
post_max_size = 256M
max_execution_time = 300
max_input_vars = 3000
Restart the PHP-FPM service to apply the updated configuration:
sudo systemctl restart php8.2-fpm
Step 2: Database Initialization for WordPress.org
Log into the MariaDB command-line interface to create a dedicated database and user with scoped privileges, enforcing secure character sets to prevent data corruption during import.
sudo mysql -u root -p
Execute the following SQL commands within the MariaDB prompt:
CREATE DATABASE sitesuraksha_wp CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wp_admin'@'localhost' IDENTIFIED BY 'CryptographicallySecurePasswordHere!987';
GRANT ALL PRIVILEGES ON sitesuraksha_wp.* TO 'wp_admin'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Step 3: Extracting and Exporting Data from WordPress.com
Log in to your WordPress.com account, navigate to Tools > Export, and select the comprehensive Export All utility. This generates an uncompressed XML file (WXR format) containing all posts, pages, comments, custom fields, categories, and tags. Concurrently, navigate to the media library interface or utilize an archive tool if available to back up high-resolution media assets, ensuring you have a local fallback if media sideloading times out over HTTP.
Step 4: Installing WordPress Core via WP-CLI
Establish an SSH connection to your server, create the web root directory, and utilize WP-CLI to install the core WordPress files cleanly.
# Create web root directory
sudo mkdir -p /var/www/html/sitesuraksha
sudo chown -R $USER:$USER /var/www/html/sitesuraksha
cd /var/www/html/sitesuraksha
# Download WordPress Core
wp core download --locale=en_US
# Create wp-config.php with database credentials
wp config create --dbname="sitesuraksha_wp" --dbuser="wp_admin" --dbpass="CryptographicallySecurePasswordHere!987" --dbhost="localhost" --dbprefix="wp_ss_"
# Install WordPress and set initial admin parameters
wp core install --url="https://example.com" --title="Production Site" --admin_user="sysadmin_sec" --admin_password="SecureAdminPassword!123" --admin_email="[email protected]"
Step 5: Importing Content and Handling Media Sideloading
Install and activate the WordPress Importer plugin using WP-CLI, then execute the import routine, ensuring that media file attachment downloads are explicitly enabled.
# Install and activate the WordPress Importer plugin
wp plugin install wordpress-importer --activate
# Import the WXR XML export file
wp import /path/to/export-file.xml --authors=create
If the XML file is exceptionally large and causes CLI memory limits or timeout restrictions, segment the XML file using standard splitting utilities or use WP-CLI in batch mode. To update absolute URLs pointing to the old WordPress.com address, execute a serialized search-and-replace command across the database:
wp search-replace 'https://youroldblog.wordpress.com' 'https://example.com' --all-tables
Production Hardening & Edge Cases
Once the foundational migration is complete, the self-hosted environment demands immediate production hardening. Unlike managed SaaS platforms, a self-hosted WordPress instance is an attractive target for automated vulnerability scanners.
Filesystem Permissions: Enforce strict file permission boundaries to prevent unauthorized script execution and arbitrary file uploads. Set all directories to 755 and all files to 644. The wp-config.php file must be locked down to read-only for the owner (400 or 440).
sudo find /var/www/html/sitesuraksha -type d -exec chmod 755 {} \;
sudo find /var/www/html/sitesuraksha -type f -exec chmod 644 {} \;
sudo chmod 440 /var/www/html/sitesuraksha/wp-config.php
sudo chown -R www-data:www-data /var/www/html/sitesuraksha
XML-RPC and REST API Restrictions: The legacy XML-RPC interface is a primary vector for distributed brute-force attacks and credential stuffing. If remote publishing tools are not utilized, disable XML-RPC entirely via Nginx rules or inside the active theme’s functions.php / security hardening plugin.
Nginx Hardening Configuration: Implement directive blocks within your Nginx virtual host configuration file (e.g., /etc/nginx/sites-available/sitesuraksha) to block direct execution of PHP files inside upload directories:
server {
listen 443 ssl http2;
server_name example.com www.example.com;
root /var/www/html/sitesuraksha;
index index.php index.html;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
client_max_body_size 256M;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# Block access to sensitive files and directories
location ~* /(wp-config.php|xmlrpc.php|readme.html|license.txt) {
deny all;
return 404;
}
# Prevent PHP execution inside uploads directory
location ~* /wp-content/uploads/.*\.(php|php5|phtml)$ {
deny all;
}
}
Diagnostic Verification & Checklist
Execute the following verification matrix to validate the integrity, performance, and security posture of the newly migrated self-hosted WordPress.org instance before removing DNS records from the legacy WordPress.com environment.
| Verification Step | Diagnostic Command / Action | Expected Result / Success Metric |
|---|---|---|
| HTTP Status Check | curl -I https://example.com |
HTTP/2 200 OK (Ensures SSL and web server routing are correct). |
| Database Connection | wp db check |
Database checked: Success (No corrupted tables or collation errors). |
| File Permissions | name /var/www/html/sitesuraksha -perm 777 |
No output (Zero directories should possess world-writable 777 permissions). |
| XML-RPC Status | curl -I https://example.com/xmlrpc.php |
HTTP/403 Forbidden or HTTP/404 Not Found (Confirms endpoint is secured). |
| PHP Version Validation | php -v |
PHP 8.2.x active with all required security modules loaded. |
| SSL Cipher Strength | openssl s_client -connect example.com:443 |
Valid TLSv1.3 handshake with secure cipher suites. |
Monitor system logs continuously using standard diagnostic utilities to detect unauthorized connection attempts, memory spikes, or PHP execution warnings:
# Monitor Nginx access logs in real-time
sudo tail -f /var/log/nginx/access.log
# Monitor PHP-FPM error logs for fatal execution issues
sudo tail -f /var/log/php8.2-fpm.log
# Monitor system authentication failures
sudo grep "Failed password" /var/log/auth.log
Professional Security Hardening and Infrastructure Management
Self-hosted WordPress architectures require ongoing threat intelligence, proactive patch management, intrusion detection, and zero-day vulnerability mitigation. Secure your deployment against evolving attack vectors with enterprise-grade monitoring.
Is Your Website Fast & Secure?
Run an instant scan for security vulnerabilities, bloated plugins, and TTFB latency.
Need Help Securing or Speeding Up Your Site?
Get 24/7 proactive security WAF, daily cloud backups, and sub-second speed optimization from ₹3,470/mo.