mirror of
https://github.com/usetrmnl/inker.git
synced 2026-04-29 13:45:07 -07:00
224 lines
10 KiB
Nginx Configuration File
224 lines
10 KiB
Nginx Configuration File
# Preserve X-Forwarded-Proto from upstream proxy, fall back to $scheme
|
|
map $http_x_forwarded_proto $real_scheme {
|
|
default $http_x_forwarded_proto;
|
|
'' $scheme;
|
|
}
|
|
|
|
# Custom log format: use $uri (path only) instead of $request to prevent
|
|
# query string tokens (e.g., SSE ?token=...) from leaking into access logs
|
|
# (must be at http level, not inside server block)
|
|
log_format stripped '$remote_addr - $remote_user [$time_local] "$request_method $uri" $status $body_bytes_sent "$http_referer"';
|
|
|
|
server {
|
|
listen 80;
|
|
server_name localhost;
|
|
root /usr/share/nginx/html;
|
|
index index.html;
|
|
server_tokens off;
|
|
client_max_body_size 25m;
|
|
|
|
access_log /var/log/nginx/access.log stripped;
|
|
|
|
# Allow headers with underscores (e.g., HTTP_ID from TRMNL devices)
|
|
underscores_in_headers on;
|
|
|
|
# Gzip compression
|
|
gzip on;
|
|
gzip_vary on;
|
|
gzip_min_length 1024;
|
|
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json application/javascript font/woff2;
|
|
|
|
# Security headers (repeated in location blocks that use add_header,
|
|
# since nginx does not inherit server-level add_header into locations with their own)
|
|
add_header X-Frame-Options "DENY" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header X-XSS-Protection "1; mode=block" always;
|
|
add_header Referrer-Policy "no-referrer" always;
|
|
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
|
|
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://*.tile.openstreetmap.org; font-src 'self' data:; connect-src 'self' ws: wss: https://geocoding-api.open-meteo.com https://api.open-meteo.com https://nominatim.openstreetmap.org; frame-ancestors 'none';" always;
|
|
|
|
# Serve static files
|
|
location / {
|
|
try_files $uri $uri/ /index.html;
|
|
expires 1y;
|
|
add_header Cache-Control "public, immutable";
|
|
add_header X-Frame-Options "DENY" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header X-XSS-Protection "1; mode=block" always;
|
|
add_header Referrer-Policy "no-referrer" always;
|
|
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
|
|
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://*.tile.openstreetmap.org; font-src 'self' data:; connect-src 'self' ws: wss: https://geocoding-api.open-meteo.com https://api.open-meteo.com https://nominatim.openstreetmap.org; frame-ancestors 'none';" always;
|
|
}
|
|
|
|
# Cache control for HTML files
|
|
location ~* \.html$ {
|
|
expires -1;
|
|
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
|
add_header X-Frame-Options "DENY" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header X-XSS-Protection "1; mode=block" always;
|
|
add_header Referrer-Policy "no-referrer" always;
|
|
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
|
|
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://*.tile.openstreetmap.org; font-src 'self' data:; connect-src 'self' ws: wss: https://geocoding-api.open-meteo.com https://api.open-meteo.com https://nominatim.openstreetmap.org; frame-ancestors 'none';" always;
|
|
}
|
|
|
|
# Serve fonts with proper MIME types and CORS headers
|
|
location /fonts/ {
|
|
types {
|
|
font/woff2 woff2;
|
|
font/woff woff;
|
|
font/ttf ttf;
|
|
}
|
|
add_header Access-Control-Allow-Origin "*";
|
|
add_header Cache-Control "public, max-age=31536000, immutable";
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
expires 1y;
|
|
}
|
|
|
|
# Plugin and device-image render endpoints — strip security headers for ESP32 devices
|
|
location ~ ^/api/(plugins/instances/\d+/render|device-images/) {
|
|
proxy_pass http://127.0.0.1:3002;
|
|
proxy_http_version 1.1;
|
|
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 $real_scheme;
|
|
|
|
proxy_hide_header Content-Security-Policy;
|
|
proxy_hide_header Strict-Transport-Security;
|
|
proxy_hide_header X-Content-Type-Options;
|
|
proxy_hide_header X-DNS-Prefetch-Control;
|
|
proxy_hide_header X-Frame-Options;
|
|
proxy_hide_header X-XSS-Protection;
|
|
proxy_hide_header Cross-Origin-Opener-Policy;
|
|
proxy_hide_header Cross-Origin-Resource-Policy;
|
|
proxy_hide_header Origin-Agent-Cluster;
|
|
proxy_hide_header X-Download-Options;
|
|
proxy_hide_header X-Permitted-Cross-Domain-Policies;
|
|
proxy_hide_header Referrer-Policy;
|
|
proxy_hide_header Permissions-Policy;
|
|
|
|
add_header Access-Control-Allow-Origin "*";
|
|
add_header Cache-Control "no-store";
|
|
}
|
|
|
|
# Device API endpoints — strip security headers for ESP32 devices
|
|
# (ESP32 has ~8-16KB HTTP buffer; Helmet headers can overflow it)
|
|
location ~ ^/api/(setup|display|log)(/?)$ {
|
|
proxy_pass http://127.0.0.1:3002;
|
|
proxy_http_version 1.1;
|
|
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 $real_scheme;
|
|
|
|
proxy_hide_header Content-Security-Policy;
|
|
proxy_hide_header Strict-Transport-Security;
|
|
proxy_hide_header X-Content-Type-Options;
|
|
proxy_hide_header X-DNS-Prefetch-Control;
|
|
proxy_hide_header X-Frame-Options;
|
|
proxy_hide_header X-XSS-Protection;
|
|
proxy_hide_header Cross-Origin-Opener-Policy;
|
|
proxy_hide_header Cross-Origin-Resource-Policy;
|
|
proxy_hide_header Origin-Agent-Cluster;
|
|
proxy_hide_header X-Download-Options;
|
|
proxy_hide_header X-Permitted-Cross-Domain-Policies;
|
|
proxy_hide_header Referrer-Policy;
|
|
proxy_hide_header Permissions-Policy;
|
|
|
|
add_header Access-Control-Allow-Origin "*";
|
|
add_header Cache-Control "no-store";
|
|
}
|
|
|
|
# Proxy API requests to backend
|
|
location /api/ {
|
|
proxy_pass http://127.0.0.1:3002/api/;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection 'upgrade';
|
|
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 $real_scheme;
|
|
proxy_cache_bypass $http_upgrade;
|
|
}
|
|
|
|
# Serve frontend build assets (JS, CSS) as static files,
|
|
# proxy backend assets (default-screen.png, setup.png) to backend
|
|
location /assets/ {
|
|
try_files $uri @backend_assets;
|
|
expires 1y;
|
|
add_header Cache-Control "public, immutable";
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header Referrer-Policy "no-referrer" always;
|
|
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
|
|
}
|
|
|
|
location @backend_assets {
|
|
proxy_pass http://127.0.0.1:3002;
|
|
proxy_http_version 1.1;
|
|
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 $real_scheme;
|
|
|
|
# Strip Helmet security headers from image responses — ESP32 devices
|
|
# have limited HTTP buffer and excessive headers cause download failures
|
|
proxy_hide_header Content-Security-Policy;
|
|
proxy_hide_header Strict-Transport-Security;
|
|
proxy_hide_header X-Content-Type-Options;
|
|
proxy_hide_header X-DNS-Prefetch-Control;
|
|
proxy_hide_header X-Frame-Options;
|
|
proxy_hide_header X-XSS-Protection;
|
|
proxy_hide_header Cross-Origin-Opener-Policy;
|
|
proxy_hide_header Cross-Origin-Resource-Policy;
|
|
proxy_hide_header Origin-Agent-Cluster;
|
|
proxy_hide_header X-Download-Options;
|
|
proxy_hide_header X-Permitted-Cross-Domain-Policies;
|
|
proxy_hide_header Referrer-Policy;
|
|
proxy_hide_header Permissions-Policy;
|
|
|
|
# Override server-level security headers (add_header in a location
|
|
# block prevents inheriting server-level add_header directives)
|
|
add_header Access-Control-Allow-Origin "*";
|
|
add_header Cache-Control "public, max-age=0";
|
|
}
|
|
|
|
# Proxy uploads to backend (images, captures, drawings)
|
|
location /uploads/ {
|
|
proxy_pass http://127.0.0.1:3002/uploads/;
|
|
proxy_http_version 1.1;
|
|
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 $real_scheme;
|
|
expires 7d;
|
|
|
|
# Strip Helmet security headers — ESP32 devices have limited HTTP buffer
|
|
proxy_hide_header Content-Security-Policy;
|
|
proxy_hide_header Strict-Transport-Security;
|
|
proxy_hide_header X-Content-Type-Options;
|
|
proxy_hide_header X-DNS-Prefetch-Control;
|
|
proxy_hide_header X-Frame-Options;
|
|
proxy_hide_header X-XSS-Protection;
|
|
proxy_hide_header Cross-Origin-Opener-Policy;
|
|
proxy_hide_header Cross-Origin-Resource-Policy;
|
|
proxy_hide_header Origin-Agent-Cluster;
|
|
proxy_hide_header X-Download-Options;
|
|
proxy_hide_header X-Permitted-Cross-Domain-Policies;
|
|
proxy_hide_header Referrer-Policy;
|
|
proxy_hide_header Permissions-Policy;
|
|
|
|
# Minimal headers only (overrides server-level security headers)
|
|
add_header Access-Control-Allow-Origin "*";
|
|
add_header Cache-Control "public, max-age=604800";
|
|
}
|
|
|
|
# Health check endpoint
|
|
location /health {
|
|
access_log off;
|
|
return 200 "healthy\n";
|
|
add_header Content-Type text/plain;
|
|
}
|
|
}
|