aboutsummaryrefslogtreecommitdiff
path: root/deploy/nginx-aendggner.conf
blob: 3a4ad448ca98e16ed3b62f6535493d972f0a193a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# SPDX-FileCopyrightText: 2020 Matthias Andreas Benkard <code@mail.matthias.benkard.de>
# SPDX-License-Identifier: AGPL-3.0-or-later

# Vorlage für die Auslieferung der ÄndGgner-Browserfassung unter
# https://matthias.benkard.de/aendggner/.
#
# Es gibt keinen Anwendungsprozess: Der Server liefert nur statische Dateien aus
# (Inhalt von target/web/), gerechnet wird im Browser. Weil der Dienst unter einem
# Unterpfad einer bestehenden Domain läuft, ist dies kein eigener server-Block,
# sondern ein Schnipsel zum Einfügen in den vorhandenen — Zertifikat, HTTP→HTTPS
# und Domainname bleiben, wie sie sind:
#
#     server { ... include /etc/nginx/snippets/aendggner.conf; ... }
#
# Ausgerollt wird nach /var/www/aendggner (siehe README.adoc, Abschnitt „Ausrollen“).

# Ohne diesen Schritt liefe /aendggner ins Leere, und alle relativen Verweise der
# Seite gingen von /  statt von /aendggner/ aus.
location = /aendggner {
    return 301 /aendggner/;
}

location /aendggner/ {
    # Bewusst „root /var/www“ (mit dem Verzeichnis /var/www/aendggner) statt
    # „alias /var/www/aendggner/“: In einer Präfix-Location löst try_files $uri
    # gegen den root auf, nicht gegen den alias, und liefert dann 404 für alles.
    root /var/www;
    index index.html;

    try_files $uri $uri/ =404;

    # Ohne den ersten Typ lehnt der Browser die Streaming-Instanziierung des Moduls
    # ab. Ein types-Block in einer location ersetzt die Zuordnung vollständig, statt
    # sie zu ergänzen — deshalb stehen hier alle vorkommenden Endungen.
    types {
        application/wasm wasm;
        text/html        html;
        text/css         css;
        text/javascript  js;
        image/svg+xml    svg;
        text/plain       txt;
        application/gzip gz;
    }
    default_type application/octet-stream;

    # Das Wasm-Modul ist rund 24 MB groß. deploy/webpaket.sh legt beim Bauen .gz
    # und .br daneben; ausgeliefert wird die fertige Datei, statt sie je Abruf neu
    # zu packen. gzip_static bringen die üblichen Distributionspakete mit;
    # brotli_static setzt das Modul ngx_brotli voraus — fehlt es, die Zeile
    # streichen, gzip_static genügt.
    gzip_static on;
    brotli_static on;

    # Kein „expires 7d“: Die Dateinamen tragen keine Fassungskennung. Ein Browser
    # mit altem app.js und neuem .wasm (oder umgekehrt) bekäme eine Mischfassung,
    # die es nie gegeben hat. Stattdessen Revalidierung bei jedem Abruf; für das
    # unveränderte Modul kostet das ein 304 ohne Rumpf.
    add_header Cache-Control "no-cache" always;

    # add_header in einer location verdrängt sämtliche vom server-Block geerbten
    # Header. Alles, was hier gelten soll, muss deshalb hier stehen — auch ein
    # dort gesetztes HSTS.
    # add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;

    # 'wasm-unsafe-eval' ist für die Instanziierung des Moduls nötig; echtes eval
    # oder new Function kommt in der erzeugten aendggner.js nicht vor.
    #
    # style-src braucht 'unsafe-inline': Die fertige Synopse wird als blob:-Dokument
    # geöffnet, und ein blob:-Dokument erbt die CSP des Dokuments, das es erzeugt
    # hat. Der HtmlRenderer bettet sein Stylesheet als <style> ein — ohne die
    # Freigabe erschiene die Synopse unformatiert. Skripte bleiben streng.
    add_header Content-Security-Policy "default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; worker-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; form-action 'none'; frame-ancestors 'none'; base-uri 'none'" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "no-referrer" always;
    add_header Permissions-Policy "geolocation=(), camera=(), microphone=(), usb=()" always;
}

# Eine robots.txt unter /aendggner/ wäre wirkungslos: Maßgeblich ist allein die im
# Wurzelverzeichnis der Domain. Wer die Seite von der Indizierung ausnehmen will,
# trage dort ein:
#
#     User-agent: *
#     Disallow: /aendggner/