server { listen 80; server_name localhost; # ---------------------------------------- # 보안 헤더 # ---------------------------------------- add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; # 화상회의(LiveKit)용 카메라/마이크는 자기 출처(self)에 허용. 위치정보는 차단 유지. add_header Permissions-Policy "geolocation=(), microphone=(self), camera=(self)" always; # ---------------------------------------- # 압축 (Gzip) # ---------------------------------------- gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css text/javascript application/javascript application/json application/xml image/svg+xml; # ---------------------------------------- # SPA 라우팅 # ---------------------------------------- location / { root /usr/share/nginx/html; index index.html index.htm; try_files $uri $uri/ /index.html; # 정적 자원 캐싱 (SPA 빌드 산출물은 해시 파일명이라 장기 캐싱 가능) location ~* \.(?:js|css|woff2?|ttf|svg|png|jpg|jpeg|gif|ico)$ { expires 30d; add_header Cache-Control "public, immutable"; } } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } # 숨김 파일 접근 차단 location ~ /\. { deny all; access_log off; log_not_found off; } }