# Nginx

Nejrozšířenější reverse proxy a webserver. Upovídanější než [Caddy](Caddy), ale najdeš k němu návod na cokoliv a při vysoké zátěži je nejrychlejší.

Koncepty za tím jsou v [reverse proxy](Reverse-proxy) a [TLS](TLS-a-HTTPS).

## Struktura konfigurace

```
/etc/nginx/
├── nginx.conf              # hlavní, globální nastavení
├── conf.d/*.conf           # sem se dávají vlastní konfigurace
├── sites-available/        # Debian a Ubuntu: dostupné weby
└── sites-enabled/          # symlinky na aktivní
```

Debianovský systém `sites-available` a `sites-enabled` je jen konvence. Aktivace se dělá symlinkem:

```bash
ln -s /etc/nginx/sites-available/mujweb /etc/nginx/sites-enabled/
nginx -t          # kontrola syntaxe, VŽDYCKY před reload
systemctl reload nginx
```

To `nginx -t` je návyk, který stojí za vypěstování. Reload s chybnou konfigurací nechá běžet starou, ale restart shodí celý server.

## Reverse proxy, minimální funkční verze

```nginx
server {
    listen 80;
    server_name nas.example.cz;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    http2 on;
    server_name nas.example.cz;

    ssl_certificate     /etc/letsencrypt/live/nas.example.cz/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/nas.example.cz/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

    location / {
        proxy_pass http://127.0.0.1:5000;

        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host  $host;
    }
}
```

Těch pět řádků s `proxy_set_header` je povinná výbava. Bez nich aplikace nevidí skutečného klienta ani neví, že běží pod HTTPS. Proč, je vysvětlené v [reverse proxy](Reverse-proxy).

Nginx je nenastavuje sám a to je nejčastější důvod, proč „aplikace za nginxem zlobí".

## Sdílený blok hlaviček

Ať to nepíšeš dvacetkrát, dej si to do souboru `/etc/nginx/snippets/proxy.conf`:

```nginx
proxy_set_header Host              $host;
proxy_set_header X-Real-IP         $remote_addr;
proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host  $host;

proxy_http_version 1.1;
proxy_set_header Upgrade    $http_upgrade;
proxy_set_header Connection $connection_upgrade;

proxy_read_timeout    300s;
proxy_send_timeout    300s;
proxy_connect_timeout 30s;
```

a v konfiguraci pak jen:

```nginx
location / {
    proxy_pass http://127.0.0.1:5000;
    include snippets/proxy.conf;
}
```

## WebSockety

Ty tři řádky s `Upgrade` výše jsou pro WebSockety, ale samy nestačí. V `nginx.conf` v bloku `http` musíš přidat:

```nginx
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}
```

Bez toho tě čeká `Connection: upgrade` i u běžných požadavků a některé aplikace se z toho zblázní.

Pozor taky na `proxy_read_timeout` — ve výchozím stavu je 60 sekund a nečinný WebSocket po nich tiše spadne. Souvislosti v [HTTP, QUIC a WebSocketu](HTTP-QUIC-a-WebSocket).

Tohle je řešení problému „Home Assistant se načte, ale nic se neaktualizuje".

## Lomítko v proxy_pass

Nejzáludnější detail v celém nginxu. Chování se liší podle toho, jestli je za portem lomítko:

```nginx
location /app/ {
    proxy_pass http://127.0.0.1:3000;      # BEZ lomítka
    # /app/foo  →  /app/foo
}

location /app/ {
    proxy_pass http://127.0.0.1:3000/;     # S lomítkem
    # /app/foo  →  /foo
}
```

Bez lomítka se cesta předá celá. S lomítkem se prefix z `location` odřízne.

Když ti aplikace pod podcestou vrací 404 na všechny statické soubory, je to skoro vždycky tohle. Většina aplikací navíc pod podcestou nefunguje vůbec, ať děláš co děláš — pokud to nemají výslovně podporované, dej jim vlastní subdoménu.

## Velké soubory

```nginx
client_max_body_size 10G;        # výchozí je 1 MB!
proxy_request_buffering off;     # neukládat celý upload na disk proxy
```

Výchozí megabajt je důvod, proč ti na Nextcloud nejde nahrát nic většího než dokument. Chyba se hlásí jako `413 Request Entity Too Large`.

## Kompletní příklad se vším

```nginx
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    listen [::]:80;
    server_name foto.example.cz;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name foto.example.cz;

    ssl_certificate     /etc/letsencrypt/live/foto.example.cz/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/foto.example.cz/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_session_cache shared:SSL:10m;

    add_header Strict-Transport-Security "max-age=31536000" always;
    add_header X-Content-Type-Options nosniff always;
    add_header X-Frame-Options SAMEORIGIN always;

    client_max_body_size 50G;
    access_log /var/log/nginx/foto.access.log;

    location / {
        proxy_pass http://127.0.0.1:2283;
        include snippets/proxy.conf;
    }
}
```

Poslouchání na `[::]:443` je pro [IPv6](Jak-funguje-IPv6). Zapomíná se to a pak se na tebe přes IPv6 nikdo nedostane, i když DNS má `AAAA`.

## Omezení rychlosti

```nginx
# v bloku http
limit_req_zone $binary_remote_addr zone=obecne:10m rate=10r/s;

# v bloku location
limit_req zone=obecne burst=20 nodelay;
```

Ochrana proti robotům, kteří ti perou do přihlašovacího formuláře. Doplněk k [fail2ban](Fail2ban-a-CrowdSec), ne náhrada.

## Skutečné IP za další proxy

Když je před nginxem ještě Cloudflare nebo jiná proxy, vidí nginx její adresu. Náprava:

```nginx
set_real_ip_from 103.21.244.0/22;    # rozsahy Cloudflare
real_ip_header CF-Connecting-IP;
real_ip_recursive on;
```

Bez toho ti [fail2ban](Fail2ban-a-CrowdSec) zablokuje Cloudflare a s ním všechny návštěvníky.

## Diagnostika

```bash
nginx -t                             # syntaxe
nginx -T                             # kompletní výsledná konfigurace včetně include
systemctl reload nginx
tail -f /var/log/nginx/error.log
```

`nginx -T` je nedoceněný. Ukáže ti složeninu všech souborů, takže hned vidíš, co se doopravdy načetlo — včetně toho, že máš dvě konfigurace pro stejnou doménu a použije se ta první.

| Chyba | Příčina |
|---|---|
| `502 Bad Gateway` | aplikace za proxy neběží nebo je na jiném portu |
| `504 Gateway Timeout` | aplikace odpovídá moc dlouho, zvyš `proxy_read_timeout` |
| `413 Request Entity Too Large` | `client_max_body_size` |
| `404` na statické soubory pod podcestou | lomítko v `proxy_pass` |
| smyčka přesměrování | chybí `X-Forwarded-Proto` |
