Open the access log of any WordPress site that has been online for more than a few days and search for wp-login.php. You will find hundreds, often thousands, of entries from IP addresses you have never heard of, all trying common username and password combinations. WordPress login is one of the most heavily attacked endpoints on the public web, simply because the URL is universal. Every WordPress site in the world serves the login form at /wp-login.php and /wp-admin. That makes it trivially easy for botnets to attack at scale.
Moving the login URL to something custom does not fix the underlying problem (someone with a real interest in your specific site will find the new URL anyway), but it removes the site from the millions of automated lists that scrape and pound the default endpoint. In practice that drops login traffic by 95% or more, which is a meaningful win for server load alone. This guide explains what the change actually accomplishes, the various ways to implement it, and the pitfalls to avoid.
What changing the login URL does and does not do
Worth being precise about, because the topic gets misrepresented in both directions. Some guides oversell it as a complete defense, others dismiss it as security theater. The accurate position is between the two.
What it does well:
- Stops automated botnets that target
/wp-login.phpdirectly. The vast majority of brute force traffic is dumb scripts iterating through wordlists. They do not look for the new URL because that requires a real visit and HTML parsing. - Removes you from "WordPress login present" filter lists that scanners build to find targets quickly.
- Drastically reduces server load and log noise. On busy sites this alone is reason enough.
- Cuts down on accidental admin visibility. The login page leaks just by existing: error messages, the favicon, the WordPress logo, the version of the login template. Hiding the URL hides all of that.
What it does not do:
- Stop a determined attacker who specifically targets your site. They will find the new URL through any of: the login redirect that happens after logout, the password reset email that links to the new URL, the cookie path, a leaked link in your CMS, or just by guessing common slugs.
- Replace strong passwords or two factor authentication. The login URL is one layer in front of the actual auth. The real defense is still what is on the form, not where the form lives.
- Protect against application level vulnerabilities. If a plugin has a remote code execution bug, the login URL is irrelevant.
The pragmatic frame: changing the login URL is a high return, low effort change that addresses one specific class of attack (untargeted automated brute forcing). It pairs well with strong passwords, 2FA, and a rate limiter, but does not replace any of them.
The recommended way: WPS Hide Login (or equivalent)
For 95% of sites, the cleanest solution is the free plugin WPS Hide Login. It is small (under 100KB), has been actively maintained for years, has over a million active installations, and does exactly one thing: changes the login URL slug. No upsells, no settings panel cluttered with unrelated features.
Setup takes one minute:
- Install WPS Hide Login from the plugin directory.
- Activate it.
- Go to Settings, WPS Hide Login.
- Enter your new slug in the "Login URL" field. Examples:
my-secret-login,backstage,access-2347. Avoid obvious choices likeadmin,secret,login, your domain name with a number appended, etc. - Optionally set a "Redirection URL" for visits to the old
/wp-login.php. The default behavior returns a 404, which is what you want against scanners. A custom redirect to the homepage is also fine. - Save.
From this point forward, https://yourdomain.com/wp-login.php returns a 404 to outsiders, and you log in via https://yourdomain.com/my-secret-login instead. Bookmark the new URL immediately. The plugin does not store the slug anywhere visible from the outside, so if you forget the URL and you do not have FTP access, you have a problem (though there is a recovery path described below).
Alternative: a custom slug without a plugin
If you prefer not to add another plugin, you can implement the same effect in code. Drop this into a must use plugin under wp-content/mu-plugins/custom-login-url.php:
<?php
/**
* Plugin Name: Custom Login URL
* Description: Hides /wp-login.php behind a custom slug.
*/
if (!defined('ABSPATH')) {
exit;
}
const CUSTOM_LOGIN_SLUG = 'my-secret-login';
add_action('init', function () {
$requestUri = isset($_SERVER['REQUEST_URI']) ? (string) $_SERVER['REQUEST_URI'] : ';
$path = parse_url($requestUri, PHP_URL_PATH) ?? ';
$path = trim($path, '/');
// Allow access via the custom slug by serving wp-login.php
if ($path === CUSTOM_LOGIN_SLUG) {
require_once ABSPATH . 'wp-login.php';
exit;
}
// Block direct access to the default login URL
if (preg_match('#(^|/)wp-login\.php$#', $path)) {
status_header(404);
nocache_headers();
include get_404_template();
exit;
}
});
// Rewrite the URL in emails and login redirects
add_filter('site_url', function ($url, $path) {
if (str_contains((string) $path, 'wp-login.php')) {
return str_replace('wp-login.php', CUSTOM_LOGIN_SLUG, $url);
}
return $url;
}, 10, 2);
add_filter('wp_redirect', function ($location) {
return str_replace('wp-login.php', CUSTOM_LOGIN_SLUG, $location);
});
The advantage of this approach is full control and zero plugin dependencies. The downside is that you have to maintain it yourself, and edge cases (multisite, custom registration flows, certain page builders that use the login form) need extra handling. For most sites, WPS Hide Login is genuinely the more practical answer.
Choosing a good slug
The slug only has to be non obvious, not cryptographically secret. A few rules of thumb:
- Avoid common variations of "login", "admin" or "wp". Scanners that go beyond
/wp-login.phptypically have a list:/login,/admin,/secure-login,/wp-secret,/backend,/dashboard. Pick something that is not on that list. - Pick something memorable for you, not random. A slug you can type from memory is fine.
my-cat-tofuis much better thank7Hg2Pbecause the latter you will store in a password manager and the former you will not forget. - Do not include the domain or company name.
example-loginonexample.comis the first thing a targeted attacker tries. - Lowercase, no special characters. URLs are technically case sensitive, but real users mistype case all the time. A lowercase slug avoids that whole class of "I cannot log in" tickets.
Common pitfalls and edge cases
A handful of things break or behave unexpectedly when you move the login URL. Worth knowing about up front.
- WooCommerce "My Account" page. The customer login on
/my-account/is a separate flow and is not affected by the change. You only hide the admin login. WooCommerce customers still log in normally. - Two factor authentication plugins. Most major 2FA plugins (Wordfence, WP 2FA, miniOrange, etc.) work fine with WPS Hide Login. They hook into the login process at a layer below the URL and do not care where the form lives. Test once after activating.
- REST API and XML RPC. The login URL change does not affect
/wp-json/or/xmlrpc.php. If you have applications that authenticate against those, they continue to work as before. If you do not actively use XML RPC, this is a good moment to disable it entirely (separate guide in our knowledge base). - Caching plugins. Some aggressive page caches (LiteSpeed Cache with edge caching, Cloudflare APO) can cache the new login URL or, worse, cache a logged in admin response and serve it to logged out visitors. After the change, visit the new URL in a private window. If you see a logged in admin view, the cache is broken; clear it and add the slug to the cache exclusion list.
- Password reset emails. Both WPS Hide Login and the code snippet above rewrite the password reset URL automatically, but custom email plugins or transactional email integrations sometimes hardcode
wp-login.phpin their templates. Send a test password reset to yourself after the change. - Subdirectory installs and multisite. Both work, but watch for the slug being appended to the wrong base URL. If WordPress is in
/blog/, the new URL ishttps://yourdomain.com/blog/my-secret-login, nothttps://yourdomain.com/my-secret-login.
Recovery: what to do if you lock yourself out
This happens. You set up the slug, log out, close the tab, and a week later cannot remember whether you used dashes, underscores, or what the third word was. You are not stuck, but the recovery path depends on the method you used.
If you used WPS Hide Login: deactivate the plugin via SFTP. Either rename the folder wp-content/plugins/wps-hide-login to wps-hide-login.disabled, or delete it entirely. The default /wp-login.php URL comes back immediately. Log in, look up your slug in the plugin settings, then activate the plugin again. Or alternatively, look in the WordPress database directly: the slug is stored in the wp_options table under the key whl_page.
If you used the custom code: SFTP into wp-content/mu-plugins/, open the file, read the slug from the CUSTOM_LOGIN_SLUG constant. Or temporarily rename the file to disable it, log in, then put it back.
In both cases, write the URL down somewhere persistent the moment you set it up. Password manager, project documentation, anything that survives a browser bookmark loss.
How to verify the change is active
- Open
https://yourdomain.com/wp-login.phpin a private browser window. Expected result: a 404 page. - Same check for
https://yourdomain.com/wp-admin/. Should also redirect to the 404, not to the login form. - Open the new URL
https://yourdomain.com/my-secret-login. Should show the standard WordPress login form. - Log in normally, log out, click the "Log out" link. The redirect should land on the new URL, not on
/wp-login.php. - Trigger a password reset for yourself. The email link should point to the new URL.
- Run a fresh InspectWP scan. Login URL related checks should reflect the new state.
Once verified, the only ongoing maintenance is making sure the plugin (or your custom code) keeps working through WordPress core updates, which it should. If a major update ever changes how the login flow works internally, WPS Hide Login is one of the first plugins to ship a patch, usually within days.
The whole change is half an hour of work for a permanent reduction in attack surface and a noticeable drop in server load. There is no good reason to leave WordPress login at its default location on any production site.