Úvod
Nginx patří mezi nejoblíbenější webové servery na světě. Dokáže úspěšně zpracovat velké množství současných klientských připojení. Zároveň funguje jako poštovní, webový nebo reverzní proxy server.
Tento průvodce si klade za cíl nastínit zákulisní metody, které určují, jak Nginx zpracovává požadavky klientů. Demystifikujeme konfiguraci serveru a location bloků a také vysvětlíme, jak nejlépe omezit zdánlivou nepředvídatelnost při zpracování požadavků.
Nejprve si ukážeme komplexní návod na jak nainstalovat Nginx na váš server Ubuntu. Nyní můžeme začít!
Konfigurace bloků v Nginx
Logický přístup serveru Nginx spočívá v třídění konfigurací určených pro různé účely do samostatných, logičtějších obsahových bloků. Ty jsou uspořádány v hierarchické struktuře. Když klient odešle požadavek, Nginx zahájí proces, kterým určí, které z těchto konfiguračních bloků jsou pro vyřízení tohoto požadavku nejvhodnější. Na tento rozhodovací proces se zaměříme.
Hlavními bloky, o kterých budeme hovořit, budou bloky server a location. Bloky server jsou podmnožinou konfigurací, které Nginx vytváří a které definují, který virtuální server bude zodpovědný za zpracování definovaného typu požadavku. Nejčastěji jsou založeny na IP adrese, doménovém jménu nebo portu příchozího požadavku. Administrátoři konfigurují více bloků server. Poté se musí rozhodnout, které z připojení by mělo požadavek zpracovat.
Bloky location se nacházejí uvnitř bloků server. Ty rozhodují o tom, jak a které prostředky můžete využít ke zpracování příchozích požadavků na jejich konkrétní nadřazený server. Tento model je vysoce flexibilní. Prostor URI lze nakonfigurovat tak, aby tyto bloky využíval jakýmkoli způsobem, který administrátor považuje za nejlepší.
Rozhodování o tom, který blok zpracuje který požadavek v Nginx
Nginx umožňuje definovat více bloků server. Všechny fungují jako různé virtuální webové servery. Proto je zapotřebí metoda, která určí, který server bude řešit konkrétní příchozí požadavky. To se provádí nalezením nejvhodnějšího řešení pro vyřízení požadavku prostřednictvím systému definovaných kontrol.
Nginx pracuje především se dvěma hlavními direktivami bloku server: listen a server_name.
Hledání možných shod pomocí direktivy „Listen“
První věc, kterou Nginx vyhodnocuje, je port a IP adresa požadavku. Poté je porovná s direktivou listen každého serveru. Toto parsování seznamu serverů pomáhá izolovat pouze ty bloky server, které mohou daný požadavek vyřešit.
Direktiva listen obvykle definuje port a IP adresu, na které bude konkrétní blok server reagovat. Blok server, který neobsahuje direktivu listen, obdrží ve výchozím nastavení parametry listen 0.0.0.0:80. Pokud je Nginx spuštěn běžným uživatelem bez oprávnění root, je parametr listen definován jako 0.0.0.0:8080. To znamená, že bez ohledu na rozhraní, pokud požadavky přicházejí z portu 80, budou na ně reagovat takto definované bloky. Tato výchozí hodnota však nemá při výběru serveru velkou váhu.
Direktivu listen můžete nakonfigurovat na:
- Samostatnou IP adresu, která naslouchá požadavkům na výchozím portu (80).
- Samostatný port, který naslouchá na jakémkoli rozhraní na tomto portu.
- Kombinaci portu a IP adresy.
- Nastavenou cestu k Unix socketu (tato možnost má význam pouze tehdy, když požadavky procházejí mezi různými servery).
Při rozhodování o tom, kterému bloku server bude požadavek odeslán, uplatní Nginx sadu pravidel. Pravidla závisí na konkrétní konfiguraci direktivy listen. Jsou následující:
- Pokud je direktiva listen neúplná, chybějící části získají své výchozí hodnoty. To znamená, že IP adresa a port budou pro zpracování požadavku doplněny výchozími hodnotami.
- V tomto případě použije blok, který neobsahuje žádnou direktivu listen, výchozí hodnotu 0.0.0.0:80.
- Blok, kterému chybí port a má IP adresu 111.111.111.111, se změní na 111.111.111.111:80.
- Pokud neexistuje žádná IP adresa, blok s portem 8888 získá výchozí IP adresu, která se připojí a vytvoří 0.0.0.0:8888.
- Jakmile je určena IP adresa a port, Nginx začne hledat bloky serveru, které odpovídají tomuto portu.
- Pokud najde pouze jednu konkrétní shodu, bude to tento blok serveru. Pokud vyhovuje více bloků, Nginx se obrátí na direktivu server_name, aby přesněji určil konkrétní blok serveru.
Nginx přistoupí k vyhodnocení direktivy server_name pouze v případě, že nenašel blok serveru s přesnou úrovní specifičnosti z direktivy listen. Pokud je example.com na portu 80 s IP adresou 192.168.1.10, první blok v tomto příkladu bude vždy ten, který požadavek vyřídí. To platí bez ohledu na to, co říká direktiva server_name:

Pokud existuje více než jeden vyhovující blok serveru se shodnou specifičností, bude vzata v úvahu direktiva server_name.
Hledání možných shod pomocí direktivy „Server_Name“
Pokud jsou direktivy listen stejně specifické, Nginx zkontroluje hlavičku ’Host’ v požadavku. Tato hodnota obsahuje IP adresu domény, kterou se klient původně pokoušel kontaktovat. Nginx použije direktivu server_name uvnitř každého stále vyhovujícího kandidátského bloku serveru. Tato vyhodnocení provádí na základě vzorce. Ten je následující:
- Prvním pokusem Nginxu bude identifikovat blok s server_name, který přesně odpovídá hodnotě hlavičky „Host“ v požadavku. Pokud jej najde, bude požadavek obsluhovat blok obsahující přesnou shodu. V případě, že najde více bloků, vybere první v seznamu.
- Pokud neexistují žádné přesné shody, Nginx se pokusí použít server_name k nalezení bloku serveru, který odpovídá pomocí *, což je zástupný znak (wildcard) na začátku názvu bloku serveru v konfiguraci. Nalezení bloku touto metodou znamená, že blok serveru byl určen. Pokud najde více než jednu shodu, požadavek vyřídí nejdelší shoda.
- Bez odpovídajícího zástupného znaku se Nginx pokusí najít blok serveru s odpovídajícím koncovým zástupným znakem. Jinými slovy se bude jednat o název serveru s * v konfiguraci. Pokud je nalezen, použije se pro požadavek. Pokud jich však najdete více, Nginx opět použije nejdelší shodu.
- V případě, že ani po obou pokusech se zástupnými znaky neexistují žádné shody, Nginx vyhodnotí ty bloky serveru, které definují server_name pomocí regulárních výrazů (označených znakem ~ před názvem). První výskyt server_name s výrazem odpovídajícím hlavičce „Host“ bude považován za blok serveru pro vyřízení požadavku.
- Pokud v tomto okamžiku stále neexistují žádné shody, Nginx použije výchozí blok serveru pro danou kombinaci portu a IP adresy.
Každá kombinace portu/IP adresy bude mít určený blok serveru. Ten se použije v případě, že pravidla pro určení vhodného bloku serveru pro vyřízení požadavku budou bezvýsledná. Půjde o první blok v konfiguraci obsahující volbu default_server v direktivě listen (tím se přepíše původně nalezený algoritmus). Každá kombinace IP adresy/portu může mít nejvýše jedno nastavení default_server.
Příklady výběru bloku serveru
Pokud se definovaný server_name přesně shoduje s hodnotou hlavičky „Host“, bude pro zpracování požadavku vybrán tento blok serveru. Následující příklad ukazuje hlavičku „Host“ požadavku označenou jako „host1.example.com“. V tomto případě vybere druhý server:

Bez přesné shody Nginx zkontroluje, zda existuje server_name se zástupným znakem. Pokud ne, vybere se nejdelší shoda začínající zástupným znakem. V následujícím textu „www.example.org“ je v hlavičce „Host“. To znamená, že vybere druhý blok:

Pokud nedojde ke shodě začínající zástupným znakem, Nginx přejde ke kontrole koncového zástupného znaku. Pro zpracování požadavku bude vybrána nejdelší shoda končící zástupným znakem. V tomto případě je hlavička „Host“ „www.example.com“, takže zvolí třetí blok serveru:

Pokud stále nedojde k žádné shodě, Nginx se pokusí porovnat direktivy server_name pomocí regulárních výrazů. Pro zpracování požadavku se vybere první z těchto výrazů. Pokud je „Host“ „www.example.com“, pro vyřízení požadavku bude vybrán druhý blok serveru:

Pokud stále nedojde k žádné shodě, požadavek přejde na kombinaci IP adresy a portu s nastaveným odpovídajícím výchozím serverem.
Porovnávání bloků location
Nginx také musí stanovit algoritmus, podle kterého rozhodne, který blok location na serveru bude zodpovědný za odpověď na požadavek.
Syntaxe bloků location
Před vysvětlením, jak Nginx rozhoduje o určení bloku location, který bude zpracovávat požadavky, si projdeme syntaxi v definicích bloků location. Jak již bylo uvedeno dříve, bloky location se nacházejí v blocích server (a dalších blocích location). Jejich účelem je rozhodovat o tom, jak zpracovat URI požadavku. URI je část požadavku, která následuje za IP adresou a portem nebo doménovým jménem v požadavku.
Bloky location obvykle vypadají takto:

Nginx porovná URI požadavku s location_match. Přítomnost či nepřítomnost výše uvedeného modifikátoru určí způsob, jakým se Nginx pokusí bloky porovnat. V závislosti na modifikátoru budou bloky location interpretovány podle následujících pravidel:
- Bez modifikátorů: Bez jakýchkoli modifikátorů bude location interpretován jako prefixová shoda. To znamená, že zadaná cesta se bude porovnávat se začátkem URI v požadavku, aby se určila správná shoda.
- =: Znaménko rovná se znamená, že tento blok bude považován za shodný, pokud se URI požadavku přesně shoduje se zadaným umístěním.
- ~: Modifikátor vlnovka znamená, že shoda bloku location bude citlivá na velikost písmen.
- ~*: Kombinace vlnovky a hvězdičky znamená, že blok location nebude při hledání shody citlivý na velikost písmen.
- ^~: Pokud před modifikátorem vlnovky předchází stříška, k porovnávání regulárních výrazů nedojde, pokud bude tento blok vybrán jako nejlepší shoda bez regulárních výrazů.
Příklady syntaxe bloků location
Pro uvedení příkladu prefixové shody: blok location bude vybrán pro odpověď na URI požadavku ve tvaru /site, /site/page1/index.html nebo /site/index/html:

Pro účely této ukázky nezbytné shody URI bude blok vždy použit k odpovědi na požadavky URI ve tvaru /page1, a nikoli na URI požadavku /page1/index.html. Pokud se jedná o vybraný blok a ten splní požadavek pomocí indexové stránky, skutečný handler požadavku bude interně přesměrován na jiné umístění:

Například pro umístění, které musí být interpretováno s výrazem citlivým na velikost písmen, by následující blok nemohl zpracovat požadavky na /FLOWER.PNG. Bude však zpracovávat požadavky na /tortoise.jpg:

Dále si všimněte bloku, který by umožňoval shodu bez ohledu na velikost písmen, podobně jako výše uvedený. V tomto případě by blok mohl zpracovat jak //tortoise.jpg a /FLOWER.PNG:

Poslední variantou je ta, kdy blok zabrání porovnávání regulárních výrazů, pokud se ukáže, že jde o optimální shodu bez regulárních výrazů. Tento blok může zpracovávat požadavky na /costumes/ninja.html:

Abychom to upřesnili, modifikátory určují způsob, jakým se rozhoduje o blocích location. To nám však neříká, jaký rozhodovací algoritmus Nginx používá k identifikaci bloku location, do kterého má být požadavek odeslán. Tomu se budeme věnovat dále.
Výběr location, které bude zpracovávat požadavky v Nginx
Způsob, jakým Nginx vybírá location, která zpracovává požadavek, je podobný tomu, jak se vybírají bloky serveru. Jinými slovy, optimální location pro každý požadavek určuje průchodem určitým procesem. Abyste mohli Nginx nakonfigurovat přesně a správně, je nezbytné tomuto procesu porozumět.
S ohledem na dříve zmíněné deklarace location používá Nginx podobně potenciální kontexty location tak, že kontroluje způsobilost pro každou location porovnáním s URI z daného požadavku. Při tom uplatňuje následující algoritmus:
- Nejprve Nginx zkontroluje všechny typy location, které neobsahují regulární výraz. Činí tak vyhledáním všech shod na základě prefixu location. Za tímto účelem porovná location s úplným URI požadavku.
- Nginx začne hledat přesnou shodu. Jakmile je identifikován blok location, který používá modifikátor =, porovná se s požadovaným URI. Pokud se oba přesně shodují, je tento blok location okamžitě vybrán ke zpracování požadavku.
- Pokud neexistují žádné location přesně odpovídající porovnání s modifikátorem =, Nginx přistoupí k vyhodnocení neexaktních prefixů. Jakmile určí location s nejdelším prefixem, který odpovídá URI požadavku, provede následující vyhodnocení:
- Pokud location s nejdelší shodou prefixu používá modifikátor ^~, bude tato location okamžitě vybrána.
- Pokud location s nejdelším prefixem nepoužívá modifikátor ^~, Nginx si tuto shodu dočasně zapamatuje, aby se zaměření vyhledávání mohlo posunout.
- Jakmile je nalezena a uložena shoda s nejdelším prefixem location, Nginx přejde k vyhodnocování location s regulárními výrazy. Ty zahrnují shody rozlišující i nerozlišující velikost písmen. Pokud nejdelší shodná prefixová location obsahuje nějaké regulární location, Nginx přeorganizuje seznam tak, aby je umístil blízko začátku seznamu location. První výraz z nově uspořádaného seznamu, který odpovídá URI požadavku, bude vybrán jako location pro vyřízení požadavku.
- Pokud nebudou nalezeny žádné regulární výrazy vyhovující URI požadavku, bude ke zpracování požadavku vybrána dříve uložená location.
Nginx ve výchozím nastavení upřednostňuje shody regulárních výrazů před preferenčními prefixovými shodami. Nejprve však vyhodnocuje prefixové location, aby administrátor mohl tuto tendenci přebít pomocí modifikátorů = a ^~.
Dalším důležitým poznatkem je, že zatímco prefixové location jsou obvykle založeny na nejspecifičtější, nejdelší nalezené shodě, kontrola regulárních výrazů se zastaví, jakmile je identifikována první shoda. To znamená, že umístění v konfiguraci má pro location s regulárními výrazy reálné důsledky.
Posledním bodem, který je třeba zmínit, je, že shody regulárních výrazů v rámci shody s nejdelším prefixem v podstatě předběhnou řadu během vyhodnocování location v Nginx. Ty budou umístěny na začátek seznamu a vyhodnoceny před ostatními regulárními výrazy.
Kdy dochází ke skoku na jiné location při vyhodnocování bloků location?
Obvykle platí, že jakmile je požadavek posouzen a je vybrán blok location pro jeho zpracování, bude vyřízen zcela v tomto kontextu. To znamená, že o zpracování požadavku rozhodují pouze zděděné direktivy a vybrané location, bez jakéhokoli vlivu sourozeneckých bloků location.
Ačkoli se jedná o obecné pravidlo, které umožňuje předvídatelný návrh bloků location, někdy mohou určité direktivy uvnitř location spustit také nové vyhledávání. Jinými slovy, pravidlo „pouze jednoho bloku location“ má několik výjimek. Tyto výjimky nemusí odpovídat očekávání bloků location. Proto nemusí požadavek vyřídit podle očekávání.
Tato interní přesměrování se mohou projevit v důsledku některých direktiv, včetně:
- index
- rewrite
- error_page
- try_files
Pokud použijete direktivu index, vždy to povede k internímu přesměrování během zpracování požadavku. Zatímco nalezení shody location obvykle ukončí provádění algoritmu pro urychlení procesu výběru, pokud je nalezená shoda location adresářem, požadavek bude pravděpodobně přesměrován na jiné location, aby byl formálně zpracován.
Například následující první location odpovídá URI požadavku /exact. Aby se však požadavek zpracoval, direktiva index, kterou blok location dědí, přesměruje požadavek do sekundárního bloku:

Pro tento scénář, pokud provádění musí zůstat v primárním bloku, bude muset požadavek na adresář zpracovat jiné schéma. Jedním ze způsobů, jak toho dosáhnout, je nastavit neplatný index pro daný blok a místo toho aktivovat autoindex:

I když tato metoda může v několika případech fungovat, ve většině kontextů není obecně prakticky použitelná. Přesná shoda adresáře může být užitečná v situacích, kdy je potřeba požadavek přepsat. To spustí zcela nové vyhledávání location.
Další direktivou, kterou lze použít k přehodnocení zpracovávaného location, je direktiva try_files. Říká Nginxu, aby konkrétně zkontroloval, zda existuje definovaná sada souborů nebo adresářů, přičemž posledním vyhledávacím kritériem je URI, na které má Nginx interně přesměrovat.
Zamysleme se nad následující konfigurací:

Pokud přijde požadavek na /blahblah, obdrží jej první location. Nenalezení souboru blahblah v adresáři /var/www/main spustí následné vyhledávání blahblah.html. Poté se bude hledat podadresář s názvem blahblah v adresáři /var/www/main. Pokud všechny tyto kontroly selžou, přesměruje se na /fallback/index.html. To spustí další vyhledávání location, které zachytí jiný blok location. Poté zpracuje soubor /var/www/another/fallback/index.html.
Další direktivou, která vede k přesměrování na jiný blok location, je direktiva rewrite. Nginx bude hledat nové odpovídající location na základě výsledku direktivy rewrite, pokud je použit parametr last. Pokud se předchozí příklad upraví tak, aby nyní obsahoval tuto direktivu rewrite, je zřejmé, že požadavek lze přesměrovat na jiné location bez implementace direktivy try_files:

V tomto příkladu bude požadavek na /rewrite/hello nejprve řešen prvním location. Poté, co bude přepsán na /hello, se spustí sekundární vyhledávání location. To se shoduje s prvním location. Bude zpracováno direktivou try_file, což může vést k návratu k /fallback/index.html, pokud nepřinese žádné výsledky.
Pokud je však podán požadavek na /rewrite/fallback/hello, bude nalezena shoda s prvním blokem. Přepis (rewrite) se tedy zpracuje znovu, ale tentokrát bude výsledkem /fallback/hello. Požadavek bude zpracován v jiném bloku location.
K podobným situacím dochází, když použijete direktivu return k odeslání stavových kódů 301 nebo 302. Jediným rozdílem je, že vznikne nový požadavek, což se projeví velmi zřejmým přesměrováním. Podobně k tomu může dojít u direktivy rewrite, když použijete příznaky permanent nebo redirect.
Další direktivou, která může vést k podobným interním přesměrováním jako try_again, je direktiva error_page. Tu můžete použít, když při zpracování narazíte na konkrétní chybové kódy. Pokud je nastavena direktiva try_files, direktiva error_page se pravděpodobně nikdy nespustí. Je to proto, že tato direktiva zpracuje celý životní cyklus požadavku.
Zvažme následující příklad:

V tomto případě bude každý požadavek zpracován prvním blokem, který obsluhuje soubory z /var/www/main. To neplatí pro požadavky, které začínají na /another. Pokud by však soubor nebyl nalezen, bude zahájeno interní přesměrování na /another/whoops/html. To povede k dalšímu vyhledávání location. To následně nasměruje požadavek do sekundárního bloku, přičemž tento soubor bude adresován z /var/www/another/whoops.html.
Jak je zřejmé, pochopení situací, kdy Nginx spustí nové vyhledávání location, může pomoci lépe předvídat chování systému při zpracování požadavků.
Závěr
Práce administrátorů se nesmírně zjednoduší, jakmile pochopí metody, kterými Nginx zpracovává požadavky klientů. To administrátorům umožňuje zjistit, do kterého bloku serveru požadavek směřuje. Mohou také určit, který blok location bude vybrán na základě URI požadavku. Celkově to administrátorům dává možnost sledovat kontexty aplikované Nginxem při zpracování každého požadavku.
Na závěr se můžete podívat na další návody na našem blogu zaměřené na Nginx. Pomohou vám lépe využít jeden z nejpopulárnějších webových serverů na světě:
- Svět webových serverů: Apache vs. Nginx
- Jak zabezpečit Nginx pomocí Let’s Encrypt na Ubuntu 20.04
- Automatizace obnovy SSL certifikátů LetsEncrypt pro Nginx
- Nasazení Laravel, Nginx a MySQL pomocí Docker Compose
Příjemnou práci!
Komentáře
Zatím žádné komentáře. Buďte první.