# nginx: статический сайт-визитка. Репозиторий клонирован в /opt/vizitka-site, раздаём его папку web/ server { listen 80 default_server; listen [::]:80 default_server; server_name _; root /opt/vizitka-site/web; index index.html; # сжатие gzip on; gzip_vary on; gzip_min_length 512; gzip_types text/css application/javascript application/json image/svg+xml text/plain application/xml; # заголовки безопасности (строгий CSP: на сайте нет inline-скриптов и стилей, внешних ресурсов нет) server_tokens off; add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always; location / { try_files $uri $uri/ =404; } # кэш: картинки и шрифты надолго, стили и скрипты на час, данные без кэша location ~* \.(woff2|webp|png|jpe?g|svg|ico)$ { expires 30d; add_header Cache-Control "public, max-age=2592000" always; # add_header в location сбрасывает наследуемые, поэтому дублируем безопасные заголовки add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'" always; add_header X-Content-Type-Options "nosniff" always; } location ~* \.(css|js)$ { add_header Cache-Control "public, max-age=3600" always; add_header X-Content-Type-Options "nosniff" always; } location = /config.json { add_header Cache-Control "no-cache" always; add_header X-Content-Type-Options "nosniff" always; } # скрытые файлы наружу не отдаём location ~ /\. { deny all; } error_page 404 /404.html; location = /404.html { internal; } }