Removing WordPress Safely From a Host Running App APIs
21 leftover WordPress items removed from a shared host that also serves app backends: the pre-checks that matter, a reversible move-then-delete method, and .htaccess rules that answer 410 Gone.
This is for anyone who moved a blog off WordPress but still has its files sitting on a shared host that also runs other things, in our case the backends of several mobile apps. Our conclusion: once you cannot roll back anymore, delete the WordPress files. Do it from a hard-coded list, move each item outside the web root first, and only delete for good after the regression checks pass. Then answer the old system paths with 410 Gone, and keep the 301s for content that has a new home.
The blog moved from WordPress to a Python static site generator on 2026-11-08 (how we kept the app store URLs alive). The WordPress front controller was cut out of .htaccess, but its files stayed on the server so we could roll back. On 2026-11-09 the WordPress database was deleted. From that point the files could not run a site anymore. They were dead weight and attack surface, including a wp-config.php with real database credentials.
Why "just delete wp-*" is dangerous here
The same document root serves more than the blog. Among other things, there is an ad-configuration API that 16 installed apps call when they start. If a mistake makes PHP fail across the domain, the blog is the smallest problem. So before touching anything, we checked four things.
1. Do any apps call WordPress endpoints? We grepped about 20 Flutter app source trees for wp-content, wp-json, xmlrpc and wp-admin. There were 0 hits. Apps that once loaded images from wp-content/uploads or used the REST API would have been a reason to keep certain paths.
2. Is there an auto_prepend_file? This is the trap that is easy to forget. Security and WAF plugins for WordPress often add a .user.ini (or php.ini) with a line like this:
auto_prepend_file = '/home/…/public_html/wp-content/…/firewall-bootstrap.php'
PHP then runs that file before every PHP request in the directory tree, not only WordPress requests. Delete wp-content and every PHP script on the domain fails with a fatal "Failed opening required" error, including the app APIs. We checked for .user.ini and php.ini files. None existed, so this risk did not apply to us. If one exists on your server, remove the setting and wait for the .user.ini cache to expire (often five minutes) before removing the file it points to.
3. Is the root index.php really WordPress? Any non-WordPress PHP on a shared host might be called index.php. We downloaded it and checked that it contained WP_USE_THEMES, the line that boots the WordPress theme loader. The removal script refuses to run if that check fails. The static home page kept working because DirectoryIndex index.html index.php already served index.html first.
4. Is there anything in the backup plugin's folder? The migration plugin had a backup directory inside wp-content. It held only placeholder files and no actual archives, so there was nothing to keep.
What was left over
Besides the obvious wp-admin, wp-includes, wp-content and the wp-*.php files:
| Item | Why it matters |
|---|---|
wp-config.php |
Database host, user and password in plain text |
xmlrpc.php |
A classic target for credential-stuffing and pingback abuse |
license.txt, readme.html |
Reveal the WordPress version to scanners |
| A host-generated one-click auto-login PHP file | Created by the hosting panel's "log in to WordPress" button; not part of WordPress |
Object-cache and LiteSpeed config inside wp-content |
Plugin configuration that no longer applies |
In total: 21 items, 3 directories and 18 files. The auto-login helper is worth a special look. It does not come with WordPress, so checklists made for WordPress do not mention it. Check your document root for files your host's panel created.
Method: move, verify, then delete
The removal script had three properties:
- An explicit, hard-coded list of names. No glob, no
wp-*pattern. A pattern delete on a shared root is how you lose a folder calledwp-backup-of-the-app-apithat someone created years ago. - A refuse-list. The script asserted that none of the listed names were in a set of protected names: the API folders, the app folders, the language folders,
index.html,.htaccess,ads.txtandapp-ads.txt. That only checks the list itself, but it catches the kind of editing mistake that matters. - Rename, not delete. Each item was moved by FTP
RENAMEinto an archive folder outside the web root. A rename is instant even for a directory with thousands of files, it takes the item off the web immediately, and it can be undone with another rename.
DIRS = ["wp-admin", "wp-content", "wp-includes"]
FILES = ["index.php", "wp-config.php", "xmlrpc.php", "license.txt", "readme.html", …]
NEVER = {"api", "app", "en", "ja", "ko", "static", "index.html", ".htaccess", "ads.txt", "app-ads.txt", …}
assert not (set(DIRS + FILES) & NEVER)
if b"WP_USE_THEMES" not in root_index_php:
sys.exit("abort: root index.php is not WordPress")
for name in targets:
ftp.rename(f"{base}/{name}", f"{ARCHIVE}/{name}")
After the move we ran the full regression checks: the site URL list and the API response comparison. Only after both passed did we delete the archive. That delete ran in the background, walking the tree with MLSD and removing files one by one. Over FTP this takes a long time for wp-includes, which is why it should not happen while the site is in an unknown state.
.htaccess: 410 for system paths, 301 for content
With the files gone, WordPress paths would fall through to a 404. We made them explicit, right after the existing security block:
# WordPress was removed on purpose: answer 410 Gone
RewriteRule ^(wp-admin|wp-includes|wp-content|wp-json)(/|$) - [G,L]
RewriteRule ^(wp-[a-z-]+|xmlrpc)\.php$ - [G,L]
A few details:
(/|$)stops the first rule from matching longer names that start the same way. A request for/wpx-test/or a hypothetical/wp-contentful/is not caught.- The second rule only matches
.php./wp-sitemap.xmltherefore still reaches its older rule and gets a 301 to/sitemap.xml, which search engines may have stored. - Old post URLs, category archives,
/feed/and/?p=IDkeep their 301s to the new pages. They are content with a new address, not "gone". - Old image URLs under
wp-content/uploads/now return 410. If you have migrated images with backlinks you care about, put 301 rules for them before this block.
We did not deploy this directly. We generated the full .htaccess, put it into an isolated sandbox subfolder with RewriteBase rewritten to that folder, and ran 19 test cases against it: 19/19 passed. Among them were /wpx-test/ still returning 404, a POST to /xmlrpc.php returning 410, and an old uploads image path returning 410. After the real deploy we saw the same status codes, 53/53 site URL checks passed, and the API regression showed 0 failures.
Old .htaccess backups remain on the server for reference. They are not readable from the web because of a rule that was already there:
RewriteRule (^|/)\.htaccess(\.|_)[^/]*$ - [F,L]
404 or 410?
Google's documentation treats both as "this page does not exist" and drops such URLs from the index. 410 is the more explicit signal: the server says the resource was removed on purpose and is not coming back. Some sources say 410 leads to slightly faster removal. We did not measure that and do not claim it.
We chose 410 for a different reason: to keep our own logs readable. A 404 on /wp-login.php could mean a broken link, a typo or a scanner. A 410 says "this is the WordPress path we removed deliberately". When a 404 shows up in the logs, it is now worth looking at.
| URL type | Response |
|---|---|
Old post, category, feed, /?p=ID |
301 to the new page |
/wp-sitemap.xml |
301 to /sitemap.xml |
wp-admin/, wp-includes/, wp-content/, wp-json/ |
410 |
wp-*.php, xmlrpc.php (any method) |
410 |
| Anything else that does not exist | 404 |
Checklist
- Make sure rollback is truly gone (the database is deleted), or keep the files until it is.
- Grep your app sources for
wp-content,wp-json,xmlrpc,wp-admin. - Look for
auto_prepend_filein any.user.iniorphp.ini. - Check that the root
index.phpcontainsWP_USE_THEMESand thatDirectoryIndexserves your static home first. - List the items by hand, including host-generated helpers, and check the list against a set of protected names.
- Rename everything outside the web root, run regression checks, and only then delete.
- Test the new
.htaccessin a sandbox folder, then deploy, then re-run the same cases live.
FAQ
Can deleting wp-content break PHP that has nothing to do with WordPress?
Yes, if a .user.ini or php.ini sets auto_prepend_file to a file inside wp-content. Security and firewall plugins often install one. Once the file is gone, every PHP request on the domain fails, including unrelated APIs. Check for it before deleting anything.
Which WordPress files are worth removing even after the site is gone?
wp-config.php (database credentials), xmlrpc.php, license.txt and readme.html (they reveal the version), any host-generated auto-login helper, and the wp-admin, wp-includes and wp-content folders. We removed 21 items in total.
Should removed WordPress URLs return 404 or 410?
Google documents that both lead to removal from the index. 410 Gone is the more explicit signal that the removal was intentional. We used 410 for WordPress system paths and kept 301 redirects for old posts and feeds that have a new home.