Files
inker/docker/nginx.conf
T
wojo aebe1868a2 Inker v0.1.0 - Self-hosted e-ink device management system
Full-stack application for managing TRMNL e-ink displays with
screen designer, playlist management, custom widgets, and OTA updates.
2026-03-01 17:47:30 +00:00

127 lines
5.4 KiB
Nginx Configuration File

# 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'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self' ws: wss:; 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'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self' ws: wss:; 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'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self' ws: wss:; 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;
}
# 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 $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_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 $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 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 $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;
expires 7d;
add_header Cache-Control "public, max-age=604800";
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
}
# Health check endpoint
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}