Custom WordPress Login Page — Logo, Background, Colors, URL Title (No Plugin)
Replace the WordPress login page logo with your brand mark, swap the background, recolour the form, change the logo’s link target, and customise the page title — all in 30 lines of PHP. No plugin, no settings UI, nothing to break on theme switch. Bundled as a toggleable module in my Functionalities plugin if you want the UI route.
A branded login page is one of the smallest changes that lifts a client site from “looks like WordPress” to “looks considered”. The default login screen — blue background, giant W wordmark, link pointing to wordpress.org — screams stock install. Five minutes of work, consistent return on perceived polish. I’ve added this to every client site I’ve handed off since 2018. The code below is the full mu-plugin: upload a logo, drop in a few CSS lines, ship. For the Functionalities plugin users, this is the module that ships pre-configured — toggle it on, upload a logo via the admin UI, done. Either path gets you the same result.
What this snippet does
- Replaces the WordPress logo with your brand mark (PNG, SVG, or WebP — my recommendation is 200×60px PNG at 2x DPR)
- Changes the logo’s link from wordpress.org to your own homepage — visitors who click the logo go to your site, not WP’s
- Changes the logo’s title attribute from “Powered by WordPress” to your brand name for screen readers + hover tooltip
- Restyles the login form — background colour or image, form card shadow and radius, button colours matching your brand
- Adds a subtle background image — full-bleed brand visual instead of WordPress’s light grey
- Customises the page title in the browser tab — “Log in · Your Brand” instead of “Log in · WordPress Site”
- Dark mode support — reads
prefers-color-schemeand serves a dark variant of the form card and colours - Zero JS — pure PHP filter + inline CSS, loads before the login form renders so there’s no flash of unstyled content
- Works with 2FA plugins — layered on the standard wp-login.php, so Two Factor / Duo / Google Authenticator-style plugins render their additional fields with the same branding
- Compatible with login URL renaming — pair with the harden-login snippet for a custom slug AND custom branding
Install and configure
Upload your logo to wp-content/uploads/login-logo.png (or edit the path in the snippet). Drop the PHP below into wp-content/mu-plugins/gt-login-brand.php. Edit the brand colours at the top to match your palette. Reload /wp-login.php — the new look should render immediately. If it doesn’t, check that your logo URL is accessible and that no other plugin is overriding the login styles (some security plugins do this).
<?php
/**
* Plugin Name: GT Login Page Branding
* Description: Replaces the WordPress login page logo, background, colours, URL, and title.
*/
defined( 'ABSPATH' ) || exit;
/* ============================================================
* Configuration — edit to match your brand
* ============================================================ */
const GT_LOGIN_LOGO_URL = '/wp-content/uploads/login-logo.png';
const GT_LOGIN_LOGO_W = '200px';
const GT_LOGIN_LOGO_H = '60px';
const GT_LOGIN_PRIMARY = '#6366f1'; /* Form button background, links */
const GT_LOGIN_PRIMARY_DARK = '#4f46e5';/* Button hover state */
const GT_LOGIN_BG = '#f9fafb'; /* Page background */
const GT_LOGIN_BRAND = 'Gaurav Tiwari'; /* Used in page title + logo alt */
/* ============================================================
* 1. Inject custom CSS into the login head
* ============================================================ */
add_action( 'login_enqueue_scripts', function () {
$logo = esc_url( GT_LOGIN_LOGO_URL );
$w = esc_attr( GT_LOGIN_LOGO_W );
$h = esc_attr( GT_LOGIN_LOGO_H );
$primary = esc_attr( GT_LOGIN_PRIMARY );
$primary_dark = esc_attr( GT_LOGIN_PRIMARY_DARK );
$bg = esc_attr( GT_LOGIN_BG );
?>
<style id="gt-login-brand">
body.login {
background: <?php echo $bg; ?>;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.login h1 a {
background-image: url('<?php echo $logo; ?>');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
width: <?php echo $w; ?>;
height: <?php echo $h; ?>;
margin-bottom: 2rem;
}
.login form {
border-radius: 0.6rem;
box-shadow: 0 10px 40px rgba(0,0,0,.08), 0 1px 2px rgba(0,0,0,.04);
border: 1px solid rgba(0,0,0,.06);
padding: 2rem 1.75rem;
}
.login label {
font-size: 0.9rem;
font-weight: 500;
}
.login input[type="text"],
.login input[type="password"],
.login input[type="email"] {
border-radius: 0.4rem;
border: 1px solid rgba(0,0,0,.15);
padding: 0.55rem 0.75rem;
font-size: 0.95rem;
box-shadow: none;
}
.login input[type="text"]:focus,
.login input[type="password"]:focus,
.login input[type="email"]:focus {
border-color: <?php echo $primary; ?>;
box-shadow: 0 0 0 3px color-mix(in srgb, <?php echo $primary; ?> 20%, transparent);
}
.wp-core-ui .button-primary {
background: <?php echo $primary; ?>;
border-color: <?php echo $primary; ?>;
border-radius: 0.4rem;
text-shadow: none;
box-shadow: none;
font-weight: 600;
height: 2.5rem;
transition: background 0.15s;
}
.wp-core-ui .button-primary:hover,
.wp-core-ui .button-primary:focus {
background: <?php echo $primary_dark; ?>;
border-color: <?php echo $primary_dark; ?>;
}
.login #nav a,
.login #backtoblog a,
.login .privacy-policy-link {
color: <?php echo $primary; ?>;
text-decoration: none;
}
.login #nav a:hover,
.login #backtoblog a:hover,
.login .privacy-policy-link:hover {
color: <?php echo $primary_dark; ?>;
text-decoration: underline;
}
/* Dark mode */
@media (prefers-color-scheme: dark) {
body.login { background: #0b0f19; color: #e5e7eb; }
.login form { background: #1f2937; border-color: #374151; }
.login label { color: #e5e7eb; }
.login input { background: #111827; border-color: #374151; color: #f9fafb; }
.login #nav, .login #backtoblog { color: #9ca3af; }
}
</style>
<?php
} );
/* ============================================================
* 2. Change the logo's link target from wordpress.org to your site
* ============================================================ */
add_filter( 'login_headerurl', fn () => home_url( '/' ) );
/* ============================================================
* 3. Change the logo's title/alt attribute
* ============================================================ */
add_filter( 'login_headertext', fn () => GT_LOGIN_BRAND );
/* ============================================================
* 4. Customise the page <title>
* ============================================================ */
add_filter( 'login_title', function ( $title ) {
return sprintf( '%s · %s', esc_html( GT_LOGIN_BRAND ), 'Log in' );
} );
/* ============================================================
* 5. Remove the "Register" and "Lost password?" language if you don't use them
* Comment out if you need these
* ============================================================ */
/*
add_filter( 'login_display_language_dropdown', '__return_false' );
*/How each piece works
login_enqueue_scripts is WordPress’s hook for firing code on the login page specifically — it runs before the login form renders, so your CSS loads in the head without a flash of unstyled content. The default CSS targets .login h1 a which WordPress outputs for the logo link; replacing its background image swaps the logo cleanly. .login form is the white card; styling it gives you the modern look. The .wp-core-ui .button-primary class targets the “Log In” button — core WordPress styles win by default, so you need the .wp-core-ui prefix for specificity. login_headerurl filters where the logo link points — without it, clicking the logo sends visitors to wordpress.org. login_headertext sets the title attribute and alt text of the logo link, used by screen readers. login_title sets the page <title>, which shows in the browser tab. Dark mode is handled with CSS prefers-color-scheme — the form card inverts its background, input fields become dark-mode-friendly, text colours adjust automatically. All five filters/hooks are WordPress-native and stable across every WP version from 5.0 through 6.9 and beyond. No JS, no admin UI, no database storage — the branding is part of the mu-plugin file and versions cleanly with your site code.
Download and source
- Full mu-plugin: gist.github.com/wpgaurav — search gt-login-brand
- Bundled as a module in the Functionalities plugin — provides an admin UI to upload the logo and tweak colours without touching PHP
- For a UI-based alternative if you prefer click-configuration: Login Customizer (free, decent, more features)
- For a branded login page with member-area features: LoginPress (freemium, heavier)
- Logo design note: 200×60px PNG at 2x DPR (so 400×120px source) hits the retina-sharp sweet spot. SVG works but make sure the file has correct viewBox or the login page layout can break on some themes.
FAQs
Where should I save the logo file?
wp-content/uploads/login-logo.png is the simplest — survives core updates, accessible by relative URL. If you already have the logo in the Media Library, use its media URL. Avoid wp-content/themes/ paths because they break on theme switch. Avoid wp-admin/ because core updates overwrite the folder.
What logo format should I use?
PNG at 200×60px, saved at 2x DPR (source 400×120px), transparent background. WebP works and is smaller but older browsers (including some corporate IE11 holdouts still hitting admin) don’t render it. SVG works if you trust your source — WordPress restricts SVG uploads by default; if you upload via SFTP it’s fine. Always include transparent background so it works on any login page colour.
Will this override 2FA plugins’ styling?
The CSS styles the container and form card, which 2FA plugins render their extra fields inside. Most 2FA plugins (Two Factor, Duo, WordFence 2FA, Aioseo Login) honour the parent form styling and look good by default. If a specific plugin’s 2FA screen looks off, add extra selectors targeting that plugin’s specific classes — inspect the element and extend the CSS.
Can I use different logos for different multisite sites?
Yes. Wrap the logo URL in a conditional: $logo = is_main_site() ? '/wp-content/uploads/main-logo.png' : '/wp-content/uploads/' . get_current_blog_id() . '-logo.png';. Each subsite can then use its own file. Or store the logo URL as a site option and read it via get_option() for user-configurable per-site branding.
What about the ‘Back to Site Name’ link below the login form?
It uses the site name from WordPress general settings. Change Settings → General → Site Title and the link updates automatically. To change the link destination, filter login_backtoblog_url — not in the minimal snippet above, but available.
Will this survive a WordPress core update?
Yes — the mu-plugin lives in wp-content/mu-plugins/ which WordPress never touches. The filters target stable WordPress hooks that have existed since WordPress 3.0+ and show no signs of changing. Tested through every WP major from 5.0 to 6.9.
Can I add a background image instead of a flat colour?
Yes. Change body.login { background: #f9fafb; } to body.login { background: url('/wp-content/uploads/login-bg.jpg') center / cover no-repeat #f9fafb; }. Keep the fallback colour in case the image fails to load. A subtle gradient or a blurred brand photo works well; loud full-colour images compete with the form for attention.
How does this compare to the Functionalities plugin approach?
Same code under the hood — the Functionalities plugin just wraps it with an admin UI that lets non-developers upload a logo and pick colours from a colour picker. The raw mu-plugin is right when you prefer config-in-code (versioned in Git, applied the same across staging and production). The plugin UI is right for sites where the end client occasionally wants to tweak the logo themselves.
Can I A/B test two different login page designs?
Yes, but probably shouldn’t. The login page sees admin traffic and bot traffic — neither of which is a good sample for design-testing decisions. A/B test public-facing pages where conversion matters. Login page design is about brand consistency, not conversion optimisation.
What if my host puts a security plugin in front of wp-login that overrides my styles?
Check what’s active — Sucuri, MalCare, Wordfence’s login portal feature all sometimes skin wp-login.php with their own shell. Disable the security plugin’s login skin feature (it’s usually a toggle, not a core feature) and your styles come back. For WP Engine’s login shield specifically, log in through the WP Engine User Portal instead of /wp-login.php.
Related snippets
- Harden wp-login.php without a plugin — combine branding with rate-limiting and URL renaming
- Log every failed login attempt
- Functionalities plugin