# Two frontends on the same backend: # - Frontend 1: /opt/app/frontend/dist -> https://testforapp.website/ (build with base: '/') # - Frontend 2: /opt/app/frontend/test-dist -> https://testforapp.website/test/ (build with base: '/test/') upstream backend { server 127.0.0.1:8082; server 127.0.0.1:8080 backup; keepalive 500; } map $http_referer $is_phpmyadmin_request { ~*18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905 1; default 0; } # HTTPS server server { server_name testforapp.website; listen [::]:443 ssl ipv6only=on; # managed by Certbot listen 443 ssl; # managed by Certbot ssl_certificate /etc/letsencrypt/live/testforapp.website/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/testforapp.website/privkey.pem; # managed by Certbot include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; 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; add_header Permissions-Policy "geolocation=(), midi=(), sync-xhr=(), microphone=(), camera=(), magnetometer=(), gyroscope=(), fullscreen=(self), payment=()"; root /opt/app/frontend/dist; index index.html; # --- Frontend 2 at /test/ (from test-dist) --- location = /test { return 301 /test/; } location ^~ /test/ { alias /opt/app/frontend/test-dist/; index index.html; try_files $uri $uri/ /test/index.html; expires 0; add_header Cache-Control "no-store, no-cache, must-revalidate"; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header Content-Security-Policy "frame-ancestors 'self' https://web.telegram.org https://webk.telegram.org https://webz.telegram.org https://t.me telegram.org;" 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; add_header Permissions-Policy "geolocation=(), midi=(), sync-xhr=(), microphone=(), camera=(), magnetometer=(), gyroscope=(), fullscreen=(self), payment=()"; } # Admin Panel - root path (redirects to trailing slash) location = /dfab0676b6cb6b257370fb5743d8ddac42ab8153c2661072e8ef2717a10fcfaa { return 301 /dfab0676b6cb6b257370fb5743d8ddac42ab8153c2661072e8ef2717a10fcfaa/; } location ~ ^/dfab0676b6cb6b257370fb5743d8ddac42ab8153c2661072e8ef2717a10fcfaa/(assets/.+)$ { alias /opt/app/admin-panel/$1; expires 1y; add_header Cache-Control "public, immutable"; access_log off; } location /dfab0676b6cb6b257370fb5743d8ddac42ab8153c2661072e8ef2717a10fcfaa/ { alias /opt/app/admin-panel/; index index.html; try_files $uri $uri/ /dfab0676b6cb6b257370fb5743d8ddac42ab8153c2661072e8ef2717a10fcfaa/index.html; expires 0; add_header Cache-Control "no-store, no-cache, must-revalidate"; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; } location /18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905 { proxy_pass http://127.0.0.1:8081/; 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_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; gzip off; proxy_set_header Accept-Encoding ""; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; proxy_buffering on; proxy_buffer_size 8k; proxy_buffers 16 8k; proxy_busy_buffers_size 16k; proxy_cookie_path / /18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905/; sub_filter 'action="index.php?route=/' 'action="/18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905/index.php?route=/'; sub_filter 'action="index.php' 'action="/18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905/index.php'; sub_filter 'action="/index.php' 'action="/18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905/index.php'; sub_filter 'Location: /index.php' 'Location: /18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905/index.php'; sub_filter 'Location: index.php' 'Location: /18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905/index.php'; sub_filter 'href="index.php' 'href="/18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905/index.php'; sub_filter 'href="/index.php' 'href="/18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905/index.php'; sub_filter_once off; sub_filter_types text/html; } location ~ ^/18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905/(themes|js|favicon\.ico|libraries|templates|setup|config|tmp|index\.php) { rewrite ^/18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905/(.*)$ /$1 break; proxy_pass http://127.0.0.1:8081; 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_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; proxy_buffering off; } location ~ ^/(themes|js|favicon\.ico|libraries|templates|setup|config|tmp|index\.php) { if ($is_phpmyadmin_request = 0) { return 404; } proxy_pass http://127.0.0.1:8081; 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_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; proxy_buffering off; } location /videos/ { root /usr/share/nginx/html; add_header Cache-Control "public, max-age=31536000, immutable"; add_header Access-Control-Allow-Origin "*"; } location ^~ /images/ { root /usr/share/nginx/html; add_header Cache-Control "public, max-age=31536000, immutable"; add_header Access-Control-Allow-Origin "*"; autoindex off; } # Frontend 1: static assets at root (from dist) location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control "public, immutable"; access_log off; } # Frontend 1: SPA at / (from dist) location / { try_files $uri $uri/ /index.html; expires 0; add_header Cache-Control "no-store, no-cache, must-revalidate"; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header Content-Security-Policy "frame-ancestors 'self' https://web.telegram.org https://webk.telegram.org https://webz.telegram.org https://t.me telegram.org;" 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; add_header Permissions-Policy "geolocation=(), midi=(), sync-xhr=(), microphone=(), camera=(), magnetometer=(), gyroscope=(), fullscreen=(self), payment=()"; } location ^~ /avatars/ { alias /opt/app/data/avatars/; expires 24h; add_header Cache-Control "public, must-revalidate"; access_log off; location ~ \.(php|html)$ { deny all; } } location /api/telegram/webhook/ { access_log off; proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Connection ""; 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_read_timeout 30s; proxy_buffer_size 128k; proxy_buffers 4 256k; proxy_busy_buffers_size 256k; } location /api/ { limit_req zone=api_limit burst=200 nodelay; proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Connection ""; 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_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; } location = /phpmyadmin { return 404; } location /health { access_log off; return 200 "healthy\n"; add_header Content-Type text/plain; } location ~ /\. { deny all; access_log off; log_not_found off; } } server { if ($host = testforapp.website) { return 301 https://$host$request_uri; } listen 80; listen [::]:80; server_name testforapp.website; return 404; }