Jak ukryć wersję WordPressa i zabezpieczyć nagłówki HTTP (poradnik 2026)

Jak ukryć wersję WordPressa i zabezpieczyć nagłówki HTTP (poradnik 2026)

Ostatnio zweryfikowano: 15 października 2026
16 min czytania
Przewodnik
Audytor bezpieczeństwa

W bezpieczeństwie aplikacji webowych pierwszym etapem każdego ataku jest rekonesans. Zautomatyzowane narzędzia i botnety bez przerwy skanują sieć w poszukiwaniu nieaktualnych wersji WordPressa i domyślnych, podatnych konfiguracji. Jeśli strona od razu ogłasza dokładny numer wersji, na której działa, trafia prosto na listę celów dla ataków na znane luki.

Ukrycie numeru wersji WordPressa to klasyczne “bezpieczeństwo przez ukrycie” (security through obscurity). Samo w sobie nie zastępuje regularnej konserwacji ani szybkiego instalowania poprawek bezpieczeństwa. W połączeniu z poprawnie ustawionymi nagłówkami bezpieczeństwa HTTP (security headers), zamknięciem przestarzałych interfejsów, takich jak XML-RPC, i ograniczeniem uprawnień w systemie plików daje jednak skuteczną barierę: mniej szumu w logach i mniejszą podatność na masowe ataki.

W tym poradniku omawiam hardening platformy WordPress od strony cyklu życia żądania HTTP oraz konfiguracji serwerów Apache i Nginx.

#Gdzie WordPress ujawnia swoją wersję

Domyślna instalacja WordPressa zostawia jawne ślady swojej wersji w kilku warstwach odpowiedzi HTTP i kodu HTML:

  1. Meta tag generator w nagłówku HTML:
    <meta name="generator" content="WordPress 6.7.1" />
    Ten znacznik jest wstawiany automatycznie przez akcję wp_head i jest najczęściej zbieranym wskaźnikiem przez skanery podatności.
  2. Kanały RSS, Atom i RDF: Pliki XML generowane pod adresem /feed/ zawierają osobny element, na przykład <generator>https://wordpress.org/?v=6.7.1</generator>.
  3. Parametry w adresach zasobów statycznych (CSS i JS): Przy ładowaniu skryptów rdzenia, takich jak arkusze stylów bloków Gutenberga czy obsługa emoji, adresy URL zawierają jawny parametr, na przykład ?ver=6.7.1.
  4. Pliki statyczne w katalogu głównym instalacji: Pliki readme.html i license.txt często pozostają publicznie dostępne w katalogu głównym serwera i zdradzają informacje o zainstalowanym wydaniu.

Usunięcie tych elementów sprawia, że skanery masowe nie dopasują instalacji do publicznych list CVE (Common Vulnerabilities and Exposures), zanim w ogóle zaczną badać zachowanie systemu.

#Jak usunąć wersję WordPressa w PHP

Wielu administratorów wkleja znalezione w sieci fragmenty kodu, które usuwają parametr ver ze wszystkich arkuszy stylów i plików JavaScript. To powoduje poważny problem operacyjny: wyłącza mechanizm cache busting w przeglądarce i w sieciach CDN. Po aktualizacji motywu lub wtyczki użytkownicy nadal dostają stare pliki z pamięci podręcznej, bo adres zasobu się nie zmienił.

Poprawne podejście polega na usuwaniu tylko tych wartości, które odpowiadają wersji rdzenia WordPressa. Własne wersje i hashe plików od wtyczek i motywów zostają.

#Usuwanie meta tagu generator z head i z kanałów RSS

Najczystszym i najłatwiejszym w utrzymaniu miejscem na te reguły jest mu-plugin (Must-Use Plugin, wp-content/mu-plugins/security-hardening.php). Działa niezależnie od aktywnego motywu:

<?php
/**
 * Plugin Name: Hardening rekonesansu WordPress
 * Description: Usuwa publiczną informację o wersji rdzenia z nagłówka HTML i kanałów RSS.
 */

// 1. Usuń meta tag generator z sekcji head
remove_action('wp_head', 'wp_generator');

// 2. Usuń znaczniki wersji z kanałów RSS i Atom
remove_action('rss_head', 'the_generator');
remove_action('rss2_head', 'the_generator');
remove_action('commentsrss2_head', 'the_generator');
remove_action('atom_head', 'the_generator');
remove_action('rdf_header', 'the_generator');
remove_action('opml_head', 'the_generator');

// 3. Wyczyść wynik filtra the_generator
add_filter('the_generator', '__return_empty_string');

#Selektywne usuwanie parametru ?ver= ze skryptów i stylów

Żeby oczyścić adresy plików statycznych bez psucia polityki cache wtyczek, stosuję filtr, który sprawdza, czy argument ver jest identyczny ze zmienną globalną $wp_version:

function wppoland_usun_wersje_rdzenia_z_zasobow(string $src): string {
    global $wp_version;

    if (empty($src)) {
        return $src;
    }

    $czesci_url = parse_url($src);
    if (!isset($czesci_url['query'])) {
        return $src;
    }

    parse_str($czesci_url['query'], $parametry);

    // Usuń tylko wtedy, gdy wartość parametru to dokładnie wersja rdzenia
    if (isset($parametry['ver']) && $parametry['ver'] === $wp_version) {
        return remove_query_arg('ver', $src);
    }

    return $src;
}
add_filter('style_loader_src', 'wppoland_usun_wersje_rdzenia_z_zasobow', 9999);
add_filter('script_loader_src', 'wppoland_usun_wersje_rdzenia_z_zasobow', 9999);

Z tym filtrem pliki rdzenia WordPressa tracą publiczną informację o wersji, a skrypty z własnym, niezależnym numerem wersji nadal korzystają z szybkiego unieważniania cache w przeglądarce użytkownika.

#Które nagłówki HTTP zdradzają WordPressa poza meta tagiem generator

Meta tag generator to nie jedyny odcisk palca. Rdzeń WordPressa i samo PHP dodają nagłówki odpowiedzi, z których skaner odczyta, na jakim oprogramowaniu działa strona, nawet gdy w HTML nie ma już numeru wersji.

NagłówekKto go wysyłaKiedy
Link: <https://przyklad.pl/wp-json/>; rel="https://api.w.org/"rest_output_link_header(), od WordPressa 4.4.0przy każdym żądaniu do front endu, na akcji template_redirect (priorytet 11)
Link: <https://przyklad.pl/?p=123>; rel=shortlinkwp_shortlink_header()na pojedynczych wpisach i stronach, na akcji template_redirect (priorytet 11)
X-Pingback: https://przyklad.pl/xmlrpc.phpWP::send_headers()na pojedynczych wpisach, które przyjmują pingi (pings_open()), warunkowo od wersji 4.4.0
X-Powered-By: PHP/8.3.xsamo PHPdopóki dyrektywa expose_php jest włączona, a jest tak domyślnie

Źródła: dokumentacja funkcji rest_output_link_header, dokumentacja metody WP::send_headers i dokumentacja dyrektyw rdzenia PHP.

Nagłówki WordPressa usuwa się tymi samymi hookami, które je dodają. X-Pingback przechodzi przez filtr wp_headers, który filtruje nagłówki HTTP przed wysłaniem ich do przeglądarki. Dopisz do tego samego mu-pluginu:

// Wykrywanie REST API: nagłówek Link i znacznik <link>
remove_action('template_redirect', 'rest_output_link_header', 11);
remove_action('wp_head', 'rest_output_link_wp_head', 10);

// Shortlink: nagłówek i znacznik <link>
remove_action('template_redirect', 'wp_shortlink_header', 11);
remove_action('wp_head', 'wp_shortlink_wp_head', 10);

// X-Pingback na pojedynczych wpisach
add_filter('wp_headers', function (array $headers): array {
    unset($headers['X-Pingback']);
    return $headers;
});

Usunięcie linku wykrywania nie wyłącza REST API: adres /wp-json/ nadal odpowiada, a edytor blokowy z niego korzysta. Chodzi o to, żeby nie ogłaszać CMS-a w każdej odpowiedzi HTML, a nie o zepsucie API.

Nagłówka X-Powered-By nie zmienisz z poziomu WordPressa. Podręcznik PHP podaje, że expose_php można ustawić wyłącznie w php.ini, więc ini_set() nic tu nie da. Ustaw wartość w puli PHP-FPM albo w głównym php.ini:

expose_php = Off

OWASP HTTP Headers Cheat Sheet zaleca usunięcie wszystkich nagłówków X-Powered-By oraz usunięcie nagłówka Server albo nadanie mu wartości, która nic nie zdradza. Ten sam dokument przypomina, że atakujący i tak rozpozna stos technologiczny innymi metodami. To uczciwa granica całej tej sekcji.

#Nagłówki bezpieczeństwa HTTP w WordPressie

Nagłówki bezpieczeństwa HTTP to dyrektywy wysyłane przez serwer, które mówią nowoczesnym przeglądarkom, jak izolować zasoby, czy wolno osadzać stronę w ramkach i jakie skrypty wolno wykonać.

Konfiguracja bezpośrednio na serwerze WWW (Apache lub Nginx) jest zdecydowanie lepsza niż ustawianie nagłówków w PHP, bo reverse proxy i cache całych stron (full page cache) serwują zapisane odpowiedzi bez uruchamiania PHP.

#1. X-Content-Type-Options: nosniff

Nie pozwala przeglądarce zgadywać typu MIME pliku, gdy różni się od nagłówka Content-Type wysłanego przez serwer. Blokuje to ataki, w których ktoś wgrywa obrazek z ukrytym kodem JavaScript i nakłania przeglądarkę do jego wykonania.

Header always set X-Content-Type-Options "nosniff"

#2. X-Frame-Options: SAMEORIGIN

Chroni aplikację przed clickjackingiem. Bez tej dyrektywy obca strona może osadzić panel administracyjny albo formularz zamówienia w niewidocznym elemencie <iframe> i nakłonić zalogowanego użytkownika do wykonania szkodliwych akcji.

Header always set X-Frame-Options "SAMEORIGIN"

#3. HTTP Strict Transport Security (HSTS)

HSTS zmusza przeglądarkę do komunikacji wyłącznie przez HTTPS. Eliminuje próby obniżenia połączenia do niezabezpieczonego HTTP w sieciach publicznych oraz ataki typu man-in-the-middle (MitM).

Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

Parametr max-age=31536000 ustawia ważność polityki na jeden rok (wartość w sekundach).

#4. Referrer-Policy: strict-origin-when-cross-origin

Określa, ile danych o stronie źródłowej (nagłówek Referer) trafia do zewnętrznych serwisów, gdy odwiedzający klika w link. Ta polityka wysyła pełny adres przy żądaniach w obrębie tej samej domeny, a przy żądaniach do innych domen przez HTTPS ogranicza się do samej domeny źródłowej.

Header always set Referrer-Policy "strict-origin-when-cross-origin"

#5. Permissions-Policy

Pozwala wyłączyć funkcje urządzenia (kamera, mikrofon, geolokalizacja), z których strona firmowa czy sklep nie korzysta. Jeśli biblioteka zewnętrzna zostanie przejęta, przeglądarka zablokuje każdą próbę uruchomienia tych sensorów.

Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()"

#6. Content-Security-Policy (CSP)

Content Security Policy to najsilniejsza ochrona przed atakami Cross-Site Scripting (XSS). Jawna lista domen, z których wolno ładować skrypty, style, fonty i połączenia asynchroniczne, uniemożliwia wykonanie dowolnego kodu wstrzykniętego do bazy danych.

Praktyczny, wyważony przykład dla strony WordPress z Google Analytics i zaufanymi bibliotekami zewnętrznymi:

Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.google-analytics.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com data:; img-src 'self' data: https:; connect-src 'self' https://www.google-analytics.com; frame-ancestors 'self';"

#7. Czy X-XSS-Protection ma sens w 2026 roku

Wiele starszych poradników zalecało X-XSS-Protection: 1; mode=block. Ta rada jest nieaktualna. Strona MDN o X-XSS-Protection oznacza nagłówek jako przestarzały i niestandardowy, zaleca w jego miejsce Content-Security-Policy i ostrzega, że w niektórych przypadkach filtr może tworzyć podatności XSS na stronach, które bez niego byłyby bezpieczne. MDN dodaje, że mode=block może być podatny na ataki kanałem bocznym, gdy stronę da się osadzić w <iframe>.

OWASP odpowiada tak samo: nie wysyłać nagłówka albo jawnie ustawić go na 0. Jeśli stara konfiguracja serwera lub wtyczka już wysyła 1; mode=block, nadpisz wartość:

Header always set X-XSS-Protection "0"
add_header X-XSS-Protection "0" always;

Ochronę, o którą naprawdę chodzi, daje CSP bez 'unsafe-inline' dla skryptów. Na większości stron WordPress to dłuższy projekt.

#Czy WordPress sam wysyła X-Frame-Options

Tak, na dwóch ekranach. Rdzeń podpina funkcję send_frame_options_header() pod akcję login_init, a w pliku wp-admin/includes/admin-filters.php pod akcję admin_init. Funkcja, dostępna od WordPressa 3.1.3, wysyła dwa nagłówki, o ile żadne nagłówki nie zostały jeszcze wysłane:

X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self';

Źródło: dokumentacja funkcji send_frame_options_header.

wp-login.php i kokpit są więc chronione przed osadzaniem w ramkach nawet bez konfiguracji serwera. Publiczny front end nie jest: motywy, strony zamówienia i formularze kontaktowe nie dostaną tego nagłówka, dopóki sam go nie dodasz.

Dla Apache ma to praktyczną konsekwencję. Dokumentacja mod_headers wyjaśnia, że set zastępuje każdy wcześniejszy nagłówek o tej samej nazwie, a append dokleja nową wartość do istniejącej po przecinku. Z Header append strona logowania niesie wartość od WordPressa i Twoją. Z Header always set niesie dokładnie jedną. Ta sama dokumentacja wyjaśnia, że nagłówki generowane przez PHP za mod_proxy_fcgi trafiają do tabeli always, dlatego wszystkie przykłady w tym poradniku używają Header always set. MDN przypomina też, że dyrektywa ALLOW-FROM jest przestarzała i sprawia, że nowoczesne przeglądarki ignorują cały nagłówek, a X-Frame-Options ustawiony w znaczniku <meta> nie działa wcale.

#Konfiguracja Apache i Nginx ukrywająca wersję WordPressa

Poniżej gotowe przykłady dla najczęstszych środowisk hostingowych.

#Konfiguracja Apache (.htaccess)

Umieść te reguły na początku pliku .htaccess, przed standardowym blokiem # BEGIN WordPress:

# --- WPPoland: hardening nagłówków HTTP ---
<IfModule mod_headers.c>
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
    Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
    # Przestarzały nagłówek: jawnie wyłączony (patrz sekcja o X-XSS-Protection)
    Header always set X-XSS-Protection "0"
</IfModule>

# Blokada plików dokumentacji i plików wrażliwych
<FilesMatch "^(wp-config\.php|readme\.html|license\.txt|\.env)">
    Require all denied
</FilesMatch>

# Wyłączenie bezpośredniego dostępu do XML-RPC
<Files xmlrpc.php>
    Require all denied
</Files>

#Konfiguracja bloku server w Nginx

W Nginx zadeklaruj nagłówki w głównym bloku server obsługującym HTTPS:

server {
    listen 443 ssl http2;
    server_name przyklad.pl www.przyklad.pl;

    ssl_certificate /etc/letsencrypt/live/przyklad.pl/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/przyklad.pl/privkey.pem;

    # Nagłówki bezpieczeństwa
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

    # Ukrycie wersji Nginx
    server_tokens off;

    root /var/www/przyklad.pl;
    index index.php index.html;

    # Blokada informacyjnych plików rdzenia
    location ~* /(?:readme\.html|license\.txt|wp-config\.php) {
        deny all;
    }

    # Pełna blokada żądań XML-RPC
    location = /xmlrpc.php {
        deny all;
        access_log off;
        log_not_found off;
    }

    # Obsługa PHP przez FastCGI
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }
}

#Dlaczego nagłówki bezpieczeństwa znikają w blokach location Nginx

Częsta niespodzianka po “działającej” konfiguracji: curl -I na stronie głównej pokazuje wszystkie nagłówki bezpieczeństwa, a plik CSS albo wp-login.php nie pokazuje żadnego. Dokumentacja modułu ngx_http_headers_module wyjaśnia dlaczego: dyrektywy add_header są dziedziczone z poprzedniego poziomu konfiguracji wtedy i tylko wtedy, gdy na bieżącym poziomie nie ma żadnej dyrektywy add_header.

Wystarczy jedno add_header Cache-Control ... w location ~* \.(css|js)$, żeby ten blok stracił wszystkie nagłówki bezpieczeństwa. Są dwa wyjścia:

  1. Trzymać nagłówki w osobnym pliku i dołączać go w bloku server oraz w każdym location, który definiuje własne add_header:
# /etc/nginx/snippets/security-headers.conf
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
location ~* \.(css|js|woff2)$ {
    include snippets/security-headers.conf;
    add_header Cache-Control "public, max-age=31536000, immutable";
}
  1. W Nginx 1.29.3 i nowszym dyrektywa add_header_inherit merge; dokleja wartości z poprzedniego poziomu do bieżących, zamiast je zastępować.

Liczy się też parametr always. Bez niego Nginx dodaje nagłówek tylko do odpowiedzi o kodach 200, 201, 204, 206, 301, 302, 303, 304, 307 i 308. Odpowiedź 403 na /xmlrpc.php albo strona 404 wychodzą bez ochrony.

#Wymagania listy HSTS preload

Token preload w przykładach powyżej sam niczego nie włącza. To prośba o wpisanie domeny na stałe do kodu przeglądarek, a serwis hstspreload.org wymienia warunki zgłoszenia:

  • poprawny certyfikat,
  • przekierowanie z HTTP na HTTPS na tym samym hoście, jeśli serwer nasłuchuje na porcie 80,
  • wszystkie subdomeny dostępne przez HTTPS, łącznie z www, jeśli istnieje dla niej rekord DNS,
  • nagłówek HSTS na domenie głównej, wysyłany przez HTTPS, z max-age co najmniej 31536000 sekund, includeSubDomains i preload,
  • jeśli strona HTTPS przekierowuje dalej, już to przekierowanie musi nieść nagłówek HSTS.

Ta sama strona ostrzega, że preload obejmuje wszystkie subdomeny, także wewnętrzne i niepubliczne, że nowe wpisy mogą potrzebować kilku miesięcy, zanim trafią do stabilnej wersji Chrome, i że usunięcie z listy zwykle jest powolne i uciążliwe. Środowisko stagingowe na subdomenie bez certyfikatu przestaje się otwierać w każdej przeglądarce, która ma tę listę.

OWASP zaleca wartość max-age=63072000; includeSubDomains; preload, czyli dwa lata. Kolejność, która chroni przed zablokowaniem samego siebie: zacznij od krótkiego max-age bez preload, sprawdź wszystkie subdomeny, podnieś wartość i zgłoś domenę dopiero wtedy, gdy cała na stałe działa wyłącznie po HTTPS. Jeśli nie jesteś gotów na takie zobowiązanie, usuń preload z nagłówka.

#Jak zablokować XML-RPC i enumerację użytkowników

Odporność WordPressa zależy od świadomego zmniejszania wystawionej powierzchni. Dwa klasyczne kanały zbierania danych logowania warto zamknąć w każdej instalacji firmowej.

#Blokada pliku xmlrpc.php

Protokół XML-RPC powstał do zdalnej komunikacji, zanim ustabilizowało się nowoczesne REST API. Dziś jest poważnym ryzykiem z powodu metody system.multicall. Jednym żądaniem HTTP POST ze spreparowanym XML-em atakujący może wysłać wiele prób logowania naraz i obejść typowe wtyczki ograniczające liczbę prób.

Jeśli nie korzystasz z Jetpacka ani z aplikacji mobilnej WordPressa, wyłącz dostęp do xmlrpc.php na poziomie serwera WWW (jak w konfiguracjach powyżej).

Nie traktuj filtra add_filter('xmlrpc_enabled', '__return_false'); jako pełnej blokady. Dokumentacja hooka xmlrpc_enabled wyjaśnia, że wbrew nazwie filtr steruje tylko metodami XML-RPC wymagającymi uwierzytelnienia. Pingbacki i inne punkty końcowe bez logowania zostają otwarte. Dopiero reguła na serwerze WWW zatrzymuje żądanie, zanim uruchomi się PHP.

#Blokada enumeracji użytkowników w REST API

Domyślnie interfejs REST WordPressa udostępnia publiczny endpoint /wp-json/wp/v2/users, który wypisuje autorów wpisów razem z ich slugami (nazwami kont używanymi przy logowaniu). Boty mogą w ten sposób zebrać loginy, zanim zaczną atak brute force na ekran logowania.

Żeby zamknąć ten kanał, ukryj endpointy użytkowników przed osobami bez uprawnień. Sprawdzenie list_users jest ważne: bez niego redaktorzy tracą listę autorów w edytorze blokowym, który czyta ten sam endpoint.

add_filter('rest_endpoints', function(array $endpoints): array {
    if (isset($endpoints['/wp/v2/users']) && !current_user_can('list_users')) {
        unset($endpoints['/wp/v2/users']);
    }
    if (isset($endpoints['/wp/v2/users/(?P<id>[\d]+)']) && !current_user_can('list_users')) {
        unset($endpoints['/wp/v2/users/(?P<id>[\d]+)']);
    }
    return $endpoints;
});

Dodatkowo przechwyć stary sposób odpytywania o autora po ID w adresie (/?author=1) i przekieruj go na stronę główną:

add_action('template_redirect', function(): void {
    if (is_author() && !is_admin()) {
        wp_safe_redirect(home_url(), 301);
        exit;
    }
});

#Jak zabezpieczyć stronę logowania WordPressa

Adresy /wp-admin/ i /wp-login.php są atakowane bez przerwy. Tak je chronisz.

#1. Ogranicz liczbę prób logowania

Domyślnie WordPress pozwala na nieograniczoną liczbę prób logowania.

Wtyczka: Limit Login Attempts Reloaded albo fail2ban na serwerze, który czyta nieudane logowania z access logu. Minimalna wersja jako mu-plugin musi podpiąć się pod filtr authenticate, inaczej licznik niczego nie zablokuje:

add_filter('authenticate', function ($user) {
    $key = 'wppoland_login_fail_' . md5($_SERVER['REMOTE_ADDR'] ?? '');
    if ((int) get_transient($key) >= 5) {
        return new WP_Error('too_many_attempts', 'Too many failed logins. Try again in 15 minutes.');
    }
    return $user;
}, 30);

add_action('wp_login_failed', function () {
    $key = 'wppoland_login_fail_' . md5($_SERVER['REMOTE_ADDR'] ?? '');
    set_transient($key, (int) get_transient($key) + 1, 15 * MINUTE_IN_SECONDS);
});

Za reverse proxy albo CDN REMOTE_ADDR to adres proxy, więc ten kod zablokowałby wszystkich naraz. Najpierw skonfiguruj na serwerze WWW prawdziwy adres IP klienta.

#2. Uwierzytelnianie dwuskładnikowe (2FA)

Nawet jeśli atakujący zdobędzie hasło, bez drugiego składnika się nie zaloguje.

Wtyczki:

#3. Zmień adres logowania

Domyślnie każdy wie, że logowanie jest pod /wp-login.php.

Wtyczka: WPS Hide Login

Wtyczka zmienia adres na coś w rodzaju /moje-tajne-logowanie/. Boty skanujące /wp-login.php dostają 404. To znowu ukrywanie: zmniejsza szum w logach, ale nie zastępuje 2FA. Blokadę enumeracji użytkowników opisuje poprzednia sekcja.

#Jak zabezpieczyć bazę danych WordPressa

#Zmień prefiks tabel

Domyślnie WordPress używa prefiksu tabel wp_. Własny prefiks nie chroni przed SQL injection; psuje tylko automatyczne skrypty, które mają nazwy tabel wp_ wpisane na sztywno.

Podczas instalacji: ustaw własny prefiks, np. xyz_ albo prod_.

Po instalacji: użyj wtyczki takiej jak Change Table Prefix albo zmień ręcznie nazwy tabel oraz klucze z prefiksem w options i usermeta (najpierw zrób kopię zapasową).

Wyłączenie edytora plików (DISALLOW_FILE_EDIT) i uprawnienia wp-config.php opisuje następna sekcja.

#Uprawnienia plików i katalogów WordPressa w Linuksie

Nawet przy mocnych nagłówkach izolacja na poziomie systemu operacyjnego jest ostatnią barierą, gdy luka w jednej z wtyczek pozwoli wgrać złośliwy plik.

Oficjalne wytyczne administracyjne zalecają ścisły podział uprawnień do odczytu, zapisu i wykonywania:

# Wszystkie katalogi na 755 (rwxr-xr-x)
find /var/www/przyklad.pl/ -type d -exec chmod 755 {} \;

# Wszystkie zwykłe pliki na 644 (rw-r--r--)
find /var/www/przyklad.pl/ -type f -exec chmod 644 {} \;

# Plik konfiguracyjny z danymi dostępowymi do bazy tylko do odczytu dla właściciela
chmod 400 /var/www/przyklad.pl/wp-config.php

Przy takiej konfiguracji proces serwera WWW (www-data albo użytkownik puli PHP-FPM) nie ma prawa zapisu do głównych plików PHP, z wyjątkiem katalogu wp-content/uploads/ przeznaczonego na pliki multimedialne.

#Wyłączenie edycji plików w kokpicie WordPressa

Żeby atakujący z danymi konta administratora nie mógł zmieniać motywów ani wtyczek we wbudowanym edytorze, dodaj do wp-config.php stałą:

define('DISALLOW_FILE_EDIT', true);

Ta instrukcja całkowicie usuwa podmenu edycji plików z kokpitu i zamyka ważną drogę do utrwalenia złośliwego kodu.

#Jak sprawdzić hardening poleceniem curl

Po wdrożeniu zmian w kodzie i na serwerze sprawdź rzeczywiste odpowiedzi z terminala:

  1. Nagłówki HTTP przez cURL:
    curl -sI https://przyklad.pl | grep -iE "(strict-transport|x-frame|x-content|referrer|permissions|content-security)"
  2. Brak tagu generator i odcisków palca:
    curl -s https://przyklad.pl | grep -i "generator"
    curl -sI https://przyklad.pl | grep -iE "x-powered-by|x-pingback|api.w.org"
  3. Strona logowania z jednym nagłówkiem X-Frame-Options:
    curl -sI https://przyklad.pl/wp-login.php | grep -i x-frame
  4. Zablokowane pliki powinny zwracać 403:
    curl -sI https://przyklad.pl/xmlrpc.php | head -1
    curl -sI https://przyklad.pl/readme.html | head -1
    Powtórz sprawdzenie nagłówków na adresie, który zwraca 404. W Nginx bez always właśnie tam nagłówki znikają.
  5. Automatyczne audyty: Sprawdź domenę w serwisie SecurityHeaders.com, żeby zobaczyć ogólną ocenę i potwierdzić, że zadeklarowane dyrektywy rzeczywiście są wysyłane.

#Podsumowanie

Utwardzanie infrastruktury WordPressa to ciągła praca, która łączy dobre praktyki programowania w PHP z rzetelną administracją serwerem. Usunięcie śladów wersji CMS, nagłówki bezpieczeństwa i restrykcyjne uprawnienia plików chronią stronę przed atakami oportunistycznymi i automatycznymi skanerami.

Pełną ocenę ryzyk w Twoim serwisie znajdziesz w ramach mojego audytu bezpieczeństwa WordPress. Możesz też skorzystać ze wsparcia moich specjalistów 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.

Sprawdź inne usługi WordPress i bazę wiedzy

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

Często zadawane pytania

Najważniejsze odpowiedzi, które pomagają wdrożyć temat w praktyce.

Czy ukrycie wersji WordPressa wystarczy, żeby strona była bezpieczna#
Nie. Ukrycie wersji jedynie utrudnia wstępny rekonesans botom skanującym. Realne bezpieczeństwo opiera się na regularnych aktualizacjach, silnych hasłach z uwierzytelnianiem dwuskładnikowym (2FA) i poprawnie ustawionych nagłówkach bezpieczeństwa.
Czy usunięcie parametru ver psuje ładowanie CSS i JS#
Jeśli parametr zniknie ze wszystkich zasobów, przeglądarki przestaną wykrywać zaktualizowane pliki. Dlatego filtr z tego poradnika usuwa wyłącznie wersję rdzenia, a parametry wtyczek i motywów zostawia.
Czy całkowite wyłączenie XML-RPC jest bezpieczne#
Na zdecydowanej większości stron tak. Z tego interfejsu korzystają tylko Jetpack i aplikacja mobilna WordPressa przy stronach na własnym hostingu. Blokadę ustaw na serwerze WWW, bo filtr xmlrpc_enabled zostawia pingbacki aktywne.
Jak Content Security Policy wpływa na motywy#
Zbyt restrykcyjna polityka blokuje fonty, zewnętrzne wideo albo skrypty page builderów. Najpierw testuj ją nagłówkiem Content-Security-Policy-Report-Only, a dopiero potem włącz na produkcji.
Czy nadal warto wysyłać X-XSS-Protection#
Nie. MDN oznacza ten nagłówek jako przestarzały, a OWASP zaleca, by go nie wysyłać albo ustawić na 0. Ochronę przed XSS daje Content Security Policy.

Potrzebujesz FAQ dopasowanego do branży i rynku? Przygotuję wersję pod Twoje cele biznesowe.

Porozmawiajmy

Polecane artykuły