mirror of
https://github.com/kennethreitz/kjvstudy.org.git
synced 2026-06-05 06:46:13 +00:00
2ab3dfa142
Pre-render ~1,277 high-traffic HTML pages (homepage, books, chapters) at build time and serve them directly via nginx. All other routes (verses, search, API, PDFs, Strong's) fall through to a FastAPI sidecar. If the sidecar crashes, nginx continues serving static pages and health checks. Also harden the FastAPI app against the memory/crash issues: - Switch from bare uvicorn to gunicorn with uvicorn workers - Add --max-requests worker recycling to prevent memory leaks - Add --timeout to kill hung workers - Add per-IP rate limiting middleware (10 req/s, burst of 50) - Add request timeout middleware (30s max per request) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
185 lines
6.1 KiB
Nginx Configuration File
185 lines
6.1 KiB
Nginx Configuration File
worker_processes auto;
|
|
error_log /var/log/nginx/error.log warn;
|
|
pid /var/run/nginx.pid;
|
|
|
|
events {
|
|
worker_connections 1024;
|
|
}
|
|
|
|
http {
|
|
include /etc/nginx/mime.types;
|
|
default_type application/octet-stream;
|
|
|
|
log_format main '$remote_addr - [$time_local] "$request" $status $body_bytes_sent "$http_user_agent"';
|
|
access_log /var/log/nginx/access.log main;
|
|
|
|
sendfile on;
|
|
keepalive_timeout 65;
|
|
|
|
# Gzip
|
|
gzip on;
|
|
gzip_vary on;
|
|
gzip_proxied any;
|
|
gzip_comp_level 6;
|
|
gzip_min_length 500;
|
|
gzip_types text/plain text/css text/xml text/javascript
|
|
application/json application/javascript application/xml
|
|
application/rss+xml image/svg+xml;
|
|
|
|
# Upstream: FastAPI sidecar for dynamic routes
|
|
upstream sidecar {
|
|
server 127.0.0.1:8001;
|
|
}
|
|
|
|
server {
|
|
listen 8000;
|
|
server_name _;
|
|
root /app/dist;
|
|
|
|
# Security headers
|
|
add_header X-Content-Type-Options nosniff always;
|
|
add_header X-Frame-Options SAMEORIGIN always;
|
|
|
|
# -----------------------------------------------------------
|
|
# Dynamic routes — proxy to FastAPI sidecar
|
|
# -----------------------------------------------------------
|
|
|
|
# Search (dynamic query results)
|
|
location = /search {
|
|
proxy_pass http://sidecar;
|
|
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;
|
|
}
|
|
|
|
# All API endpoints
|
|
location /api/ {
|
|
proxy_pass http://sidecar;
|
|
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;
|
|
}
|
|
|
|
# PDF generation (on-demand)
|
|
location ~ /pdf$ {
|
|
proxy_pass http://sidecar;
|
|
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_read_timeout 30s;
|
|
}
|
|
|
|
# Verse of the day redirect (needs server-side date logic)
|
|
location = /verse-of-the-day {
|
|
proxy_pass http://sidecar;
|
|
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;
|
|
}
|
|
|
|
# OG images (dynamically generated)
|
|
location /og/ {
|
|
proxy_pass http://sidecar;
|
|
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;
|
|
}
|
|
|
|
# Family tree search (dynamic query)
|
|
location = /family-tree/search {
|
|
proxy_pass http://sidecar;
|
|
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;
|
|
}
|
|
|
|
# Family tree SVG (dynamically rendered)
|
|
location = /family-tree/lineage.svg {
|
|
proxy_pass http://sidecar;
|
|
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;
|
|
}
|
|
|
|
# OpenAPI docs
|
|
location /api/docs {
|
|
proxy_pass http://sidecar;
|
|
proxy_set_header Host $host;
|
|
}
|
|
location /api/redoc {
|
|
proxy_pass http://sidecar;
|
|
proxy_set_header Host $host;
|
|
}
|
|
location /api/openapi.json {
|
|
proxy_pass http://sidecar;
|
|
proxy_set_header Host $host;
|
|
}
|
|
|
|
# -----------------------------------------------------------
|
|
# Health check — static (no sidecar dependency)
|
|
# -----------------------------------------------------------
|
|
location = /health {
|
|
default_type application/json;
|
|
return 200 '{"status":"healthy","service":"kjv-study"}';
|
|
}
|
|
|
|
# -----------------------------------------------------------
|
|
# Static assets — aggressive caching
|
|
# -----------------------------------------------------------
|
|
location /static/ {
|
|
expires 1y;
|
|
add_header Cache-Control "public, immutable";
|
|
try_files $uri =404;
|
|
}
|
|
|
|
# -----------------------------------------------------------
|
|
# Robots / sitemaps
|
|
# -----------------------------------------------------------
|
|
location = /robots.txt {
|
|
default_type text/plain;
|
|
expires 1d;
|
|
}
|
|
location ~ ^/sitemap.*\.xml$ {
|
|
default_type application/xml;
|
|
expires 1d;
|
|
}
|
|
|
|
# Random verse list JSON
|
|
location = /random-verse-list.json {
|
|
default_type application/json;
|
|
expires 7d;
|
|
}
|
|
|
|
# -----------------------------------------------------------
|
|
# Default — serve pre-rendered HTML with clean URLs
|
|
# -----------------------------------------------------------
|
|
location / {
|
|
try_files $uri $uri/index.html $uri/ @sidecar;
|
|
expires 7d;
|
|
add_header Cache-Control "public";
|
|
}
|
|
|
|
# Fallback: if no static file exists, proxy to sidecar
|
|
location @sidecar {
|
|
proxy_pass http://sidecar;
|
|
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;
|
|
}
|
|
|
|
# Custom 404
|
|
error_page 404 /404.html;
|
|
location = /404.html {
|
|
internal;
|
|
}
|
|
}
|
|
}
|