Repository navigation
Expand file tree
/
Copy pathnginx.conf.example
More file actions
164 lines (144 loc) · 6.67 KB
/
Copy pathnginx.conf.example
File metadata and controls
164 lines (144 loc) · 6.67 KB
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# ──────────────────────────────────────────────────────────────────────
# Music Library — nginx site config
#
# Drop this into /etc/nginx/sites-available/music-library.conf, symlink
# into sites-enabled, and reload nginx. Assumes the compose stack is
# running with:
#
# backend bound to 127.0.0.1:8080 (BACKEND_PORT in .env)
# frontend bound to 127.0.0.1:8081 (FRONTEND_PORT in .env)
#
# Replace music.example.com with your hostname — the server blocks only
# match requests for that name, so other nginx sites keep working.
#
# TLS: the :443 block below is the intended deployment. Terminate TLS here
# (certbot writes the two cert paths) and the :80 block only redirects.
# Session cookies are Secure by default, so COOKIE_SECURE=true is correct.
#
# X-Forwarded-Proto is set from $scheme rather than hardcoded. Hardcoding
# "https" on a plaintext listener made the backend believe every request
# arrived over TLS and mint https:// share, preview and cover URLs regardless.
# Note it is $scheme and not $http_x_forwarded_proto: forwarding the client's
# own header would let any caller choose the scheme of the links this server
# generates.
#
# If you really must run plaintext to the origin (e.g. Cloudflare Flexible
# SSL), understand that session cookies travel in the clear between the CDN
# and this host. Prefer Full (strict) with a real certificate; if you accept
# that trade-off anyway, delete the :443 block, drop the redirect from the :80
# block, set COOKIE_SECURE=false, and replace $scheme with a literal https in
# the two X-Forwarded-Proto headers below.
# ──────────────────────────────────────────────────────────────────────
upstream music_library_backend {
server 127.0.0.1:8080;
keepalive 32;
}
upstream music_library_frontend {
server 127.0.0.1:8081;
keepalive 32;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# :80 exists only to send browsers to TLS.
server {
listen 80;
listen [::]:80;
server_name music.example.com;
# Leave this reachable so certbot's HTTP-01 challenge keeps working.
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 308 https://$host$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name music.example.com;
ssl_certificate /etc/letsencrypt/live/music.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/music.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_stapling on;
ssl_stapling_verify on;
# Only enable HSTS once you are certain every hostname served here will
# keep working over TLS — browsers cache it for the full max-age.
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Audio uploads can be chunky — default 1m rejects multipart uploads.
client_max_body_size 512m;
# Behind Cloudflare: restore the visitor's address, or every per-address
# limit in the backend (login lockout, upload and cover rate limits) keys
# on a Cloudflare edge shared by many users. List every range from
# https://www.cloudflare.com/ips/, IPv4 and IPv6.
# set_real_ip_from 173.245.48.0/20;
# set_real_ip_from 2400:cb00::/32;
# real_ip_header CF-Connecting-IP;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# $scheme, never the client's own X-Forwarded-Proto and never a hardcoded
# literal: TLS terminates here, so $scheme is the authoritative answer, and
# the backend mints share/preview/cover URLs from this header. Passing the
# inbound header through would let any client choose the scheme in the
# links this server generates.
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection "";
proxy_read_timeout 1h; # long enough for a full album side in one request
proxy_send_timeout 1h;
# Same level and minimum size as frontend/nginx.conf; the type list is a
# superset of the one the frontend container compresses.
gzip on;
gzip_vary on;
gzip_comp_level 6;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript
application/xml+rss application/atom+xml image/svg+xml;
# Backend — API, streaming, cover art.
location = /api/activity/ws {
proxy_pass http://music_library_backend;
# proxy_set_header inheritance is all-or-nothing in nginx. Because
# this location overrides Upgrade/Connection, repeat the common
# forwarding headers as well or Host becomes the upstream name and
# the backend's WebSocket Origin check rejects the handshake.
proxy_set_header Host $host;
proxy_set_header X-Forwarded-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 Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 1h;
proxy_buffering off;
}
location /api/ {
proxy_pass http://music_library_backend;
# Range requests (audio scrubbing) don't benefit from nginx buffering.
proxy_buffering off;
proxy_request_buffering off;
}
# Public link-preview pages (what Discord / chat apps scrape).
location /share/ {
proxy_pass http://music_library_backend;
}
# Public embeddable players advertised in link-preview metadata.
location /embed/ {
proxy_pass http://music_library_backend;
}
# Everything else → SPA. The frontend container (frontend/nginx.conf)
# already sends Cache-Control: a year and immutable for the hashed
# /assets/*, no-cache for index.html and the SPA fallback. proxy_pass
# forwards those headers untouched, so none are added here: add_header
# would send a second Cache-Control and, since add_header inheritance is
# all-or-nothing, drop the HSTS header above from every SPA response.
location / {
proxy_pass http://music_library_frontend;
}
}