blumeops/fly/nginx.conf
Erich Blume d8960266b1 Serve friendly error page when Fly.io proxy upstreams are unreachable
When indri is offline, the Tailscale tunnel is down, or the emergency
shutoff was triggered, visitors now see a branded 503 page instead of
nginx's default 502. Stale cache is still served when available
(proxy_cache_use_stale takes priority). Test endpoint at /_error.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 11:58:50 -08:00

106 lines
3.3 KiB
Nginx Configuration File

worker_processes auto;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# JSON access log for Alloy to tail → Loki + metric extraction
log_format json_log escape=json
'{'
'"time":"$time_iso8601",'
'"remote_addr":"$remote_addr",'
'"client_ip":"$http_fly_client_ip",'
'"request_method":"$request_method",'
'"request_uri":"$request_uri",'
'"status":$status,'
'"body_bytes_sent":$body_bytes_sent,'
'"request_time":$request_time,'
'"upstream_response_time":"$upstream_response_time",'
'"upstream_cache_status":"$upstream_cache_status",'
'"http_host":"$http_host",'
'"http_user_agent":"$http_user_agent"'
'}';
access_log /var/log/nginx/access.json.log json_log;
# Rate limiting zones — define per-service zones as needed
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
# Proxy cache: 200MB, evict after 24h of no access
proxy_cache_path /tmp/cache levels=1:2 keys_zone=services:10m
max_size=200m inactive=24h;
# MagicDNS resolver — using a variable in proxy_pass defers upstream DNS
# resolution to request time (not config time). Results are cached for
# 30s per worker to avoid per-request DNS lookups.
resolver 100.100.100.100 valid=30s;
resolver_timeout 5s;
# --- docs.eblu.me (static site) ---
server {
listen 8080;
server_name docs.eblu.me;
limit_req zone=general burst=20 nodelay;
# Serve a friendly error page when upstreams are unreachable
# (indri offline, Tailscale tunnel down, emergency shutoff, etc.)
# proxy_cache_use_stale still takes priority when cached content exists.
error_page 502 503 504 /error.html;
location = /error.html {
root /usr/share/nginx/html;
internal;
}
location = /_error {
root /usr/share/nginx/html;
try_files /error.html =404;
}
location / {
set $upstream_docs https://docs.tail8d86e.ts.net;
proxy_pass $upstream_docs$request_uri;
proxy_ssl_verify off;
proxy_ssl_server_name on;
proxy_intercept_errors on;
# Cache aggressively — static site only.
# Do NOT use these settings for dynamic services.
proxy_cache services;
proxy_cache_valid 200 1d;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout updating;
proxy_cache_lock on;
# Prevent cache-busting: ignore query strings and
# client cache-control headers.
# Safe for static sites; breaks dynamic services.
proxy_cache_key $host$uri;
proxy_ignore_headers Cache-Control Set-Cookie;
add_header X-Cache-Status $upstream_cache_status;
}
}
# Catch-all: reject unknown hosts, but serve health check
server {
listen 8080 default_server;
location /healthz {
return 200 "ok\n";
}
location /stub_status {
stub_status;
allow 127.0.0.1;
deny all;
}
location / {
return 444;
}
}
}