2026年11月13日
2026年11月13日
How to Restrict Access to wp-login.php: IP Rules, Basic Auth, Nginx
This article is a localized edition of the Japanese original. Read the Japanese original
The WordPress login screen, wp-login.php, sits at the same location on every site, which makes it a target for password brute-force attacks by bots. It is not unusual to find large numbers of POST /wp-login.php requests from unknown IP addresses in your access logs.
This article explains how to restrict access to wp-login.php, laid out so you can see how the methods differ in effect and effort.
Comparing the options
| Method | Best when | Effect |
|---|---|---|
| Restrict by IP address | Administrators connect from known IPs | Requests from IPs you have not allowed never reach the login screen |
| Add Basic authentication | Your IP address changes | Adds another authentication step before the WordPress password |
| Change the login URL (plugin) | You cannot change server settings | Avoids bots that target the default URL |
| Limit failed login attempts | Any environment | Slows down brute-force attempts |
IP restrictions and Basic authentication block requests at the server, before WordPress (PHP) runs, so they also reduce server load.
Before you configure anything
- Back up
.htaccessor your Nginx configuration file before you start. A mistake in the rules can make the whole site return "500 Internal Server Error". - For IP restrictions, check your current source IP before you set them. If your IP changes (on a home connection, for example), you will lock yourself out too. If that happens, revert the setting via FTP or the server's file manager.
- If traffic passes through a CDN or proxy such as Cloudflare, the server sees the proxy's IP as the source, and IP restrictions will not work as intended. In that case, see How to restrict access to wp-login.php with Cloudflare.
- The password form for password-protected posts also submits to
wp-login.php?action=postpass. With an IP restriction or Basic authentication in place, ordinary visitors can no longer open password-protected pages.
Method 1: Restrict by IP address in .htaccess (Apache 2.4)
Add the following to the .htaccess file in your WordPress folder (the same place as wp-login.php). Write it outside (above) the section between # BEGIN WordPress and # END WordPress that WordPress writes to.
# Allow access to wp-login.php only from the specified IP addresses
<Files "wp-login.php">
Require ip 203.0.113.1 198.51.100.0/24
</Files>
203.0.113.1is an example of a single IP address, and198.51.100.0/24an example of an address range such as an office network. Replace them with the values for your environment.- Apache configuration does not allow
#comments partway through a line. Put comments on their own line. Order Deny,Allow/Deny from all/Allow from, often seen in older articles, is Apache 2.2 syntax. On Apache 2.4 it causes an error whenmod_access_compatis disabled, so useRequire.
After setting it up, open https://example.com/wp-login.php from a connection you have not allowed (such as a smartphone's mobile network) and confirm that you get "403 Forbidden".
Method 2: Add Basic authentication (Apache)
If your source IP is not fixed, put Basic authentication on wp-login.php. The login screen does not appear until you pass the browser's username and password prompt.
First, create the password file. Put it outside the public directory (the area visible from a browser).
# Included in the apache2-utils package on Ubuntu / Debian
htpasswd -c /home/user/.htpasswd admin-user
-c is the option that creates a new file. When adding a second user or more, run it without -c (with it, the existing contents are erased).
Add the following to .htaccess. Write AuthUserFile as the full path on the server (a path starting with /).
<Files "wp-login.php">
AuthType Basic
AuthName "Restricted"
AuthUserFile /home/user/.htpasswd
Require valid-user
</Files>
Basic authentication sends the username and password without encryption, so it assumes your site runs on HTTPS. On rental hosting, you may also be able to set up access restrictions (Basic authentication) from the control panel. For details, see How to password-protect with .htaccess.
Method 3: Restrict with Nginx
In Nginx, you write allow and deny per location. The key point is to also put the PHP execution settings inside the restricting location. Nginx uses only one location per request, so if location = /wp-login.php contains only allow / deny, PHP is not executed and the file is downloaded or not shown.
Use the same PHP settings as the contents of your server's existing location ~ \.php$ { ... } block. The example below is for Ubuntu 24.04 (PHP 8.3, PHP-FPM).
# Put this inside server { ... }
location = /wp-login.php {
allow 203.0.113.1;
allow 198.51.100.0/24;
deny all;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
To restrict /wp-admin/ as well, nest the PHP settings inside a location marked with ^~. Without ^~, PHP files under /wp-admin/ are handled by your existing location ~ \.php$ instead, and the restriction does not apply.
location ^~ /wp-admin/ {
allow 203.0.113.1;
deny all;
# Let anyone use admin-ajax.php, which front-end features rely on
location = /wp-admin/admin-ajax.php {
allow all;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
}
Themes and plugins use admin-ajax.php for features aimed at visitors who are not logged in (form submissions, "load more" buttons and so on), so it is excluded from the restriction. For the same reason, if a plugin uses admin-post.php from the front end, that file also needs an exclusion.
After writing the configuration, check the syntax before applying it.
sudo nginx -t
sudo systemctl reload nginx
For Basic authentication, see How to set up Basic authentication in Nginx.
Method 4: Change the login URL (WPS Hide Login)
If you cannot change server settings, you can use a plugin that changes the login screen's URL. A well-known one is "WPS Hide Login" (listed in the official wordpress.org directory as of November 2026).
- Go to "Plugins" → "Add New", search for "WPS Hide Login", install it and activate it
- In "Settings" → "WPS Hide Login", enter the new login URL (for example
my-login) and save - Log out and confirm that you can log in at
https://example.com/my-login
After the change, opening wp-login.php or /wp-admin/ while logged out no longer shows the login screen.
- Write down the new URL. If you forget it, check the value of
whl_pagein the database'swp_optionstable, or delete thewp-content/plugins/wps-hide-loginfolder via FTP so you can log in through the originalwp-login.phpagain (as the plugin's official page explains). - If you use a page caching plugin, exclude the new login URL from caching.
- This only hides the URL, so WordPress (PHP) still runs when bots hit the site. To reduce server load, methods 1 to 3 are more effective.
Method 5: Limit failed login attempts (code)
Stop accepting logins for a while from an IP address that has failed a certain number of times. The code below is an example that "stops logins for 15 minutes after 5 failures within 15 minutes".
Save it as a file named wp-content/mu-plugins/login-limit.php and it keeps working even if you change themes (create the mu-plugins folder if it does not exist).
<?php
/**
* Plugin Name: Login attempt limit
*/
function wpdr_login_limit_key() {
$ip = isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
return 'wpdr_login_fail_' . md5( $ip );
}
// Count each failed login (expires after 15 minutes)
add_action( 'wp_login_failed', function () {
$key = wpdr_login_limit_key();
$count = (int) get_transient( $key );
set_transient( $key, $count + 1, 15 * MINUTE_IN_SECONDS );
} );
// Refuse logins after 5 or more failures
add_filter( 'authenticate', function ( $user ) {
if ( (int) get_transient( wpdr_login_limit_key() ) >= 5 ) {
return new WP_Error(
'too_many_attempts',
'Too many failed login attempts. Please wait a while and try again.'
);
}
return $user;
}, 99 );
wp_login_failedis the action WordPress runs when a login fails. Attempts made while logins are blocked are also counted as failures, so a source that keeps trying stays blocked the whole time.- Logins through XML-RPC (
xmlrpc.php) also go through the sameauthenticatefilter, so this limit applies to them too. - Behind a CDN or reverse proxy,
REMOTE_ADDRis the proxy's IP, and everyone gets blocked together. Do not use this code in that setup.
For plugin-based approaches and more background, see How to limit login attempts without a plugin and Limiting login attempts.
Other things to check
- xmlrpc.php: Logins can also be attempted through XML-RPC. If you do not use it for Jetpack or external blogging apps, you can block access to it at the server.
- Passwords and two-factor authentication: None of these methods replaces a password that is hard to guess. Use a long password for administrator accounts, and consider adding a two-factor authentication plugin.
Summary
If administrators connect from known IPs, an IP restriction at the server is the least effort for the most effect. If your IP is not fixed, use Basic authentication, and if you cannot change server settings, combine changing the login URL with limiting failed login attempts. Whichever you choose, confirm both that "it cannot be opened from an environment you have not allowed" and that "you can still log in" after setting it up.
Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)