NGINX: Nazwy domen i dyrektywa server_name

12 Sep 2019

best-practices  http  listen  nginx  server_name 

Share on:

Dokładne nazwy, nazwy symboli wieloznacznych rozpoczynające się od gwiazdki i nazwy symboli wieloznacznych kończące się gwiazdką są przechowywane w trzech tablicach skrótów powiązanych z dyrektywami nasłuchiwania.

Najpierw przeszukiwana jest tabela skrótów z dokładnymi nazwami. Jeśli więc najczęściej żądanymi nazwami serwerów są np. example.com i www.example.com, bardziej efektywne jest ich jawne zdefiniowanie.

Jeśli nie zostanie znaleziona dokładna nazwa, przeszukiwana jest tablica skrótów z nazwami symboli wieloznacznych rozpoczynającymi się gwiazdką. Jeśli nie ma tam żądanej nazwy, przeszukiwana jest tablica skrótów z nazwami symboli wieloznacznych kończącymi się gwiazdką. Wyszukiwanie takiej tablicy skrótów jest wolniejsze niż wyszukiwanie tabeli skrótów nazw dokładnych, ponieważ nazwy są wyszukiwane według części domeny.

Jeżeli żadne z dopasowań nazwy serwera nie zostanie znalezione, to na samym końcu przeszukiwana jest tablica skrótów nazw zbudowanych za pomocą wyrażeń regularnych. Wyrażenia regularne są testowane sekwencyjnie, dlatego są najwolniejszą metodą i nie są skalowalne. Z tych powodów lepiej jest używać dokładnych nazw tam, gdzie to możliwe w celu dokładnej identyfikacji i filtrowania puli domen.

Podczas wyszukiwania serwera wirtualnego według nazwy, jeśli nazwa pasuje do więcej niż jednego z określonych wariantów, np. zarówno nazwa wieloznaczna, jak i wyrażenie regularne pasują do żądania, wybierany będzie wariant z zachowaniem następującej kolejności (co wyjaśniłem już wyżej):

Spójrzmy jeszcze co na temat nazw wieloznacznych oraz wyrażeń regularnych mówi oficjalna dokumentacja:

A wildcard name may contain an asterisk only on the name’s start or end, and only on a dot border. The names www.*.example.org and w*.example.org are invalid. [...] A special wildcard name in the form .example.org can be used to match both the exact name example.org and the wildcard name *.example.org.

The name *.example.org matches not only www.example.org but www.sub.example.org as well.

To use a regular expression, the server name must start with the tilde character. [...] otherwise it will be treated as an exact name, or if the expression contains an asterisk, as a wildcard name (and most likely as an invalid one). Do not forget to set ^ and $ anchors. They are not required syntactically, but logically. Also note that domain name dots should be escaped with a backslash. A regular expression containing the characters { and } should be quoted.

Na podstawie tego należy wiedzieć, że:

Ogólnie rzecz biorąc, wykorzystywanie wyrażeń regularnych czy to dla server_name, czy dla bloków lokalizacji jest w większości przypadków średnim pomysłem (trzeba je dokładnie przetestować) i powinno być ograniczone do naprawdę prostych przypadków — jednak w specyficznych sytuacjach może być pomocne. Co ciekawe, podczas budowania wyrażeń regularnych, możliwe jest przechwycenie takiego wyrażenia (lub jego elementu) i użycie go jako zmiennej:

server {

  server_name ~^(www\.)?(?<domain>.+)$;

  location / {
    root /sites/$domain;
  }

}

server {

  server_name ~^(?<subdomain>.+)\.example\.org;

  location / {
    rewrite ^/(.*)$ /$subdomain/$1 break;
  }

}

Oto przykład konfiguracji niezalecanej:

server {

  listen 192.168.252.10:80;

  # Z oficjalnej dokumentacji: "Searching wildcard names hash table is slower than searching
  # exact names hash table because names are searched by domain parts. Note that the special
  # wildcard form '.example.org' is stored in a wildcard names hash table and not in an exact
  # names hash table.":
  server_name .example.org;

  ...

}

Natomiast niżej znajduje się przykład konfiguracji zalecanej (zwłaszcza w stosunku do powyższej):

server {

  listen 192.168.252.10:80;

  # .example.org = example.org i *.example.org
  server_name example.org www.example.org *.example.org;

  ...

}

Widzimy, że kiedy najczęściej żądanymi nazwami serwera są example.org i www.example.org, bardziej efektywne jest ich jawne zdefiniowanie (drugi przykład).

Na koniec poruszę jeszcze jeden ciekawy problem. Jeśli zdefiniowana przez nas domena nie istnieje, klient HTTP nie będzie mógł połączyć się z serwerem HTTP, a tym samym nie otrzyma żadnej odpowiedzi, ponieważ protokoły niższej warstwy nie będą mogły zestawić połączenia, aby zapewnić kanał komunikacji dla protokołu HTTP. Często się jednak zdarza, że klient ustawił domenę, która nie jest obsługiwana przez serwer, a żądanie do serwera dochodzi — wtedy dobrym pomysłem jest zwrócić kod błędu, który zapętli się wewnątrz serwera i zerwie połączenie po określonym czasie bez faktycznego zwrócenia odpowiedzi do klienta.

Serwer NGINX pozwala zrobić to za pomocą return 444;, który nie jest częścią standardu (w rzeczywistości nie jest to nawet stan odpowiedzi) i został wprowadzony, aby wskazać, by serwer po prostu nie wysłał odpowiedzi i zamknął połączenie. Inne serwery w takim przypadku zwracają często kod 404 Domain not found. Po więcej informacji zerknij do artykułu NGINX: Przetwarzanie żądań a niezdefiniowane nazwy serwerów.