vps·web
PL

Konfiguracja Apache VirtualHost dla WordPress

VirtualHost WordPress — hardening i izolacja

Serwery · Konfiguracja Apache VirtualHost dla WordPress

Konfiguracja Apache VirtualHost dla WordPress — hardening, który przeżyje zhakowaną wtyczkę

Konfiguracja VirtualHost dla WordPress decyduje o tym, jak duże szkody wyrządzi jedna dziurawa wtyczka. Domyślny vhost z większości poradników uruchamia każdą stronę jako www-data, wykonuje PHP wszędzie tam, gdzie wyląduje plik, i zostawia xmlrpc.php otwarty na oścież — więc pierwszy wgrany backdoor czyta pliki wszystkich pozostałych witryn na serwerze. Ten artykuł buduje inny vhost: osobna pula PHP-FPM dla każdej strony pod dedykowanym użytkownikiem, blokada wykonywania PHP w wp-content/uploads, open_basedir zamykający każdą witrynę w jej własnym katalogu i odcięte na poziomie serwera punkty, które atakuje się brute-force'em. Tę samą izolację wgrywa za Ciebie generator konfiguracji Apache VirtualHost dla WordPress na vps-web.com, więc poniższy plik wygenerujesz z własną domeną jednym kliknięciem. Niżej masz pełną konfigurację, każdą dyrektywę z wyjaśnieniem i błędy, które zamieniają „zabezpieczony" vhost z powrotem w łatwy cel.

Generator VirtualHost dla WordPress — co robi i komu się przyda

Generator na vps-web.com przyjmuje kilka pól — domenę, opcjonalny alias subdomeny, wersję PHP, użytkownika systemowego będącego właścicielem strony, decyzję o certyfikacie Let's Encrypt oraz to, czy chcesz osobne logi per domena — i zwraca kompletny, zahardenowany zestaw: plik puli PHP-FPM, polecenie certbota, blok <VirtualHost> i komendy aktywacyjne. To nie jest „pięciolinijkowy vhost" z poradników dla początkujących. Każda strona dostaje własną pulę FPM działającą pod dedykowanym, pozbawionym logowania użytkownikiem systemowym, z open_basedir zamykającym PHP w obrębie jednego drzewa katalogów, z disable_functions ścinającym funkcje pomocnicze RCE (exec, shell_exec, proc_open i pokrewne) oraz z jawnymi blokadami na pliki, które atakujący sprawdza w pierwszej kolejności.

Rozwiązanie jest dla osoby, która prowadzi więcej niż jedną stronę WordPress na jednym VPS i nie godzi się, żeby przejęcie strony A oznaczało przejęcie stron od B do F. To freelancer hostujący kilkanaście stron klientów na jednej maszynie, agencja konsolidująca niskotrafficowe wizytówki i self-hoster, który woli poświęcić godzinę na izolację teraz niż weekend na reagowanie na incydent później. Jeśli prowadzisz dokładnie jedną stronę i nic poza nią, nadal zyskujesz na blokadach wykonywania PHP i bazowym disable_functions — izolacja per użytkownik ma po prostu mniejsze znaczenie, gdy lokator jest tylko jeden.

Generator oszczędza dokładnie tę część, którą wszyscy robią źle ręcznie: pamięć o tym, że wp-content/uploads wymaga zabicia wykonywania PHP dodatkowo, obok reguły w katalogu głównym, że .user.ini to wektor utrwalenia ataku, jeśli nie zneutralizujesz go w puli, że właścicielem socketu FPM musi być www-data, nawet gdy pula działa pod innym kontem. Pomiń jedno i konfiguracja wygląda na zahardenowaną, ale nie jest.

CTA: Otwórz generator VirtualHost dla WordPress na vps-web.com →

Praktyka — budowa zahardenowanego vhosta WordPress

Postawimy sklep.example.com od początku do końca: dedykowany użytkownik systemowy, osobna pula PHP-FPM, certyfikat Let's Encrypt i sam VirtualHost z pełnym blokiem hardeningu. Stan docelowy to strona, na której webshell wgrany przez dziurawą wtyczkę może czytać i zapisywać wyłącznie wewnątrz /var/www/sklep.example.com, nie wyjdzie do powłoki systemu i nie uruchomi niczego, co uda mu się zapisać w katalogu uploads.

Krok 1 — Użytkownik systemowy i katalogi

Najważniejsza pojedyncza decyzja to nie uruchamianie strony jako www-data. Wspólne konto www-data jest tym, co pozwala przejęciu jednej strony sięgnąć do plików każdej innej witryny w /var/www. Dedykowany, pozbawiony logowania użytkownik per strona to fundament, na którym opiera się wszystko inne.

# Dedykowany użytkownik bez logowania; -r = konto systemowe, -M = bez tworzenia katalogu domowego
useradd -r -M -d /var/www/sklep.example.com -s /usr/sbin/nologin sklepexample 2>/dev/null || true
install -d -o sklepexample -g sklepexample /var/www/sklep.example.com/tmp
chown -R sklepexample:sklepexample /var/www/sklep.example.com/public_html

useradd -r tworzy konto systemowe (bez wygasania, w niskim zakresie UID); -M pomija tworzenie katalogu domowego, bo to web root jest terytorium tego użytkownika; -s /usr/sbin/nologin sprawia, że wykradzione poświadczenia tego konta nie otworzą powłoki. Końcówka || true utrzymuje idempotentność polecenia, jeśli użytkownik już istnieje. Osobny katalog tmp należący do użytkownika strony staje się domem dla upload_tmp_dir i session.save_path w następnym kroku — pliki tymczasowe i sesje siedzą w izolacji strony, zamiast we współdzielonym przez wszystkich /tmp.

Krok 2 — Pula PHP-FPM dla pojedynczej strony

Tu izolacja staje się realna. Każda strona dostaje własny plik puli, działający jako użytkownik strony, z PHP zamkniętym w drzewie katalogów tej witryny.

; /etc/php/8.4/fpm/pool.d/sklep.example.com.conf
[sklep.example.com]
user = sklepexample
group = sklepexample
listen = /run/php/sklep.example.com.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = ondemand
pm.max_children = 10
pm.process_idle_timeout = 10s
pm.max_requests = 500
catch_workers_output = yes

; --- izolacja: webshell zamknięty w jednej witrynie ---
php_admin_value[open_basedir] = /var/www/sklep.example.com/:/tmp/
php_admin_value[upload_tmp_dir] = /var/www/sklep.example.com/tmp
php_admin_value[session.save_path] = /var/www/sklep.example.com/tmp

; --- neutralizacja .user.ini (wektor utrwalenia ataku) ---
php_admin_value[user_ini.filename] =

; --- ścięcie funkcji pomocniczych RCE ---
php_admin_value[disable_functions] = exec,passthru,shell_exec,system,proc_open,popen,proc_nice,proc_terminate,pcntl_exec,dl
php_admin_flag[allow_url_include] = off
; allow_url_fopen zostaje ON (aktualizacje WP/REST/oEmbed) — ogranicz ruch wychodzący na firewallu

Dwie linie z właścicielem mylą ludzi: pula działa jako sklepexample, ale listen.owner/listen.group to www-data. To celowe — worker działa jako użytkownik strony (więc zapisywane przez niego pliki mają poprawnego właściciela i nie czyta on innych stron), podczas gdy socket należy do www-data, żeby Apache (działający jako www-data) mógł się do niego podłączyć. Linia open_basedir to klatka: PHP odmawia każdej operacji na plikach poza /var/www/sklep.example.com/ i /tmp/, więc webshell nie zrobi cat /var/www/inna-strona/wp-config.php. disable_functions usuwa funkcje, których webshell używa do wyjścia z PHP do powłoki systemu — bez exec i proc_open większość gotowych backdoorów jest bezwładna. Linia user_ini.filename = jest subtelna, ale ważna: PHP domyślnie czyta plik .user.ini z katalogu wykonywanego skryptu, a atakujący, który potrafi zapisać plik w uploads, może podrzucić .user.ini z powrotem włączający funkcje zablokowane w disable_functions. Ustawienie nazwy pliku na pustą wyłącza ten wektor. Po zmianach przeładuj usługę:

systemctl reload php8.4-fpm

Workery WordPress zajmują 30–80 MB każdy, więc pm.max_children = 10 to ostrożny punkt startowy dla strony o niskim i średnim ruchu; podnoś dopiero po zmierzeniu szczytowego zużycia pamięci na worker względem przydzielonego RAM-u. Oficjalna dokumentacja konfiguracji PHP-FPM opisuje każdą dyrektywę puli, jeśli chcesz stroić dalej.

Krok 3 — Certyfikat Let's Encrypt

Wystaw certyfikat metodą webroot, zanim włączysz blok :443 — wyzwanie HTTP-01 musi najpierw przejść po zwykłym porcie 80.

certbot certonly --agree-tos --email root@example.com --webroot -w /var/lib/letsencrypt/ \
  -d sklep.example.com

certonly pobiera certyfikat bez pozwalania certbotowi na przepisanie Twojego vhosta (zbudowałeś go ręcznie, więc nie chcesz automatycznych edycji). --webroot -w /var/lib/letsencrypt/ mówi certbotowi, żeby zostawił token wyzwania pod tą ścieżką — czyli dokładnie tam, gdzie Alias w bloku :80 (następny krok) kieruje URL /.well-known/acme-challenge/. Certyfikat ląduje w /etc/letsencrypt/live/sklep.example.com/. Pełen zestaw wariantów certbota — webroot, standalone, DNS-01, wildcard — wygenerujesz w generatorze poleceń Let's Encrypt na vps.pyrek.com.pl.

Krok 4 — Plik VirtualHost

Teraz sam vhost: blok :80, który obsługuje wyzwanie ACME, a potem przekierowuje na HTTPS, i blok :443, który serwuje WordPress z pełnym stosem hardeningu.

# /etc/apache2/sites-available/sklep.example.com.conf
<VirtualHost *:80>
    ServerName sklep.example.com

    # ACME http-01 MUSI przejść przed przekierowaniem:
    Alias /.well-known/acme-challenge/ /var/lib/letsencrypt/.well-known/acme-challenge/
    <Directory "/var/lib/letsencrypt/">
        Require all granted
    </Directory>

    RewriteEngine On
    RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/
    RewriteRule ^ https://sklep.example.com%{REQUEST_URI} [L,R=301]

    ErrorLog  ${APACHE_LOG_DIR}/sklep.example.com-error.log
    CustomLog ${APACHE_LOG_DIR}/sklep.example.com-access.log combined
</VirtualHost>

<VirtualHost *:443>
    ServerAdmin root@example.com
    ServerName sklep.example.com
    Protocols h2 http/1.1

    DocumentRoot /var/www/sklep.example.com/public_html

    # permalinki WP wymagają .htaccess w katalogu głównym:
    <Directory "/var/www/sklep.example.com/public_html">
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    # ============================ HARDENING ============================
    # PHP TYLKO z puli per-user tej witryny:
    <FilesMatch "\.php$">
        SetHandler "proxy:unix:/run/php/sklep.example.com.sock|fcgi://localhost"
    </FilesMatch>

    # blokada alternatywnych rozszerzeń PHP (.phtml/.phar/.php5...):
    <FilesMatch "\.(phtml|pht|phps|phar|php[0-9])$">
        Require all denied
    </FilesMatch>

    # pliki wrażliwe / dotfiles / dumpy / wyciek wersji:
    <FilesMatch "(^\.ht|^\.user\.ini$|wp-config\.php|readme\.html|license\.txt|\.(sql|sqlite|log|bak|swp|inc)$)">
        Require all denied
    </FilesMatch>
    <DirectoryMatch "/\.(git|svn)(/|$)">
        Require all denied
    </DirectoryMatch>

    # xmlrpc.php — wektor brute/pingback (odblokuj, jeśli używasz Jetpack/aplikacji mobilnej):
    <Files "xmlrpc.php">
        Require all denied
    </Files>

    # ZERO PHP w katalogach zapisywalnych:
    <Directory "/var/www/sklep.example.com/public_html/wp-content/uploads">
        AllowOverride None
        <FilesMatch "\.(php[0-9]?|phtml|pht|phar|phps|inc)$"> Require all denied </FilesMatch>
    </Directory>
    <Directory "/var/www/sklep.example.com/public_html/wp-content/cache">
        AllowOverride None
        <FilesMatch "\.(php[0-9]?|phtml|pht|phar|phps|inc)$"> Require all denied </FilesMatch>
    </Directory>
    <Directory "/var/www/sklep.example.com/public_html/wp-content/upgrade">
        AllowOverride None
        <FilesMatch "\.(php[0-9]?|phtml|pht|phar|phps|inc)$"> Require all denied </FilesMatch>
    </Directory>
    # ==================================================================

    ErrorLog  ${APACHE_LOG_DIR}/sklep.example.com-error.log
    CustomLog ${APACHE_LOG_DIR}/sklep.example.com-access.log combined

    SSLEngine On
    SSLCertificateFile    /etc/letsencrypt/live/sklep.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/sklep.example.com/privkey.pem
    SSLProtocol -all +TLSv1.2 +TLSv1.3
    SSLHonorCipherOrder off
</VirtualHost>

Przejdźmy dyrektywy, które mają znaczenie:

SetHandler "proxy:unix:/run/php/sklep.example.com.sock|fcgi://localhost" wewnątrz <FilesMatch "\.php$"> kieruje PHP do socketu tej strony — nie do współdzielonej puli FPM. To linia wiążąca Apache z pulą per-user z kroku 2. Pomyl ścieżkę socketu, a dostaniesz albo 503, albo — gorzej — po cichu wpadniesz na pulę innej strony i stracisz izolację.

Dwa bloki <FilesMatch> z odmową domykają luki, które zostawia główny handler PHP. Pierwszy blokuje alternatywne rozszerzenia PHP — .phtml, .phar, .php5 — bo atakujący, który nie zapisze evil.php, chętnie zapisze evil.phtml w nadziei, że Twój handler złapał tylko .php. Drugi blokuje pliki, które wyciekają informacje lub zawierają sekrety: wp-config.php (dane do bazy), readme.html (dokładna wersja WordPressa), dumpy SQL, pliki backupów, pliki swap. Require all denied to składnia Apache 2.4; stara para Order deny,allow / Deny from all wyrzuca błędy na nowoczesnym Apache. Apache w swoim poradniku zabezpieczania PHP opisuje podejście z open_basedir w vhoście, jeśli chcesz odniesienia u źródła.

Trzy bloki <Directory> dla uploads, cache i upgrade to najcenniejszy element hardeningu specyficznego dla WordPressa. Te katalogi są zapisywalne przez serwer WWW — to tu dziura w nieuwierzytelnionym uploadzie podrzuca swój ładunek. Odmawiając wykonania jakiegokolwiek rozszerzenia PHP-podobnego w ich obrębie, backdoor wgrany do wp-content/uploads/2026/evil.php staje się statycznym plikiem, którego serwer odmawia uruchomić. AllowOverride None na tych katalogach oznacza też, że podrzucony .htaccess nie włączy wykonywania z powrotem. To zalecana przez WordPressa blokada wykonywania PHP, zrobiona na poziomie vhosta, żeby nie dało się jej nadpisać z wnętrza document roota.

Krok 5 — Aktywacja i przeładowanie

a2ensite sklep.example.com
apache2ctl configtest && systemctl reload apache2

a2ensite tworzy dowiązanie pliku z sites-available do sites-enabled. apache2ctl configtest waliduje składnię — nigdy tego nie pomijaj, bo przeładowanie zepsutej konfiguracji potrafi położyć cały serwer. && gwarantuje, że reload wykona się tylko, jeśli configtest przeszedł. reload ponownie wczytuje konfigurację bez zrywania żywych połączeń, w przeciwieństwie do restart, który ubija aktywne żądania.

Częste błędy i pułapki

Uruchomienie puli jako www-data i złudzenie izolacji

Najczęstsza wpadka: ludzie kopiują szablon puli per strona, ale zostawiają user = www-data. Teraz każda „izolowana" pula działa jako ten sam użytkownik, open_basedir jest jedyną rzeczą stojącą między stronami, a każda funkcja ignorująca open_basedir (a kilka to robi) daje odczyt między stronami. Jeśli user puli pokrywa się z użytkownikiem puli jakiejkolwiek innej strony, nie masz izolacji — masz jej iluzję. Każda strona potrzebuje własnego dedykowanego użytkownika.

Zapomniana blokada PHP w wp-content/uploads

Handler PHP w katalogu głównym z radością wykona wp-content/uploads/evil.php, jeśli jawnie tego nie zablokujesz. Tą ścieżką idzie niemal każde przejęcie WordPressa: wtyczka z dziurawym formularzem uploadu pozwala atakującemu zapisać plik .php w uploads, potem wchodzi na niego przeglądarką i ten się wykonuje. Blokada wykonywania tam — oraz w cache i upgrade — zamienia udany upload w nieszkodliwy plik statyczny. Sprawdź przez curl -s -o /dev/null -w '%{http_code}\n' https://sklep.example.com/wp-content/uploads/x.php — chcesz 403.

Pozostawiony aktywny .user.ini

PHP domyślnie czyta .user.ini z katalogu wykonywanego skryptu. Atakujący, który podrzuci plik do uploads, może też zostawić .user.ini z powrotem włączający funkcje zablokowane przez disable_functions dla każdego PHP uruchamianego z tego katalogu. Ustawienie php_admin_value[user_ini.filename] = (puste) w puli wyłącza ten mechanizm. Pomiń to, a Twoja linia disable_functions ma udokumentowany bypass.

Niezgodność właściciela socketu — błąd 503 na każdym żądaniu PHP

Jeśli listen.owner/listen.group nie są ustawione na www-data, Apache (działający jako www-data) nie podłączy się do socketu FPM, a każde żądanie PHP zwróci 503 z błędem uprawnień w logu. Worker działa jako użytkownik strony; socket musi być czytelny dla Apache. Ustaw listen.owner = www-data, listen.group = www-data, listen.mode = 0660.

„AH00558: Could not reliably determine the server's fully qualified domain name"

Ostrzeżenie, nie błąd krytyczny, ale zaśmieca każde przeładowanie. Oznacza, że Apache nie zdołał ustalić globalnego ServerName. Naprawisz to, dodając ServerName twoja.fqdn do /etc/apache2/apache2.conf (albo po prostu ServerName localhost). Nie wpływa to na dyrektywy ServerName na poziomie vhosta — to osobna sprawa.

Blokada xmlrpc.php psuje Jetpack lub aplikację mobilną

<Files "xmlrpc.php"> Require all denied </Files> zwraca 403 na wszystko, co trafia w ten endpoint — czego właśnie chcesz, bo xmlrpc.php to magnes na brute-force i amplifikację pingback. Ale jeśli faktycznie używasz Jetpacka, aplikacji mobilnej WordPress albo zdalnego publikowania, zależą one od xmlrpc.php i się zepsują. Usuń wtedy blokadę albo zastąp ją listą dozwolonych IP dla usług, które tego potrzebują.

Certbot pada, bo przekierowanie łapie wyzwanie

Jeśli blok :80 przekierowuje wszystko na HTTPS, zanim wyzwanie ACME zdąży się zaserwować, walidacja HTTP-01 certbota pada. Linia RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/ to ta, która wyłącza ścieżkę wyzwania z przekierowania. Usuń ten warunek, a certbot nie zwaliduje. Kolejność ma znaczenie: Alias i <Directory> dla wyzwania muszą być przed regułą przekierowania.

FAQ — najczęściej zadawane pytania

Jak sprawić, żeby jeden zhakowany WordPress nie zaraził pozostałych na serwerze?

Uruchom każdą stronę pod własnym użytkownikiem systemowym z własną pulą PHP-FPM i ustaw open_basedir per pula, żeby zamknąć PHP w katalogu danej strony. Przy współdzielonym koncie www-data przejęcie jednej strony pozwala czytać i zapisywać pliki każdej innej; przy pulach per użytkownik i open_basedir webshell jest zamknięty w stronie, na którą wpadł. To rdzeń konfiguracji, którą zwraca generator vhostów WordPress.

Jak wyłączyć wykonywanie PHP w katalogu uploads WordPressa na Apache?

Dodaj blok <Directory> dla wp-content/uploads z AllowOverride None i blokiem <FilesMatch> odmawiającym .php, .phtml, .phar i podobnych rozszerzeń przez Require all denied. Zrobienie tego na poziomie vhosta (nie w .htaccess) oznacza, że podrzucony do uploads .htaccess nie włączy wykonywania z powrotem. Powtórz dla cache i upgrade, które również są zapisywalne przez serwer WWW.

Czy Require all granted to to samo co Allow from all?

Nie. Allow from all to składnia Apache 2.2; w 2.4 zastąpiono ją modułem mod_authz_core i dyrektywą Require all granted (albo Require all denied do blokady). Stara składnia w 2.4 wyrzuca AH01630: client denied by server configuration i serwuje 403. Jeśli widzisz Order allow,deny w parze z Allow from all, konfiguracja jest sprzed 2012 roku.

Czy blokować xmlrpc.php w WordPressie?

Tak, chyba że używasz Jetpacka, aplikacji mobilnej albo zdalnego publikowania — te tego wymagają. xmlrpc.php to ulubiony cel ataków brute-force (pozwala próbować wiele poświadczeń w jednym żądaniu) i amplifikacji DDoS przez pingback. Blokada na poziomie serwera przez Require all denied serwuje tani 403 bez ładowania PHP i bazy, co jest dużo lżejsze niż blokada przez wtyczkę.

Dlaczego pula PHP-FPM działa jako jeden użytkownik, a socket należy do www-data?

Proces workera działa jako dedykowany użytkownik strony, żeby tworzone przez niego pliki miały poprawnego właściciela i żeby nie czytał innych stron. Socket należy do www-data, bo Apache działa jako www-data i potrzebuje uprawnień do podłączenia się do socketu. Dwie różne sprawy: tożsamość procesu (izolacja) i dostęp do socketu (łączność). Ustaw user/group na użytkownika strony, a listen.owner/listen.group na www-data.

Czy potrzebuję osobnej puli PHP-FPM dla każdej strony WordPress?

Jeśli hostujesz więcej niż jedną stronę na serwerze i zależy Ci na izolacji — tak. Współdzielona pula oznacza, że przejęcie którejkolwiek strony działa jako ten sam użytkownik z dostępem do plików wszystkich stron. Pula per strona pod dedykowanym użytkownikiem, z open_basedir per strona, to różnica między jedną przejętą stroną a wszystkimi. Przy serwerze z jedną stroną ma to mniejsze znaczenie, ale blokady disable_functions i wykonywania PHP nadal obowiązują.

Której wersji PHP użyć do WordPressa?

Użyj aktualnie wspieranej gałęzi — PHP 8.2, 8.3 lub 8.4 w chwili pisania. Uruchamianie wersji po końcu wsparcia (cokolwiek z 7.x albo 8.0/8.1, gdy wsparcie wygaśnie) oznacza niezałatane podatności interpretera, które atakujący może powiązać z punktem zaczepienia we wtyczce. Generator pozwala wybrać wersję FPM, żeby ścieżka puli i nazwa socketu zgadzały się z tym, co masz zainstalowane.

Jak sprawdzić, czy hardening faktycznie działa?

Użyj curl przeciwko endpointom, które powinny być zablokowane, i potwierdź, że zwracają 403: curl -s -o /dev/null -w '%{http_code}\n' https://sklep.example.com/wp-content/uploads/x.php (blokada PHP w uploads), to samo przeciwko /x.phtml (alternatywne rozszerzenie), /wp-config.php (plik wrażliwy) i /xmlrpc.php. Sprawdź przekierowanie przez curl -sI http://sklep.example.com/ | grep -i location (powinno pokazać 301 na https). Potwierdź użytkownika puli przez ps -o user= -C php-fpm8.4 | sort -u — Twój użytkownik strony powinien się pojawić.

Następne kroki

Wygeneruj swoją zahardenowaną konfigurację z własną domeną, wersją PHP i użytkownikiem w generatorze Apache VirtualHost dla WordPress na vps-web.com — wypełnij formularz, dostaniesz plik puli, polecenie certbota i vhost gotowy do wklejenia. Każde ustawienie izolacji z tego artykułu jest stosowane automatycznie.

Tematy powiązane, które rozbudują zahardenowany stos:

Jeśli wolisz format wideo, na kanale pokazuję administrację Linuksem, Proxmox i hardening hostingu w praktyce: Praktycznie o informatyce na YouTube. Subskrybuj, jeśli interesuje Cię izolowany hosting wielu stron i bezpieczeństwo serwera.