WordPress site redirecting to another site: how to tell a hack from a setting, and what to do first
A redirect to a site you did not choose is a sign of a hacked WordPress, but a redirect rule, the site address settings, an expired domain or the visitor's own device can look the same. Check what a visitor is served, look where redirects are planted, and delete nothing until you have a copy.
- By
- WP Ministry
- Published
In short
- A redirect that only visitors from Google or only phones get is the pattern Google's documentation describes for a hacked site.
- Rule out four ordinary causes first. They are the site address settings, a redirect you or a plugin set, the domain or its DNS, and an ad or an extension on the visitor's device.
- Do not test in your own browser. Ask for the page with curl, once plainly, once as a visitor from Google and once as a phone.
- The checksum commands cover WordPress's own files and plugins from WordPress.org. They do not cover .htaccess, wp-config.php, themes or the database.
- Do not delete the rule or the file you find. Write it down, take a copy of the site, then follow the full cleanup.
- Removing one redirect is not a cleanup. If the way in stays open, or a backdoor is left behind, it comes back.
Visitors being sent to a site you did not choose is a recognized sign that a WordPress site has been broken into. Google lists redirects among its examples of hacked content. Four ordinary things look the same: the site address settings, a redirect that you or a plugin set, an expired domain or changed DNS, and an ad or a browser extension on the visitor's own device.
Tell them apart before you touch anything. None of the checks on this page changes the site, and what you find is evidence you will want later.
Is it a hack or something else?
Start with who is redirected and where they land.
| What you see | What it points to |
|---|---|
| Visitors who click a result in Google are sent away. Typing the address shows your site. | A hack. Google's spam policies give this as their example of a hacked redirect. |
| Only phones are redirected. | A hack, or an ad script. Google's Search team has described both. |
| It happens to a visitor once and not again, or never while you are logged in. | A rule that picks its visitors, which is how a planted redirect behaves. |
| Google Search Console lists a security issue for the site. | Google has found it. Its report gives redirects to a malicious site as an example of code injection. |
Everyone is sent to an address you recognize: an old domain, a staging copy, the same site with or without www. | The site address settings, or a redirect that you, a developer or a plugin set. |
| Everyone lands on a registrar's page that says the domain has expired. | The domain's registration, not WordPress. |
| One person is redirected, on one device, and it happens to them on other sites too. | Unwanted software or an extension on that device. |
| The browser shows ERR_TOO_MANY_REDIRECTS and reaches no page at all. | A loop between your own addresses. That is a different problem, and how to fix ERR_TOO_MANY_REDIRECTS covers it. |
A planted redirect is choosy on purpose. Google's guidance for hacked sites says hackers may serve their content only to certain referrers or user agents, to reach more real visitors and to avoid detection by site owners and malware scanners. Google's documentation lists the referrer, the user agent, the device and the time of day as conditions. A rule on the server can also read cookies, and WordPress marks a logged-in browser with one. So the owner can be the one person who never sees it.
Rule out the four ordinary causes
- The site address settings. In the dashboard, go to Settings, then General. "WordPress Address (URL)" and "Site Address (URL)" should both hold your own address. WordPress writes the address stored there into every link, stylesheet and script on the page, sends the login and the dashboard to it, and redirects between the
wwwand plain forms of an address. So with a wrong value the first page may still load, and a visitor lands on the other address at the first click. If the value is an old or a staging address of yours, how to change your WordPress URL has the fix. A domain you have never seen is not an ordinary cause: someone changed it. - A redirect that you or a plugin set. Look through your redirect plugin's list and through the rules in
.htaccess. How to set up redirects in WordPress shows the three places a redirect can live. A rule nobody on your side can account for is not an ordinary cause either. Leave it in place and keep reading. - The domain. Log in at your registrar. Check the renewal date, and that the nameservers and records are the ones your host gave you. Under ICANN's policy for expired registrations, a registrar that sends an expired domain's visitors to a web page must make that page say the registration has expired and give renewal instructions.
- An ad, or the visitor's own device. Chrome's help lists browsing that "redirects to unfamiliar pages or ads" among the signs of unwanted software on a computer, so ask the person who reported it whether it happens on other sites. On your side, Google's Search team notes that a script installed to show ads can send phone visitors to a different site without the owner being aware. Its advice is to check for a hack first. If there is none, remove the third-party scripts you do not control one at a time, and check on a phone between each.
See what a visitor is served, without logging in
Step 1: Ask for the page four ways
Run these in a terminal on your own computer, with your own address. The first asks plainly. The second says it arrived from Google. The third says it is Chrome on an Android phone. The fourth says both. Then run them again with the address of a post or a product, which is where a visitor from search lands.
bashcurl -s -o direct.html -w "%{response_code} %{redirect_url}\n" https://example.com/ curl -s -o from-search.html -w "%{response_code} %{redirect_url}\n" --referer "https://www.google.com/" https://example.com/ curl -s -o phone.html -w "%{response_code} %{redirect_url}\n" --user-agent "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Mobile Safari/537.36" https://example.com/ curl -s -o phone-from-search.html -w "%{response_code} %{redirect_url}\n" --referer "https://www.google.com/" --user-agent "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Mobile Safari/537.36" https://example.com/Step 2: Read the four lines
Each command prints the status code and, for a redirect, the address it leads to. It also saves the page to a file.
200with nothing after it means the page was served.301or302followed by your own address, withhttpsorwwwadded, is your site's normal behavior. An address on a domain that is not yours is a positive result. So is a redirect that shows on some of the four lines and not on others.Step 3: See what sent the redirect
If a line showed a redirect, repeat that request with
-D -, which prints the response's headers. TheLocationline names the destination. A line that beginsX-Redirect-Bymeans the redirect went through WordPress's own redirect function, which adds that header and saysWordPressunless a plugin puts its own name there. That points at the address settings, a plugin or the theme. With no such line, the redirect came from somewhere else: a server rule such as one in.htaccess, or PHP code that does not use WordPress's function.bashcurl -s -o from-search.html -D - --referer "https://www.google.com/" https://example.com/Step 4: Look in the saved pages for scripts you did not add
Not every redirect shows in the status code. Google's guidance shows a script that reads where the visitor came from and then sends the browser elsewhere.
curlruns no scripts, so that page saves with a200. This lists every script tag in a saved page. Each address in the list should be one you can name: your own domain, your analytics, your ad network. Then open the file in a text editor and search for the words Google suggests:iframe,evalandunescape.bashgrep -o '<script[^>]*>' phone-from-search.htmlStep 5: Run the two checks that need no terminal
The redirect checker follows an address hop by hop from our server, with no cookies and nothing remembered. The hacked-site check asks for a page twice, once plainly and once as a visitor from a search result, and compares where each ends. Neither presents itself as a phone.
Step 6: Check Google's Safe Browsing site status
Enter your address on Google's site status page. It says whether the site currently contains content that Safe Browsing has determined to be dangerous.
Step 7: Open the Security issues report in Search Console
The report shows what Google has found, with sample addresses. If your site is not in Search Console yet, add it first. Google's help says you may not be able to reproduce a warning yourself, and to rely on the report as the source of truth.
A clean result from all of these does not clear the site. Nobody outside can imitate a rule that goes by the time of day or by the visitor's network address. If visitors keep reporting it, look inside.
Where a planted redirect lives
WordPress's documentation for hacked sites names .htaccess as the one file you will definitely want to look at. It lists index.php, header.php, footer.php and the theme's functions file as files that affect every page request when modified. Google's guidance adds the server's configuration, the database, and code injected into pages.
The steps below go through those places. They need SSH access and WP-CLI, and most say what to open if you have neither. Look, and change nothing. Write down each thing you find, where it is, and the time.
Step 1: Run the two checksum commands
The first compares every file of WordPress itself, the main
index.phpincluded, with the checksums WordPress.org holds for your version. With--include-rootit also warns about files in the main folder that are not part of WordPress. The second does the same for each plugin that came from WordPress.org. Clean runs print "Success: WordPress installation verifies against checksums." and "Success: Verified" with a count, and nothing else. AnyWarning:line is a finding, even when the last line says Success: a file that was added, with no original changed, is reported as "File should not exist" and the command still ends in Success.Neither command checks
.htaccess,wp-config.php, any theme, the uploads folder or the database. A plugin that did not come from WordPress.org is skipped with a warning, and WP-CLI has no checksum command for themes. So a clean result here clears two places and leaves the rest of this list.bashwp core verify-checksums --include-root wp plugin verify-checksums --all --skip-plugins --skip-themesStep 2: Read every .htaccess file
.htaccessis Apache's configuration file for one folder, and there can be one in any folder. List them all and open each. In a file manager, turn on hidden files first, or you will not see them. A clean one in the main folder holds the block WordPress writes between# BEGIN WordPressand# END WordPress, shown in how to fix the 403 Forbidden error, plus rules that you, your host or a plugin can account for.Google's example of a planted rule is a
RewriteCondline that tests%{HTTP_REFERER}for Google, followed by aRewriteRulethat ends in another site's address and[R=301]. Look also for conditions on%{HTTP_USER_AGENT}or%{HTTP_COOKIE}, and for anErrorDocumentline that names another site. On nginx there is no.htaccess. Its rules sit in the server's own configuration, so ask your host to read it.bashfind . -name ".htaccess"Step 3: Open index.php and wp-config.php
The checksum command has already compared
index.php. A clean one is a few lines: it setsWP_USE_THEMESand loadswp-blog-header.php.wp-config.phphas no checksum, because every site's is different. Compare it withwp-config-sample.phpfrom a fresh download of WordPress. The sample holds the database settings, eight keys and salts, the table prefix,WP_DEBUG, and closing lines that setABSPATHand loadwp-settings.php. Yours will hold more, from your host and your plugins, and each line should be one somebody can explain. The command below prints the lines to read first: an address set withWP_HOMEorWP_SITEURL, any file being loaded, and code passed toeval. The line that loadswp-settings.phpbelongs there.bashgrep -n -E "WP_HOME|WP_SITEURL|include|require|eval|base64_decode" wp-config.phpStep 4: Check the two address settings
These print the two addresses WordPress is using. Both should be your own. A
WP_HOMEorWP_SITEURLline inwp-config.phpoverrides the stored value, and is what prints here when one is set. Without WP-CLI, they are the two fields under Settings, then General. If a wrong address keeps you out of the dashboard, write it down. How to change your WordPress URL shows how to get back in.bashwp option get home --skip-plugins --skip-themes wp option get siteurl --skip-plugins --skip-themesStep 5: List the must-use plugins and drop-ins
Must-use plugins are PHP files in
wp-content/mu-plugins. They are always on, cannot be switched off from the dashboard, and are not in the default list on the Plugins screen. Drop-ins are files with fixed names placed straight inwp-content, such asadvanced-cache.php,object-cache.phpanddb.php. Hosts commonly use must-use plugins, and a caching plugin may add a drop-in. A clean list is short, or empty, and every entry is one your host or a plugin of yours accounts for. Without WP-CLI, openwp-contentandwp-content/mu-pluginsin your host's file manager.bashwp plugin list --status=must-use --skip-plugins --skip-themes wp plugin list --status=dropin --skip-plugins --skip-themesStep 6: Read the active theme's files
The list shows which theme is
activeand, for a child theme, which is itsparent. WordPress runs the functions file of both. Openfunctions.php,header.phpandfooter.phpin each theme's folder underwp-content/themes. A block theme hasheader.htmlandfooter.htmltemplate parts in place of the last two.The only clean original is a fresh copy of the same version from where the theme came, so compare against that. Without one, read for the patterns Google's guidance shows: a script tag that loads from a domain you do not know, an
iframewith no height or width, a script that testsdocument.referrerand then sets the location, and a long unreadable string passed toeval.bashwp theme list --skip-plugins --skip-themesStep 7: Search posts, widgets and settings for scripts
This searches every text column of WordPress's tables and prints the table, the column, the row and the text around each match. Posts show under
post_content. Widgets and settings show underoption_value. WordPress lets administrators and editors put JavaScript in posts, pages and widgets, so a match is not a finding by itself. A clean result is no match, or matches you can name, such as an embed you pasted. Run it again for<iframe. Without WP-CLI, use the search in a database tool such as phpMyAdmin.bashwp db search "<script" --table_column_once --skip-plugins --skip-themesStep 8: List the administrators
WordPress's documentation gives a user nobody created as a clear sign of a hack. Every name here should be a person you can account for, with their own email address. Without WP-CLI, open the Users screen and select the Administrator link above the table.
bashwp user list --role=administrator --fields=ID,user_login,user_email,user_registered --skip-plugins --skip-themes
These steps cover where a redirect is planted. How to check whether your WordPress site has been hacked covers the wider checks: the uploads folder, scheduled events, recently changed files and the access log.
What to do now
If the checks pointed at an ordinary cause, fix that one thing: the address, the redirect, the domain or the script. Then run the four curl requests again. If they pointed at a hack, or you cannot tell, do these in order.
Step 1: Leave what you found where it is
Do not delete the rule, the file or the user. The site as it stands is the record of what was done to it, and deleting the one thing you can see leaves the things you cannot. Keep your notes and the files
curlsaved.Step 2: Follow the runbook for the first hour
The hacked site runbook has the order: have the host take the site offline for the public if it is harming visitors, take a full copy of the files and the database, then change the passwords and the secret keys. Take the copy before any cleaning.
Step 3: Then do the full cleanup
How to remove malware from a hacked WordPress site has the steps: restore a backup from before the break-in, or replace everything that has a clean original and check the rest by hand. It also covers finding the way in.
Get help when the redirect comes back after you removed it, when you find code you cannot read, or when the site takes orders or holds customer accounts. Our malware removal service is a one-time clean-up with hardening, a blacklist removal request and a written report.
After it is clean
Why the redirect comes back
- Something was left behind. Google's guidance warns that a hacker may leave "back doors" that allow re-entry, or that put back the malicious code you removed. It also says that if an infected file remains on the server, the site is more likely to be hacked again.
- The way in is still open. Google lists the usual ones: a virus on an administrator's computer, weak or reused passwords, out-of-date software, and permissive code. It adds that there may be several independent hacks in place, so finding one hole is not a reason to stop looking.
WordPress's documentation ends its own list the same way: update everything, and change every password again once the site is clean.
Ask Google for a review if the site was flagged
Cleaning the site does not lift a warning by itself. Google says you must request a review to have a site unflagged. If the Security issues report lists an issue, fix it on every page, not only on the sample addresses, then select "Request Review" in the report. Google asks that the request explain the exact issue, describe the steps you took and document the outcome.
Google says most reviews take several days or weeks, and that you are emailed when one is complete. Do not send a second request while the first is open. Google's help warns that asking before the issue is fixed can lengthen the next review, or get the site marked as a repeat offender. If the report shows no issue, there is nothing to request.
Common questions
Why does the redirect only happen on phones, or only from Google?
Because the rule was written that way. Google's spam policies describe hacked redirects that depend on the referrer, the user agent or the device, and its guidance for hacked sites says the aim is to reach real visitors while avoiding detection by the site's owner. A redirect that only phones get can also come from an ad script, so check the Security issues report first.
A visitor says they were redirected, and I cannot see it. Who is right?
Possibly both. A planted redirect can leave out anyone who typed the address, anyone not on a phone and anyone who is logged in. Run the four curl requests on this page before deciding. If they are clean and only one person reports it, ask whether it happens to them on other sites, which points at their own device.
Can I delete the bad line from .htaccess and be done?
That stops this redirect and settles nothing else. Whoever added the line could write to your files, and Google's guidance warns of back doors that put removed code back. Take a copy of the site first, then do the full cleanup and find the way in.
I cleaned the site and I am still redirected. Is it back?
Check with curl or the redirect checker before deciding. A 301 is a permanent redirect, and a browser is allowed to store one and keep following it without asking your site again. Both tools ask fresh. If they show the redirect too, it is back.
- Cost guideWhat WordPress malware removal costs: how it is priced and what a cleanup should include
- ResourceHacked WordPress site: a runbook for the first hour
- GuideWordPress search results pages (/?s=) in Google and Search Console: what they are and what to do
- GuideHow to audit a WordPress site for security weaknesses
- GuideHow to remove malware from a hacked WordPress site
- GuideHow to set up two-factor authentication in WordPress

