Ask your WordPress questions! Pay money and get answers fast! Comodo Trusted Site Seal
Official PayPal Seal

How to use .htaccess to secure wp-login.php and /wp-admin on WPMU WordPress

  • SOLVED

<strong>Please note: I'm looking for a working solution to this problem, not a variety of things to test so I can write the solution myself. Thanks for your help!</strong>

I've got a WordPress Multisite network running on the latest version of WordPress. Sites are accessible on a subdomain of the main domain. For example, a site might be http://joe.domain.com. The network has WPMU Domain Mapping loaded on it so users will ultimately access their site with their own domain (e.g. www.realdomain.com) but the administration is always done through their subdomain.

1) The login page and admin area for the root site are only accessible from a specific list of IP addresses. For example:
a) www.domain.com/wp-admin (and all subordinate files and directories)
b) domain.com/wp-admin (and all subordinate files and directories)
c) www.domain.com/wp-login.php
d) domain.com/wp-login.php

2) The login page and admin area are only accessible from subdomains of the main domain. For example, http://joe.domain.com/wp-admin or http://joe.domain.com/wp-login.php. The subdomains that might be used here could be anything and shouldn't be hardcoded.

3) The login page and admin area are NOT accessible from any other top level domains. For example, http://www.realdomain.com/wp-admin or http://www.realdomain.com/wp-login.php. The domains that might be used here could be anything and shouldn't be hardcoded.

I'm open to suggestions on what "inaccessible" means but would prefer that the user be redirected to a specific URL where a custom message would be displayed. They should not receive an error message directly from the web server.

If you have questions, let me know. I'm happy to clarify.

Thanks!

Answers (3)

2013-09-26

Eric P. answers:

Personally, I prefer Using the .htaccess in the wp-admin directory to limit access to that directory.

So in wp-admin/.htaccess, I put:

order allow,deny
deny from all
allow from xx.xx.xx.xx # Specific IP
Allow from xx.xx.xx.0/24 # allow from a specific network/CIDR block



It's much cleaner to have the .htaccess file in the actual directory you are protecting. That way, you can't mess it up when you're working on the parent directory.

For login.php, you do have to put it in the main directory. It goes after the REWRITE ENGINE on, but before the other entries for WordPress. Otherwise WordPress index.php will catch it and not limit access.

Follow Up/edit.

I often use mod_rewrite for this, to return 404 error pages instead of "403 Access Denied." That makes it look less like a wordpress installation to random bots and hackers. It looks like there is no wp-admin directory and there is no login.php.

If you want examples of .htaccess entries for that, contact me.

UPDATE:

I'm adding my code here from the follow up answer, with corrections to the typos in the followup answers.

OK. For the wp-admin part, try this in the wp-admin/.htaccess file:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /wp-admin/
RewriteCond %{HTTP_HOST} ^(www\.)?domain\.com$ [NC]
RewriteCond%{REMOTE_HOST} =xxx.xxx.xxx.xxx [OR]
RewriteCond%{REMOTE_HOST} =xxx.xxx.xxx.xxx [OR]
RewriteCond%{REMOTE_HOST} =xxx.xxx.xxx.xxx
RewriteRule ^(.*)$ $1 [L,QSA]
RewriteCond %{HTTP_HOST} ^(www.)?domain.com$ [NC]
RewriteRule ^(.*)$ 404.php [R=404,L]
RewriteCond %{HTTP_HOST} ^.+\.domain\.com$ [NC]
RewriteRule ^(.*)$ [L,QSA]
RewriteCond %{HTTP_HOST} !^.+\.domain\.com$ [NC]
RewriteRule ^(.*)$ 404.php [R=404,L]
</IfModule>


I theory, the first lines up to the first RewriteRule allow access from the hosts specified iin the REMOTE_HOST list. The second RewriteRule gives the 404 page for the main domain to anyone not in the REMOTE_HOST list. The third RewriteRule is for all subdomains (joe.domain.com/wp-admin, fred.domain.com/wp-admin, etc.) and allows access anywhere.

Now, I don't have a multi-site network setup to test on. Is there only one index.php and config.php and main wordpress directory for a multi-site network, with exactly one wp-login.php file? If that's the case, then these lines should go in .htaccess right after the RewriteEngine on and Rewrite Base lines in the .htaccess file in the main wordpress directory:

RewriteCond %{HTTP_HOST} ^(www\.)?domain\.com$ [NC]
RewriteCond%{REMOTE_HOST} =xxx.xxx.xxx.xxx [OR]
RewriteCond%{REMOTE_HOST} =xxx.xxx.xxx.xxx [OR]
RewriteCond%{REMOTE_HOST} =xxx.xxx.xxx.xxx
RewriteRule ^(wp-login\.php)$ $1 [L,QSA]
RewriteCond %{HTTP_HOST} ^(www.)?domain.com$ [NC]
RewriteRule ^(wp-login\.php)$ 404.php [R=404,L]
RewriteCond %{HTTP_HOST} ^.+\.domain\.com$ [NC]
RewriteRule ^(wp-login\.php)$ $1 [L,QSA]
RewriteCond %{HTTP_HOST} !^.+\.domain\.com$ [NC]
RewriteRule ^(wp-login\.php)$ 404.php [R=404,L]


This will allow access to the wp-login.php file for specified REMOTE_HOSTs on the main domain and the www. on the main domain, allow subdomains of the main domain to access wp-login.php from anywhere, and deny (404) access from any domains that aren't in the domain.com domain, but still hit this server for some reason (cname records, A records pointing at this IP, whatever).

Is that what you intend?


Kevin McGillivray comments:

Thanks for your reply. This doesn't address my particular situation since it limits access to the entire directory on all domains and doesn't address the situations I outlined above. Can you provide a complete solution that addresses the items I mention in my original post?


Eric P. comments:

Yeah, I'm doing some research for you on the network setup for WordPress for multisite networks. I'll be back with a solution.


Eric P. comments:

OK. For the wp-admin part, try this in the wp-admin/.htaccess file:

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_HOST} ^(www\.)?domain\.com$ [NC]
RewriteCond%{REMOTE_HOST} =xxx.xxx.xxx.xxx [OR]
RewriteCond%{REMOTE_HOST} =xxx.xxx.xxx.xxx [OR]
RewriteCond%{REMOTE_HOST} =xxx.xxx.xxx.xxx
RewriteRule ^(.*)$ $1 [L]
RewriteCond %{HTTP_HOST} ^(www.)?domain.com$
RewriteRule ^(.*)$ 404.php [R=404,L]
RewriteCond %{HTTP_HOST} ^.+\.domain\.com$
RewriteRule ^(.*)$ [L]
</IfModule>

I theory, the first lines up to the first RewriteRule allow access from the hosts specified iin the REMOTE_HOST list. The second RewriteRule gives the 404 page for the main domain to anyone not in the REMOTE_HOST list. The third RewriteRule is for all subdomains (joe.domain.com/wp-admin, fred.domain.com/wp-admin, etc.) and allows access anywhere.

Now, I don't have a multi-site network setup to test on. Is there only one index.php and config.php and main wordpress directory for a multi-site network, with exactly one wp-login.php file? If that's the case, then these lines should go in .htaccess right after the RewriteEngine on and Rewrite Base lines in the .htaccess file in the main wordpress directory:


RewriteCond %{HTTP_HOST} ^(www\.)?domain\.com$ [NC]
RewriteCond%{REMOTE_HOST} =xxx.xxx.xxx.xxx [OR]
RewriteCond%{REMOTE_HOST} =xxx.xxx.xxx.xxx [OR]
RewriteCond%{REMOTE_HOST} =xxx.xxx.xxx.xxx
RewriteRule ^(wp-login\.php)$ $1 [L QSA]
RewriteCond %{HTTP_HOST} ^(www.)?domain.com$
RewriteRule ^(wp-login\.php)$ 404.php [R=404,L]
RewriteCond %{HTTP_HOST} ^.+\.domain\.com$
RewriteRule ^(wp-login\.php)$ $1 [L QSA]
RewriteCond %{HTTP_HOST} !^.+\.domain\.com$
RewriteRule ^(wp-login\.php)$ 404.php [R=404,L]


This will allow access to the wp-login file for specified REMOTE_HOSTs on the main domain and the www. on the main domain, allow subdomains of the main domain to access wp-login.php from anywhere, and deny (404) access from any domains that aren't in the domain.com domain, but still hit this server for some reason (cname records, A records pointing at this IP, whatever).

Is that what you intend?


Eric P. comments:

Doh!!! it doesn't let me edit the follow up reply. I see a couple of typos there.

Here's the summary.

For wp-admin/.htaccess, the first and third RewriteRule lines should be:


RewriteRule ^(.*)$ $1 [L,QSA]

I left out the QSA on both, and also left out the $1 on the third RewriteRule.

In the main .htaccess file, where I put "[L QSA]" it should be "[L,QSA]", with a comma. I think a space might be wrong enough to be a problem.


Eric P. comments:

Also, note that I don't have a multi-site network to test this on, so these are untested by me.


Eric P. comments:

Use the code in my main answer, it fixes a couple of missing [NC] directives and all the other typos I noted above.


Kevin McGillivray comments:

Eric, thanks so much for this. From your description, it sounds like what I want. I'm going to test it today. Stay tuned.


Kevin McGillivray comments:

Hey, Eric. Thanks for all your help. I was able to use something very close to the scripts you sent. The 404 redirects aren't working correctly, though. I get a non-specific error message in Chrome saying that there's a redirect loop, so I did 301s back to the front page of the site. It doesn't (sort of) obscure WordPress like a 404 would so it's not ideal.

Here's what I have in the root of the site:

# if the user is accessing from the correct IP
RewriteCond %{HTTP_HOST} ^(www\.)?domain.com$ [NC]
RewriteCond %{REMOTE_ADDR} ^xxx\.xxx\.xxx\.xxx7$

# let them view the login page
RewriteRule ^(wp-login\.php)$ $1 [L,QSA]

# if user is trying access the admin for the root site
RewriteCond %{HTTP_HOST} ^(www.)?domain.com$ [NC]
# redirect them to the front page of the site
RewriteRule ^(wp-login\.php)$ http://domain.com [R=301,L]

# if user is trying to access it for a subdomain,
RewriteCond %{HTTP_HOST} ^.+\.domain\.com$ [NC]
# let them access it
RewriteRule ^(wp-login\.php)$ $1 [L,QSA]

# if the user is from anything else
RewriteCond %{HTTP_HOST} !^.+\.domain\.com$ [NC]
# redirect them to the front page of their site
RewriteRule ^(wp-login\.php)$ http://domain.com/ [R=301,L]


And here's what I have in /wp-admin.

# if the user is accessing from the correct IP
RewriteCond %{HTTP_HOST} ^(www\.)?domain.com$ [NC]
RewriteCond %{REMOTE_ADDR} ^xxx\.xxx\.xxx\.xxx$

# let them view the login page
RewriteRule ^(.*)$ $1 [L,QSA]

# if user is trying access the admin for the root site
RewriteCond %{HTTP_HOST} ^(www.)?domain.com$ [NC]
# redirect them to the front page of the site
RewriteRule ^(.*)$ http://domain.com [R=301,L]

# if user is trying to access it for a subdomain,
RewriteCond %{HTTP_HOST} ^.+\.domain\.com$ [NC]
# let them access it
RewriteRule ^(.*)$ $1 [L,QSA]

# if the user is from anything else
RewriteCond %{HTTP_HOST} !^.+\.domain\.com$ [NC]
# redirect them to the front page of their site
RewriteRule ^(.*)$ http://domain.com/ [R=301,L]


I had to make a couple other changes (like how the IP address was being recognized) to get the script to work. I'm still going to do some more testing on this today but it seems to do the trick. If you have any advice on the 404 issue, I would definitely appreciate it.

2013-09-26

Yakir Sitbon answers:

Please try this plugin: http://wordpress.org/plugins/better-wp-security/


Kevin McGillivray comments:

That plugin does not do what I need. Please reread my post and reply to my question specifically if you're interested.

2013-09-26

Balanean Corneliu answers:

Add this to your .htaccess:
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress


Balanean Corneliu comments:

+

<files wp-config.php>
order allow,deny
deny from all
</files>


Balanean Corneliu comments:

+
# directory browsing
Options All -Indexes


Balanean Corneliu comments:

And if you need: Disable any Hotlinking
RewriteEngine on
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?YourDomain [NC]
RewriteRule \.(jpg|jpeg|png|gif)$ - [NC,F,L]


Balanean Corneliu comments:

Protect /wp-content Directory

WordPress holds all your media files in here and they’re an asset you want search engines to crawl. But, “/wp-content” is a place where your themes and plugins reside, too. You don’t want to allow access to those sensitive .php files.

In order to work you need to create a separate .htaccess file (just use your FTP client and create a file with no name and give it an “.htaccess” extension) and put it in your /wp-content directory. This code will allow access to images, CSS, java-script and XML files, but deny it for any other type.

order deny,allow
deny from all
<files ~ ".(xml|css|jpe?g|png|gif|js)$">
allow from all
</files>

That’s it. Your WordPress website should be a lot safer place now. There’s just one last thing we should do and that’s protecting the .htaccess file(s).


Balanean Corneliu comments:

Protect the .htaccess Itself

We’ve done a lot to protect WordPress, but the .htaccess file itself is still open to attacks. The following code snippet will stop anyone from accessing (reading or writing) any file that starts with “hta“.

<files ~ "^.*\.([Hh][Tt][Aa])">
order allow,deny
deny from all
satisfy all
</files>


While you can install various WordPress security plugins, sign-up for monitoring services and content delivery networks which filter your traffic, configuring .htaccess file so it strengthens your WordPress security is a good step toward that peace of mind every website owner needs.


Balanean Corneliu comments:

Good Luck with adding code;)


Balanean Corneliu comments:

For last question and i think for all if you whant to block that folder for other and give acces by ip you need to create one .htaccess file in that folder and add just this lines

deny from all
allow from xx.xx.xx.xx(change xx with your ip.)


Balanean Corneliu comments:

Exactly what I said in the last comment on 09/26/13 6:35pm
But more detailed.
Ty Eric P.


Kevin McGillivray comments:

You are not addressing my specific questions. Please reread my question above.


Balanean Corneliu comments:

Setup a script to handle 403 errors by adding this line to your .htaccess:

ErrorDocument 403 /forbidden.php

Then handle the redirect in the script:

<?php
header('Location: http://google.com');


Or to keep it all in .htaccess you could do:

RewriteEngine On
RewriteCond %{REMOTE_ADDR} 127.0.0.1
RewriteRule (.*) http://google.com [R]



Balanean Corneliu comments:

Let me know if im more close now to your wish ?


Kevin McGillivray comments:

Balanean, I appreciate you replying but you're just sending pretty general .htaccess settings but not a solution to my specific case that I outlined above. If you're interested, please reread my requirements above. Thanks for replying.