morphit/ops/nginx/relay.conf

130 lines
5 KiB
Text
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ============================================================================
# OPTIONAL — ADVANCED "split-subdomain" TOPOLOGY ONLY.
#
# The RECOMMENDED deployment is single-host (everything under one hostname);
# it does NOT use this file. The frontend already proxies /relay/ to the
# loopback relay via ops/nginx/web.conf — see docs/RUN-A-MORPHIT-NODE.md §9.
#
# Use this file ONLY if you deliberately run the relay on its own subdomain
# (e.g. relay.example.com). That requires a separate DNS record + TLS cert
# for it, AND rebuilding the frontend with MORPHIT_RELAY_ORIGIN set to that
# absolute URL (apps/web/src/lib/net/config.ts) — otherwise the same-origin
# default frontend keeps calling /relay/ on its OWN host and never reaches
# this subdomain. If you're not sure, you want the single-host setup —
# delete any relay.* DNS record and use ops/nginx/web.conf instead.
# ============================================================================
# Morphit relay — nginx vhost.
#
# Drop into /etc/nginx/sites-available/relay.morphit.io.conf, symlink
# into sites-enabled/, test with `sudo nginx -t`, then reload. TLS is
# assumed to come from Let's Encrypt / certbot; adjust ssl_certificate
# paths for your setup.
#
# The relay listens on 127.0.0.1:8080 (see relay.env.example). This
# vhost does TLS termination, sets defensive headers, enforces body-size
# limits, and reverse-proxies. The relay's own middleware stack also
# sets security headers; having both is defence-in-depth.
# HTTP -> HTTPS redirect.
server {
listen 80;
listen [::]:80;
server_name relay.morphit.io;
# ACME http-01 challenge stays on port 80.
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS vhost.
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name relay.morphit.io;
ssl_certificate /etc/letsencrypt/live/relay.morphit.io/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/relay.morphit.io/privkey.pem;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# HSTS — 1 year, include subdomains, mark for preload.
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Baseline security headers. The relay sets its own; these are a
# belt-and-braces layer for edge conditions.
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer" always;
add_header X-Frame-Options "DENY" always;
add_header Permissions-Policy "interest-cohort=()" always;
# The relay is JSON-only — no HTML, no scripts, no styles.
# `default-src 'none'` makes sure that any accidental HTML
# response (e.g. a misconfigured error page) can't load
# anything at all.
add_header Content-Security-Policy "default-src 'none'; frame-ancestors 'none'; base-uri 'none'" always;
# Hide the nginx version from response headers.
server_tokens off;
# Matches relay's own MaxRequestBodyBytes (64 KiB).
client_max_body_size 64k;
# No access log by default (privacy commitment; re-enable for abuse
# response on operator discretion — log to journald not a persistent
# file).
access_log off;
error_log /var/log/nginx/relay.morphit.io.error.log warn;
# Connection-level timeouts. Slightly tighter than the relay's
# so nginx sheds misbehaving clients before the relay wakes up.
client_body_timeout 10s;
client_header_timeout 10s;
send_timeout 15s;
keepalive_timeout 30s;
# ─── Compression (cp165 byte budget) ──────────────────────
# JSON responses compress ~4-8×. Helps both the frontend
# and third-party API consumers (signup status pollers, etc.)
# over slow mobile connections.
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_min_length 256;
gzip_types
application/json
application/javascript
text/plain
text/xml;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Request-Start $msec;
# Don't forward connection-related headers to the upstream.
proxy_set_header Connection "";
proxy_connect_timeout 5s;
proxy_read_timeout 15s;
proxy_send_timeout 15s;
}
# Deny anything that isn't under /v1/. Relay itself 404s these too,
# but rejecting at the edge saves the upstream a hop.
location ~ ^/(?!v1/) {
return 404;
}
}