A client writes: "People say my site opens a casino. I open it and everything is fine." This is the most common hack I clean up on WordPress and Joomla sites. Here is how it works and how to remove it properly.

Why the owner does not see it

The injected code is selective. Typical conditions:

  • the visitor came from Google or another search engine
  • the visitor is on a phone
  • it is the first visit from this IP or without a cookie
  • the visitor is not logged in

The owner types the address directly, is logged in and has visited before, so the site looks normal. To reproduce the redirect, open a search result for your site in a private window on a phone, or send a request with a mobile user agent and a Google referrer:

curl -sIL -A "Mozilla/5.0 (Linux; Android 14) Mobile" -e "https://www.google.com/" https://example.com/ | grep -i -E "^HTTP|^location" 

If you see a location: header pointing to a domain you do not know, the redirect is real.

Where the code usually lives

Check these places in this order.

1. .htaccess in the site root and in every subfolder. Look for RewriteCond %{HTTP_REFERER} or %{HTTP_USER_AGENT} rules that you did not write.

2. Entry files: index.php, wp-config.php, wp-blog-header.php, wp-load.php, configuration.php and includes/defines.php in Joomla. An injected block is often at the very top of the file, sometimes after hundreds of spaces so you do not see it without scrolling right.

3. Theme and template files: functions.php, header.php, footer.php, the template index.php.

4. Fake plugins and must-use plugins: wp-content/mu-plugins/ is loaded automatically and does not appear in the normal plugin list. Also look for plugin folders with generic names you did not install.

5. The database: in WordPress, the wp_options table (siteurl, home, and rows with long encoded values) and wp_posts with <script> tags inside content. In Joomla, custom HTML modules and the template parameters.

6. JavaScript files: a line appended to jQuery or to the theme script that loads code from another domain.

7. Uploads: PHP files in wp-content/uploads/ or Joomla images/ and media/. There should be none.

Useful searches

Files changed in the last two weeks:

find . -type f -name "*.php" -mtime -14 -not -path "./wp-content/cache/*" 

PHP files where they should not be:

find wp-content/uploads -type f -name "*.php" 

Typical obfuscation:

grep -rlE "eval\(|base64_decode\(|gzinflate\(|str_rot13\(|fromCharCode" --include="*.php" --include="*.js" . 

These functions are also used by honest code, so read every match before deleting anything. Modification dates can be faked too: treat them as a hint, not as proof.

Why it comes back

If you delete the redirect and it returns in a day, something restores it. Look for:

  • a backdoor file (a small PHP script that accepts commands)
  • an unknown administrator account
  • a scheduled task: crontab -l on the server and the WP-Cron list
  • a still running malicious PHP process that rewrites files (restart PHP after cleaning)
  • another infected site in the same hosting account

Cleaning in the right order

  1. Make a full backup of the infected site. You may need it to recover content.
  2. Close the site for maintenance or restrict access by IP.
  3. Replace the core with a clean copy of the same version. For WordPress: delete wp-admin and wp-includes and upload fresh ones. Do the same for Joomla core folders.
  4. Reinstall every plugin, extension and theme from the original source. Do not "clean" them by hand if a clean copy exists.
  5. Review what is left by hand: wp-content, uploads, .htaccess, config files.
  6. Clean the database.
  7. Remove unknown users. Change every password: admin, database, FTP or SSH, hosting panel. Replace the WordPress salts so all sessions end.
  8. Update everything and remove what you do not use.

Close the hole

Cleaning without finding the entry point is half the job. The usual causes are an outdated plugin or extension, a nulled theme, a weak admin password, or a neighbour site on the same account. Check the access log for the first POST request to the backdoor file: the date tells you which backup is clean and which vulnerability was used.

Then: disable PHP execution in upload folders, turn off file editing in the admin panel, set up automatic backups stored outside the server.

After cleaning

  • In Google Search Console, open Security issues and request a review if there is a warning.
  • Check that the sitemap and robots.txt are your own.
  • Look at the indexed pages with site:example.com: spam pages created by the attacker should return 404 or 410.

If you do not want to do this yourself, I clean hacked WordPress and Joomla sites and close the entry point: website hack recovery.