Jak dostosować stronę logowania WordPress w 2026 (bez wtyczek)

Jak dostosować stronę logowania WordPress w 2026 (bez wtyczek)

Ostatnio zweryfikowano: 21 września 2026
11 min czytania
Przewodnik
Audytor bezpieczeństwa
Full-stack developer

Domyślny ekran wp-login.php jest rozpoznawalny w trzy sekundy: szare tło, logo WordPressa, generyczny formularz. Dla bloga hobbystycznego to akceptowalne. Dla sklepu, portalu klienta albo wdrożenia agencyjnego pod marką klienta to przeciek wizerunkowy. Redaktor loguje się codziennie; pierwsze wrażenie to nie strona główna, tylko ten ekran.

Ten poradnik pokazuje, jak zbudować white-label logowania wyłącznie hookami WordPressa: login_enqueue_scripts, filtry logo (login_headerurl, login_headertext), login_message, utwardzenie komunikatów błędów oraz decyzję mu-plugin kontra motyw. Bez wtyczki „Custom Login”, bez edycji plików rdzenia, bez CSS, które „ukrywa” URL logowania i udaje bezpieczeństwo.

Materiał jest pod polskie wdrożenia produkcyjne: motyw potomny albo must-use plugin, staging z osobnym kontem testowym, checklista regresji po aktualizacji WordPressa. Kod poniżej używa prefiksu wppoland_ (wppoland.com), żeby nie kolidował z innymi snippetami w tym samym projekcie.

#Co WordPress ładuje na wp-login.php

Zanim podmienisz logo, warto wiedzieć, co rdzeń robi przy żądaniu wp-login.php:

  1. Ładuje wp-admin/css/login.css i zależne style kokpitu (przyciski, formularze).
  2. Uruchamia akcję login_enqueue_scripts - tu dokładasz własne CSS i JS, dokładnie jak wp_enqueue_scripts na froncie.
  3. Renderuje nagłówek z linkiem logo (domyślnie na wordpress.org), formularz, linki „Nie pamiętasz hasła?” / „Zarejestruj się”, stopkę.
  4. Przy błędzie walidacji uruchamia wp_shake_js i pokazuje komunikat z filtra login_errors.
  5. Filtry login_message i login_messages pozwalają wstawić HTML nad formularzem (komunikat po wylogowaniu, info o utrzymaniu, banner SSO).

Kluczowa konsekwencja: strona logowania nie przechodzi przez pełny pipeline motywu frontowego. header.php, bloki FSE i większość enqueue z wp_enqueue_scripts tu nie działają. Stąd osobna akcja login_enqueue_scripts i osobny plik CSS. Jeśli wrzucisz style tylko do style.css motywu, na wp-login.php ich nie będzie.

Druga konsekwencja: aktualizacja WordPressa może zmienić selektory, kolejność skryptów albo markup formularza (np. dodatkowe pola przy 2FA). Branding oparty na hookach przeżywa aktualizację; patchowanie wp-login.php w rdzeniu - nie.

#login_enqueue_scripts: CSS i assety

Oficjalny punkt zaczepienia to akcja login_enqueue_scripts. Używaj wp_enqueue_style / wp_enqueue_script, nie wklejaj kilkuset linii CSS w echo wewnątrz login_head, chyba że to naprawdę pięć reguł na logo.

Minimalny enqueue z motywu potomnego:

/**
 * Ładuje style tylko na ekranie logowania.
 */
function wppoland_login_stylesheet() {
	wp_enqueue_style(
		'wppoland-login',
		get_stylesheet_directory_uri() . '/assets/css/style-login.css',
		array(),
		wp_get_theme()->get( 'Version' )
	);
}
add_action( 'login_enqueue_scripts', 'wppoland_login_stylesheet' );

Dlaczego wersja z wp_get_theme()->get( 'Version' ): po deployu CSS nie zostaje w cache przeglądarki pod starym query stringiem. Na mu-pluginie użyj stałej wersji albo filemtime() ścieżki absolutnej.

Przykład style-login.css pod ciemny branding (dostosuj kolory do palety klienta, nie kopiuj ślepo):

body.login {
	background-color: #0d1117;
	background-image: none;
}

.login form {
	background: #161b22;
	border: 1px solid #30363d;
	box-shadow: none;
	border-radius: 8px;
}

.login label,
.login #nav a,
.login #backtoblog a {
	color: #c9d1d9;
}

.wp-core-ui .button-primary {
	background: #238636;
	border-color: #238636;
	text-shadow: none;
	box-shadow: none;
}

.wp-core-ui .button-primary:hover,
.wp-core-ui .button-primary:focus {
	background: #2ea043;
	border-color: #2ea043;
}

.login #login_error,
.login .message,
.login .success {
	border-left-width: 4px;
}

Reguły praktyczne przy CSS logowania:

  • Celuj w klasy, które WordPress faktycznie emituje: body.login, #login, .login form, .wp-core-ui .button-primary, #login_error, .message.
  • Nie chowaj #nav i #backtoblog bez uzgodnienia z klientem - redaktorzy tracą link powrotu na front i odzyskiwanie hasła wygląda „zepsute”.
  • Unikaj display: none na polach formularza „na wszelki wypadek”. Wtyczka 2FA albo passkey doda własne kontrolki; ukrycie ich CSS-em wygląda jak awaria logowania.
  • Testuj kontrast (WCAG) na komunikatach błędów i etykietach - ciemne tło + szary tekst często odpada w audycie dostępności.

Jeśli potrzebujesz fontów klienta, enqueue je tutaj albo hostuj lokalnie w motywie. Trzecia domena na ekranie logowania to zbędny request i kolejny punkt awarii DNS.

#Logo: background-image, login_headerurl, login_headertext

WordPress nie ma filtra „podmień plik logo”. Klasyczny wzorzec to CSS na #login h1 a / .login h1 a (background-image) plus dwa filtry na atrybuty linku.

Podmiana grafiki przez login_enqueue_scripts:

function wppoland_login_logo() {
	$logo = get_stylesheet_directory_uri() . '/assets/images/logo-logowania.svg';
	?>
	<style type="text/css">
		#login h1 a,
		.login h1 a {
			background-image: url(<?php echo esc_url( $logo ); ?>);
			background-size: contain;
			background-repeat: no-repeat;
			background-position: center;
			width: 320px;
			height: 80px;
			margin: 0 auto 16px;
		}
	</style>
	<?php
}
add_action( 'login_enqueue_scripts', 'wppoland_login_logo' );

esc_url() na ścieżce assetu to obowiązek, nawet gdy budujesz URL z get_stylesheet_directory_uri(). Logo trzymaj w SVG lub PNG z przezroczystością; JPEG ze zbędnym białym tłem wygląda jak naklejka na ciemnym formularzu.

Domyślny href logo prowadzi na wordpress.org. Filtr login_headerurl zmienia cel:

function wppoland_login_logo_url() {
	return home_url( '/' );
}
add_filter( 'login_headerurl', 'wppoland_login_logo_url' );

Tekst dostępności (title / zawartość dla czytników) ustawia login_headertext (dawniej login_headertitle - starsze snippetaty w internecie nadal go cytują):

function wppoland_login_logo_title() {
	return get_bloginfo( 'name' );
}
add_filter( 'login_headertext', 'wppoland_login_logo_title' );

Na instalacjach z osobną domeną kokpitu (np. cms.klient.pl vs www.klient.pl) home_url() nadal wskazuje front, co jest zwykle pożądane. Jeśli logo ma prowadzić do dokumentacji wewnętrznej albo do IdP, zwróć pełny URL HTTPS i przetestuj w oknie incognito.

#login_message: komunikaty nad formularzem

Filtr login_message dokłada HTML nad formularzem. Typowe zastosowania produkcyjne:

  • Informacja o oknie utrzymaniowym („Logowanie chwilowo wyłączone poza VPN”).
  • Wskazówka dla redaktorów („Używaj konta firmowego, nie prywatnego Gmaila”).
  • Banner po wylogowaniu albo po resecie hasła (WordPress i tak dokłada własne message classes).
  • Link do SSO / „Zaloguj przez Microsoft”, jeśli IdP jest obok klasycznego hasła.
/**
 * Komunikat nad formularzem logowania.
 *
 * @param string $message Istniejący HTML.
 * @return string
 */
function wppoland_login_message( $message ) {
	$notice = '<p class="message">' . esc_html__(
		'Konto służbowe tylko dla zespołu redakcyjnego. Podejrzenie wycieku hasła zgłoś do IT.',
		'wppoland'
	) . '</p>';

	return $notice . $message;
}
add_filter( 'login_message', 'wppoland_login_message' );

Uwagi wdrożeniowe:

  • Escapuj tekst (esc_html__) albo buduj markup przez wp_kses_post, jeśli musisz wstawić link.
  • Nie zastępuj całego $message ślepo - WordPress dokłada tu własne komunikaty (np. po loggedout=true). Doklejaj albo filtruj warunkowo po $_REQUEST.
  • Klasa .message jest już ostylowana przez login.css; Twój style-login.css powinien ją respektować (kolor tekstu, border-left), inaczej banner znika w tle.

Dla wariantów zależnych od akcji (login, lostpassword, rp) sprawdzaj isset( $_GET['action'] ) albo globalne kontekstowe flagi, zamiast pokazywać ten sam tekst na odzyskiwaniu hasła i na ekranie 2FA.

#Bezpieczeństwo: komunikaty błędów i mit „ukryj wp-login CSS-em”

Domyślny komunikat przy złym haśle potrafi zdradzić, że użytkownik o danej nazwie istnieje. To ułatwia enumerację kont. Filtr login_errors pozwala zwrócić jedną, generyczną treść:

function wppoland_login_errors() {
	return __( 'Logowanie nie powiodło się. Sprawdź dane i spróbuj ponownie.', 'wppoland' );
}
add_filter( 'login_errors', 'wppoland_login_errors' );

To utrudnia reconnaissance. To nie jest firewall, rate limiting ani 2FA. Nadal potrzebujesz:

  • limitów prób (serwer, WAF, albo sensowna wtyczka / hardening na poziomie hostingu),
  • silnych haseł i najlepiej drugiego składnika (aplikacja TOTP, klucz sprzętowy),
  • aktualnego WordPressa, motywów i wtyczek,
  • ograniczenia dostępu do wp-admin / XML-RPC tam, gdzie polityka firmy na to pozwala (VPN, IP allowlist, HTTP auth na stagingu).

Najczęstszy antypatern w briefach klientów: „ukryjemy logowanie CSS-em / wyślemy /wp-login.php na 404 w .htaccess i będziemy mieli security”. Dlaczego to nie działa:

  1. Security through obscurity - boty i skanery znają ścieżki domyślne; zmiana URL bez twardej kontroli dostępu tylko psuje bookmarki redaktorów.
  2. CSS nic nie chowa po stronie serwera - display: none na linku „Zaloguj” w menu nie usuwa endpointu. wp-login.php i wp-admin nadal odpowiadają.
  3. Własny slug logowania (wtyczki typu rename login) bywa użyteczny jako warstwa tarcia, ale wymaga utrzymania, testów resetu hasła, WooCommerce My Account, REST i cronów. Samo CSS albo robots.txt Disallow nie zastąpi polityki haseł.
  4. Komunikaty REST / XML-RPC nadal mogą zdradzać istnienie użytkowników niezależnie od brandingu ekranu.

Branding logowania i bezpieczeństwo to dwa tory. White-label poprawia UX i zaufanie. Ochrona konta to capability, rate limit, 2FA i aktualizacje. Nie łącz ich w jeden ticket „zrób ładne i bezpieczne samym CSS”.

Jeśli w projekcie jest wtyczka Two Factor albo passkeys, po wdrożeniu CSS sprawdź ekrany dodatkowych metod (backup codes, security key). Selektor .login form często dostaje więcej pól; zbyt agresywne height / overflow: hidden na #login ucina je w połowie.

#mu-plugin kontra motyw potomny

Gdzie trzymać kod? Dwie sensowne lokalizacje, jedna zła.

Motyw potomny (functions.php + assets/css/style-login.css)

  • Zalety: logo i kolory jadą razem z designem frontu; designer zmienia pliki w jednym repozytorium motywu; wersjonowanie przez Version w style.css.
  • Wady: przełączenie motywu (awaria, test A/B, migracja na blokowy) wyłącza branding logowania. Na multisite z różnymi motywami per site kod trzeba powielać albo przenosić wyżej.

Must-use plugin (wp-content/mu-plugins/wppoland-login-branding.php)

  • Zalety: działa niezależnie od aktywnego motywu; idealne na sieć agencyjną i na instalacje, gdzie motyw bywa wymieniany; trudniej „przypadkiem” wyłączyć z kokpitu.
  • Wady: assety (logo, CSS) musisz ładować z katalogu mu-pluginu (plugin_dir_url( __FILE__ )) albo z uploads; deploy mu-pluginów bywa pomijany w workflow „tylko motyw”.

Zła lokalizacja: edycja plików w wp-admin/ albo patch wp-login.php w rdzeniu. Znika przy aktualizacji. Unikaj też „snippetów” wstawianych przez wtyczkę do bazy bez kontroli wersji - przy migracji staging → prod giną albo dublują się.

Wzorzec mu-pluginu z enqueue:

<?php
/**
 * Plugin Name: WPPoland Login Branding
 * Description: White-label wp-login.php niezależnie od motywu.
 * Author: wppoland.com
 */

defined( 'ABSPATH' ) || exit;

add_action( 'login_enqueue_scripts', function () {
	$base = plugin_dir_url( __FILE__ );
	wp_enqueue_style(
		'wppoland-login',
		$base . 'assets/style-login.css',
		array(),
		'2026.09.21'
	);
} );

add_filter( 'login_headerurl', function () {
	return home_url( '/' );
} );

add_filter( 'login_headertext', function () {
	return get_bloginfo( 'name' );
} );

Reguła decyzyjna w briefie: jeśli branding logowania jest częścią produktu klienta (kolory marki, logo kampanii) - motyw potomny. Jeśli to standard agencyjny na wielu instalacjach (to samo logo agencji na stagingach, generyczne błędy, wyłączony shake) - mu-plugin albo wspólny pakiet Composer / prywatne repo wdrażane na wszystkie środowiska.

#Branding bez łamania aktualizacji rdzenia

Cel: wygląd własny, zachowanie jak w Core. Checklista, która trzyma ten kontrakt:

  1. Tylko hooki publiczne - login_enqueue_scripts, login_headerurl, login_headertext, login_message, login_errors, ewentualnie login_footer. Nie nadpisuj template’ów z wp-login.php przez require skopiowanego pliku.
  2. Nie usuwaj zależnych stylów Core na ślepo - wp_dequeue_style( 'login' ) bez własnego pełnego arkusza psuje dostępność i layout przycisków po aktualizacji.
  3. Shake JS możesz wyłączyć świadomie, jeśli klient uważa animację za nieprofesjonalną:
function wppoland_remove_login_shake() {
	remove_action( 'login_head', 'wp_shake_js', 12 );
}
add_action( 'login_head', 'wppoland_remove_login_shake' );

Po major update sprawdź, czy priorytet 12 i nazwa callbacka nadal obowiązują (changelog / blame w Core).

  1. Test regresji po każdej aktualizacji WordPressa: poprawne hasło, złe hasło, puste pola, lost password, reset link z maila, ewentualnie 2FA. Porównaj screenshot stagingu z produkcją.
  2. Języki - jeśli strona jest PL, a kokpit EN (albo odwrotnie), komunikaty z login_errors i login_message trzymaj w textdomainie i ładuj tłumaczenia; hardkodowany polski string na angielskim kokpicie wygląda jak niedokończone wdrożenie.
  3. Multisite - home_url() jest per-site; logo z mu-pluginu może wymagać switch_to_blog albo assetów per blog. Nie zakładaj jednego pliku SVG na całą sieć bez uzgodnienia.
  4. Cache i CDN - HTML logowania zwykle omija page cache, ale CSS z motywu bywa cache’owany agresywnie. Po deployu bump wersji enqueue.

Gdy klient prosi o „całkowicie własny ekran logowania jak w SaaS”, rozważ osobną stronę frontową z formularzem opartym o wp_signon() / custom auth tylko jeśli masz budżet na utrzymanie resetu hasła, nonce, lockout i zgodność z wtyczkami membership. W 80% projektów agencyjnych wystarczy ostylowany wp-login.php. Nadmiarowa aplikacja logowania to drugi produkt do łatania przy każdej lukę w auth.

#Checklista wdrożenia (15 minut na staging)

Przejdź po kolei na kopii produkcyjnej:

  1. Wgraj CSS i logo; odśwież wp-login.php z pominięciem cache (okno incognito).
  2. Kliknij logo - lądujesz na froncie klienta, nie na wordpress.org.
  3. Zaloguj się poprawnym kontem; wyloguj; zaloguj złym hasłem - komunikat jest generyczny.
  4. Sprawdź „Nie pamiętasz hasła?” i mail resetu na środowisku ze skonfigurowanym SMTP.
  5. Jeśli jest 2FA - przejdź pełną ścieżkę raz na desktopie i raz na telefonie.
  6. Przełącz tymczasowo motyw na Twenty Twenty-Something: jeśli branding znika, a miał być globalny, przenieś kod do mu-pluginu.
  7. Zapisz w README projektu ścieżki plików i listę filtrów - kolejny deweloper nie szuka „magii” w pięciu wtyczkach snippet.

Po akceptacji klienta zrób ten sam smoke test na produkcji zaraz po deployu, nie „jutro”.

#Podsumowanie

White-label wp-login.php w 2026 to zestaw publicznych hooków, nie patch rdzenia i nie wtyczka z tysiącem opcji w kokpicie. Enqueue CSS przez login_enqueue_scripts, podmień logo i login_headerurl, doklej sensowny login_message, utwardź login_errors. Trzymaj kod w motywie potomnym albo mu-pluginie - nigdy w plikach Core. Nie udawaj zabezpieczenia ukryciem URL w CSS.

Gdy porządkujesz ryzyka wokół kont i kokpitu szerzej niż branding, zobacz audyt bezpieczeństwa WordPress.

Następny krok

Przekuj artykuł w realne wdrożenie

Pod tym wpisem dokładam linki, które domykają intencję użytkownika i prowadzą dalej w strukturze serwisu.

Powiązany klaster

Sprawdź inne usługi WordPress i bazę wiedzy

Wzmocnij swój biznes dzięki profesjonalnemu wsparciu technicznemu w kluczowych obszarach ekosystemu WordPress.

Polecane artykuły