WordPress Login Sayfasını Özelleştirme: URL ve Yönlendirme
WordPress kurulumunun en bilinen zafiyetlerinden biri, herkesin /wp-login.php adresine giderek giriş sayfasına ulaşabilmesidir. Brute force saldırıları, bot trafiği ve yetkisiz erişim denemeleri genellikle bu kapıdan gelir. Ama mesele sadece güvenlik değil; bazen müşterine özel bir giriş deneyimi sunmak, başarılı girişten sonra kullanıcıyı doğru sayfaya yönlendirmek ya da çıkış sonrası markalı bir sayfa göstermek istiyorsun. Tüm bunları functions.php dosyasına birkaç satır kod ekleyerek halledebilirsin. Bu yazıda WordPress login mekanizmasını baştan sona nasıl özelleştireceğini, gerçek dünya senaryolarıyla birlikte anlatacağım.
Neden Login URL’ini Değiştirmelisin?
Önce şunu netleştirelim: URL değiştirmek tek başına bir güvenlik çözümü değildir. Ama savunmanın derinliği (defense in depth) prensibinin bir parçası olarak oldukça değerlidir. Bir botun her WordPress sitesinde otomatik olarak /wp-login.php denediğini düşün. URL’yi değiştirdiğinde bu botların büyük çoğunluğu anında elenmiş olur.
Bunun yanı sıra pratik nedenler de var:
- Marka tutarlılığı: Müşteri paneli sunarken
/my-accountveya/girisgibi Türkçe ve anlamlı bir URL çok daha profesyonel görünür - Kullanıcı deneyimi: E-ticaret sitelerinde kullanıcıları doğru dashboard’a yönlendirmek kritik
- Log temizliği: Sunucu loglarında
/wp-login.phpiçin binlerce başarısız istek görmek sinir bozucu ve analizi zorlaştırır
Temel Yönlendirme Fonksiyonları
Başarılı Girişten Sonra Yönlendirme
En çok ihtiyaç duyulan şeyden başlayalım. Kullanıcı giriş yaptıktan sonra nereye gidecek? WordPress varsayılan olarak admin dashboard’a yönlendirir, ama bu çoğu zaman istemediğin bir durumdur.
// functions.php
function custom_login_redirect( $redirect_to, $request, $user ) {
// Kullanıcı objesi yoksa veya hata varsa devam et
if ( ! isset( $user->roles ) || is_wp_error( $user ) ) {
return $redirect_to;
}
// Yöneticileri admin paneline yönlendir
if ( in_array( 'administrator', $user->roles ) ) {
return admin_url();
}
// Editor rolündekiler için farklı sayfa
if ( in_array( 'editor', $user->roles ) ) {
return admin_url( 'edit.php' );
}
// WooCommerce müşterileri ve diğer tüm kullanıcılar
if ( in_array( 'customer', $user->roles ) || in_array( 'subscriber', $user->roles ) ) {
return home_url( '/hesabim/' );
}
// Varsayılan: ana sayfaya yönlendir
return home_url();
}
add_filter( 'login_redirect', 'custom_login_redirect', 10, 3 );
Bu kodun güzel yanı rol bazlı yönlendirme yapmasıdır. Adminler admin panelini görür, müşteriler hesap sayfasına gider, editörler direkt yazılar listesine düşer.
Çıkış Sonrası Yönlendirme
Kullanıcı çıkış yaptıktan sonra ne olur? Varsayılan davranış tekrar login sayfasına atmaktır. Ama sen belki ana sayfaya ya da özel bir “Güle güle!” sayfasına yönlendirmek istiyorsun.
// functions.php
function custom_logout_redirect() {
wp_redirect( home_url( '/tekrar-bekleriz/' ) );
exit;
}
add_action( 'wp_logout', 'custom_logout_redirect' );
Dikkat et: Bu hook’u kullanırken exit çağırmayı unutma, yoksa yönlendirme çalışmaz. Ayrıca eğer çoklu site yapısı kullanıyorsan her alt site için ayrı yönlendirme sayfası tanımlaman gerekebilir.
Login URL’ini Değiştirme
Plugin kullanmadan login URL’ini değiştirmek biraz daha karmaşık, ama yapılabilir. Temel mantık şu: /wp-login.php isteğini engelle, kendi belirlediğin slug’ı dinle.
// functions.php
// Yeni login URL'ini belirle
function custom_login_url( $login_url, $redirect, $force_reauth ) {
$login_slug = 'guvenli-giris'; // İstediğin slug
$login_url = home_url( $login_slug );
if ( ! empty( $redirect ) ) {
$login_url = add_query_arg( 'redirect_to', urlencode( $redirect ), $login_url );
}
if ( $force_reauth ) {
$login_url = add_query_arg( 'reauth', '1', $login_url );
}
return $login_url;
}
add_filter( 'login_url', 'custom_login_url', 10, 3 );
// Gerçek wp-login.php adresine direkt erişimi engelle
function block_default_login_page() {
$request_uri = $_SERVER['REQUEST_URI'];
// wp-login.php'ye direkt erişim varsa
if ( strpos( $request_uri, 'wp-login.php' ) !== false && ! is_admin() ) {
// 404 döndür veya ana sayfaya yönlendir
wp_redirect( home_url( '/404/' ) );
exit;
}
}
add_action( 'init', 'block_default_login_page' );
Burada önemli bir uyarı: /wp-login.php‘yi tamamen engellersen, WordPress’in kendi iç mekanizmaları da etkilenebilir. Örneğin parola sıfırlama linkleri, bazı eklenti auth akışları bu dosyaya bağımlıdır. Bu yüzden bu yaklaşımı üretim ortamında uygulamadan önce kapsamlı test et.
Nginx ile Destekleme
Eğer Nginx kullanıyorsan, PHP seviyesinde engellemenin yanı sıra sunucu tarafında da koruma ekleyebilirsin:
# /etc/nginx/sites-available/siteniz.conf
# wp-login.php için IP kısıtlaması
location = /wp-login.php {
allow 85.12.34.56; # Ofis IP'si
allow 192.168.1.0/24; # İç ağ
deny all;
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
Bu yaklaşım PHP’ye hiç ulaşmadan Nginx seviyesinde engelleme yapar, dolayısıyla çok daha verimlidir.
Özel Login Sayfası Oluşturma
Gerçek anlamda özelleştirme istiyorsan, tamamen ayrı bir WordPress sayfası oluşturup bunu login sayfası olarak kullanabilirsin. Bu yaklaşım WooCommerce projelerinde çok yaygındır.
// functions.php
// Özel login sayfası şablonu için shortcode
function custom_login_form_shortcode( $atts ) {
// Zaten giriş yapmışsa yönlendir
if ( is_user_logged_in() ) {
wp_redirect( home_url( '/hesabim/' ) );
exit;
}
$atts = shortcode_atts( array(
'redirect' => home_url( '/hesabim/' ),
'form_id' => 'custom-login-form',
), $atts );
// Login hata mesajları
$errors = '';
if ( isset( $_GET['login'] ) && $_GET['login'] === 'failed' ) {
$errors = '<div class="login-error">Kullanıcı adı veya şifre hatalı. Lütfen tekrar deneyin.</div>';
}
if ( isset( $_GET['login'] ) && $_GET['login'] === 'empty' ) {
$errors = '<div class="login-error">Lütfen tüm alanları doldurun.</div>';
}
ob_start();
?>
<?php echo $errors; ?>
<form id="<?php echo esc_attr( $atts['form_id'] ); ?>"
class="custom-wp-login-form"
action="<?php echo esc_url( site_url( 'wp-login.php', 'login_post' ) ); ?>"
method="post">
<div class="form-group">
<label for="user_login">E-posta veya Kullanıcı Adı</label>
<input type="text"
name="log"
id="user_login"
class="form-control"
autocomplete="username"
required />
</div>
<div class="form-group">
<label for="user_pass">Şifre</label>
<input type="password"
name="pwd"
id="user_pass"
class="form-control"
autocomplete="current-password"
required />
</div>
<div class="form-group remember-me">
<input type="checkbox" name="rememberme" id="rememberme" value="forever" />
<label for="rememberme">Beni hatırla</label>
</div>
<input type="hidden" name="redirect_to" value="<?php echo esc_url( $atts['redirect'] ); ?>" />
<?php wp_nonce_field( 'custom-login-nonce', 'custom_login_nonce' ); ?>
<button type="submit" class="btn btn-primary btn-login">Giriş Yap</button>
<div class="form-links">
<a href="<?php echo wp_lostpassword_url(); ?>">Şifremi Unuttum</a>
<?php if ( get_option( 'users_can_register' ) ) : ?>
<a href="<?php echo wp_registration_url(); ?>">Kayıt Ol</a>
<?php endif; ?>
</div>
</form>
<?php
return ob_get_clean();
}
add_shortcode( 'ozel_giris_formu', 'custom_login_form_shortcode' );
Bu shortcode’u bir WordPress sayfasına [ozel_giris_formu] şeklinde ekleyebilirsin. Sayfa URL’ini /giris/ olarak ayarla, hazır.
Hata Mesajlarını Özelleştirme
Varsayılan WordPress hata mesajları bazen fazla bilgi verir. “Kullanıcı adı hatalı” ya da “Şifre hatalı” demek, saldırgana hangi bilginin doğru olduğunu söylemek demektir. Bunu genel bir mesajla değiştir:
// functions.php
function custom_login_error_message( $error ) {
// Tüm hata mesajlarını tek bir genel mesajla değiştir
$generic_message = '<strong>Hata:</strong> Kullanıcı adı veya şifre hatalı.';
// Spesifik hata tiplerini yakala ve genel mesaj döndür
if ( strpos( $error, 'incorrect_password' ) !== false ||
strpos( $error, 'invalid_username' ) !== false ||
strpos( $error, 'invalid_email' ) !== false ) {
return $generic_message;
}
return $error;
}
add_filter( 'login_errors', 'custom_login_error_message' );
// Login sayfasındaki "Şifre mi unuttun?" hint'ini de gizle
function remove_login_hints( $error ) {
// "Hata: Şifre hintini" kaldır
if ( strpos( $error, 'lost your password' ) !== false ) {
return '<strong>Hata:</strong> Giriş bilgileriniz hatalı.';
}
return $error;
}
add_filter( 'login_errors', 'remove_login_hints' );
WooCommerce Entegrasyonu
WooCommerce kullanan bir projede login yönetimi biraz daha katmanlı hale gelir. WooCommerce kendi login/register sayfasını my-account üzerinden yönetir, ama bunu da özelleştirebilirsin.
// functions.php
// WooCommerce giriş sonrası yönlendirme
function woo_custom_login_redirect( $redirect, $user ) {
// Müşteri siparişi varsa sipariş listesine gönder
if ( in_array( 'customer', $user->roles ) ) {
$orders = wc_get_orders( array(
'customer' => $user->ID,
'limit' => 1,
'status' => array( 'pending', 'processing', 'on-hold' ),
) );
// Aktif siparişi olan müşteriyi sipariş sayfasına gönder
if ( ! empty( $orders ) ) {
return wc_get_account_endpoint_url( 'orders' );
}
// Aktif siparişi yoksa shop sayfasına gönder
return wc_get_page_permalink( 'shop' );
}
// Diğer roller için varsayılan davranış
return $redirect;
}
add_filter( 'woocommerce_login_redirect', 'woo_custom_login_redirect', 10, 2 );
// WooCommerce checkout login'i sonrası yönlendirme
function woo_checkout_login_redirect( $redirect, $user ) {
// Eğer sepette ürün varsa checkout'a devam et
if ( ! WC()->cart->is_empty() ) {
return wc_get_checkout_url();
}
return $redirect;
}
add_filter( 'woocommerce_login_redirect', 'woo_checkout_login_redirect', 20, 2 );
Bu örnek gerçek bir e-ticaret senaryosunu ele alıyor: Bekleyen siparişi olan kullanıcı giriş yaptığında direkt sipariş listesine gidiyor. Bu küçük detay müşteri deneyimini ciddi ölçüde iyileştirir.
Login Girişim Sayısını Sınırlama
functions.php ile temel bir brute force koruması da ekleyebilirsin. Bu bir plugin kadar kapsamlı olmaz ama ek bir katman sağlar:
// functions.php
// Başarısız giriş denemelerini say ve geçici olarak engelle
function track_failed_login_attempts( $username ) {
$ip_address = $_SERVER['REMOTE_ADDR'];
$transient_key = 'login_attempts_' . md5( $ip_address );
$attempts = get_transient( $transient_key );
if ( false === $attempts ) {
$attempts = 1;
} else {
$attempts++;
}
// 5 dakika içinde 5'ten fazla denemede 15 dakika engelle
if ( $attempts >= 5 ) {
set_transient( $transient_key, $attempts, 15 * MINUTE_IN_SECONDS );
} else {
set_transient( $transient_key, $attempts, 5 * MINUTE_IN_SECONDS );
}
}
add_action( 'wp_login_failed', 'track_failed_login_attempts' );
// Engellenen IP'lerin giriş yapmasını önle
function check_login_attempts() {
$ip_address = $_SERVER['REMOTE_ADDR'];
$transient_key = 'login_attempts_' . md5( $ip_address );
$attempts = get_transient( $transient_key );
if ( $attempts && $attempts >= 5 ) {
// Login sayfasına erişimi engelle
wp_die(
'Çok fazla başarısız giriş denemesi. Lütfen 15 dakika sonra tekrar deneyin.',
'Erişim Engellendi',
array(
'response' => 429,
'back_link' => false,
)
);
}
}
add_action( 'login_init', 'check_login_attempts' );
// Başarılı girişte sayacı sıfırla
function reset_login_attempts( $user_login, $user ) {
$ip_address = $_SERVER['REMOTE_ADDR'];
$transient_key = 'login_attempts_' . md5( $ip_address );
delete_transient( $transient_key );
}
add_action( 'wp_login', 'reset_login_attempts', 10, 2 );
Bu kodun transient kullandığına dikkat et. Yani veriler veritabanında wp_options tablosunda saklanır. Yüksek trafikli sitelerde bu bir performans sorununa yol açabilir, bu durumda Redis veya Memcached kullanmayı düşünmelisin.
Gerçek Dünya Senaryosu: Üyelik Sitesi
Diyelim ki bir online eğitim platformu kuruyorsun. Üç farklı kullanıcı tipin var: öğrenciler, eğitmenler ve adminler. Her birinin giriş sonrası farklı bir sayfaya gitmesi gerekiyor.
// functions.php
function education_platform_login_redirect( $redirect_to, $request, $user ) {
if ( ! isset( $user->roles ) || is_wp_error( $user ) ) {
return $redirect_to;
}
// Admin paneline
if ( in_array( 'administrator', $user->roles ) ) {
return admin_url( 'index.php' );
}
// Eğitmenler: Kurs yönetim sayfasına
if ( in_array( 'instructor', $user->roles ) ) {
return home_url( '/egitmen-paneli/' );
}
// Öğrenciler: Kayıtlı oldukları kursların listesine
if ( in_array( 'student', $user->roles ) || in_array( 'subscriber', $user->roles ) ) {
// Meta kontrolü: aktif kurs kaydı var mı?
$active_course = get_user_meta( $user->ID, 'active_course_id', true );
if ( $active_course ) {
// Son bıraktığı yerden devam et
$last_lesson = get_user_meta( $user->ID, 'last_lesson_' . $active_course, true );
if ( $last_lesson ) {
return get_permalink( $last_lesson );
}
return home_url( '/kurs/' . $active_course . '/' );
}
// Aktif kurs yoksa katalog sayfasına
return home_url( '/kurslar/' );
}
return home_url();
}
add_filter( 'login_redirect', 'education_platform_login_redirect', 10, 3 );
// Kullanıcı aktif kurs bilgisini güncelle
function update_last_visited_lesson( $post_id ) {
if ( ! is_user_logged_in() ) {
return;
}
// Sadece ders tipi postlar için
if ( get_post_type( $post_id ) !== 'lesson' ) {
return;
}
$user_id = get_current_user_id();
$course_id = get_post_meta( $post_id, 'course_id', true );
if ( $course_id ) {
update_user_meta( $user_id, 'active_course_id', $course_id );
update_user_meta( $user_id, 'last_lesson_' . $course_id, $post_id );
}
}
add_action( 'wp', function() {
if ( is_singular( 'lesson' ) ) {
update_last_visited_lesson( get_the_ID() );
}
} );
Bu senaryo kullanıcı deneyimini dramatik biçimde iyileştirir. Öğrenci kaldığı yerden devam eder, eğitmen direkt kurs yönetimine gider.
Login Sayfası Logo ve Stil Özelleştirmesi
WordPress giriş sayfasını görsel olarak da markalamak isteyebilirsin. Bu tamamen functions.php ile yapılabilir:
// functions.php
// Login sayfasında özel logo
function custom_login_logo() {
$logo_url = get_theme_mod( 'custom_logo' );
$logo_src = $logo_url ? wp_get_attachment_image_url( $logo_url, 'full' ) : get_template_directory_uri() . '/images/logo.png';
?>
<style type="text/css">
#login h1 a, .login h1 a {
background-image: url(<?php echo esc_url( $logo_src ); ?>);
height: 80px;
width: 300px;
background-size: contain;
background-repeat: no-repeat;
padding-bottom: 20px;
}
body.login {
background-color: #f0f4f8;
}
.login form {
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
}
.wp-core-ui .button-primary {
background: #2563eb;
border-color: #1d4ed8;
border-radius: 6px;
width: 100%;
padding: 10px;
}
</style>
<?php
}
add_action( 'login_enqueue_scripts', 'custom_login_logo' );
// Logo tıklandığında ana sayfaya yönlendir
function custom_login_logo_url() {
return home_url();
}
add_filter( 'login_headerurl', 'custom_login_logo_url' );
// Logo title'ını site adıyla değiştir
function custom_login_logo_url_title() {
return get_bloginfo( 'name' );
}
add_filter( 'login_headertext', 'custom_login_logo_url_title' );
Sık Karşılaşılan Sorunlar ve Çözümleri
Sonsuz yönlendirme döngüsü: login_redirect filter’ında kendi login sayfasına yönlendirirsen sonsuz döngüye girersin. Her zaman is_user_logged_in() kontrolü yap ve login sayfası URL’ini hedef olarak kullanma.
WooCommerce çakışmaları: WooCommerce kendi login hookları ve redirect mekanizmaları var. woocommerce_login_redirect ve login_redirect aynı anda aktifse öncelik sırası önemlidir. WooCommerce hookları genellikle sonradan çalışır ve üst sırayı alır.
Cache sorunları: Yönlendirme kodun doğru çalışıyor ama tarayıcıda görmüyorsan önce cache’i temizle. Nginx FastCGI cache veya Redis Object Cache bu durumda sürpriz yaratabilir. Login sayfalarını her zaman cache dışında tut:
# Nginx config - login sayfalarını cache etme
location ~ ^/(wp-login.php|wp-admin/) {
fastcgi_no_cache 1;
fastcgi_cache_bypass 1;
# ... diğer ayarlar
}
wp_redirect vs wp_safe_redirect: Harici bir URL’ye yönlendiriyorsan wp_redirect kullan. Ama kendi domain’in içinde kalıyorsan wp_safe_redirect daha güvenlidir; izin verilmeyen harici yönlendirmeleri otomatik olarak engeller.
Sonuç
WordPress login sistemini functions.php ile özelleştirmek hem güvenlik hem de kullanıcı deneyimi açısından ciddi fayda sağlar. Anlattığım yaklaşımları özetleyecek olursam:
- Rol bazlı yönlendirme ile her kullanıcı tipi için anlamlı bir giriş deneyimi oluşturabilirsin
- Özel login URL’i botların büyük çoğunluğunu filtreler ama Nginx gibi sunucu tarafı korumayla desteklemelisin
- Hata mesajlarını genelleştirerek kullanıcı bilgi sızmasını önlersin
- WooCommerce entegrasyonunda checkout flow’unu akıllıca yönetmek sepet terk oranını düşürür
- Basit transient tabanlı brute force koruması ekstra bir güvenlik katmanı sunar
Bu kodların hepsini aynı anda uygulamak yerine, ihtiyacına göre parça parça entegre et ve her adımda test et. Özellikle login mekanizmasını kırdığında siteye erişemez hale gelebilirsin; bu yüzden her değişikliği önce staging ortamında dene, production’a atmadan önce emergency admin URL’inin çalıştığından emin ol. İyi kodlamalar.
