user www-data;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types; # Required for SVG mime type
default_type application/octet-stream; # Required for SVG mime type
client_max_body_size 100M;
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1; # Required for WebSocket
proxy_set_header Upgrade $http_upgrade; # WebSocket header
proxy_set_header Connection "upgrade"; # WebSocket header
# Preserve browser host:port (e.g. localhost:5000). $host strips the port
# and breaks Next.js redirects / absolute URLs.
proxy_set_header Host $http_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 X-Forwarded-Host $http_host;
proxy_read_timeout 30m;
proxy_connect_timeout 30m;
}
location /api/v1/ {
proxy_pass http://localhost:8000;
proxy_read_timeout 30m;
proxy_send_timeout 30m;
proxy_connect_timeout 30m;
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;
}
# MCP
location /mcp/ {
proxy_pass http://localhost:8001/mcp/;
proxy_read_timeout 30m;
proxy_connect_timeout 30m;
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;
}
location /mcp {
proxy_pass http://localhost:8001/mcp;
proxy_read_timeout 30m;
proxy_connect_timeout 30m;
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;
}
location /docs {
proxy_pass http://localhost:8000/docs;
proxy_read_timeout 30m;
proxy_connect_timeout 30m;
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;
}
location /openapi.json {
proxy_pass http://localhost:8000/openapi.json;
proxy_read_timeout 30m;
proxy_connect_timeout 30m;
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;
}
# Internal auth subrequest used to gate /app_data/* static access behind the
# FastAPI session cookie. Nginx serves these files directly via `alias` for
# performance, so auth_request is what keeps them from being public.
location = /_auth_check {
internal;
proxy_pass http://localhost:8000/api/v1/auth/verify;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
proxy_set_header Host $host;
proxy_set_header Cookie $http_cookie;
proxy_set_header Authorization $http_authorization;
}
# Bundled UI static assets (logos, fonts packaged with the app) are safe to
# serve unauthenticated; they contain no user data.
location /static {
alias /app/servers/fastapi/static/;
expires 1y;
add_header Cache-Control "public, immutable";
}
location /app_data/images/ {
alias /app_data/images/;
expires 1y;
add_header Cache-Control "private, max-age=31536000";
}
location /app_data/exports/ {
auth_request /_auth_check;
alias /app_data/exports/;
expires 1y;
add_header Cache-Control "private, max-age=31536000";
}
location /app_data/uploads/ {
auth_request /_auth_check;
alias /app_data/uploads/;
expires 1y;
add_header Cache-Control "private, max-age=31536000";
}
location /app_data/fonts/ {
auth_request /_auth_check;
alias /app_data/fonts/;
expires 1y;
add_header Cache-Control "private, max-age=31536000";
}
location /app_data/pptx-to-html/ {
auth_request /_auth_check;
alias /app_data/pptx-to-html/;
expires 1y;
add_header Cache-Control "private, max-age=31536000";
}
}
}