mirror of https://gitlab.com/tildes/tildes.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
110 lines
3.6 KiB
110 lines
3.6 KiB
upstream app_server {
|
|
server unix:/run/gunicorn/socket fail_timeout=0;
|
|
}
|
|
|
|
# define map to set Expires+Cache-Control headers for files based on type
|
|
map $sent_http_content_type $expires_type_map {
|
|
default off;
|
|
text/css max;
|
|
application/javascript max;
|
|
image/x-icon 1d;
|
|
~image/ max;
|
|
}
|
|
|
|
server {
|
|
# block bots that don't obey robots.txt
|
|
if ($http_user_agent ~* (SemrushBot)) {
|
|
return 403;
|
|
}
|
|
|
|
# remove trailing slash from addresses, the $port thing is a hack for
|
|
# development in Vagrant, so the port forwarding from the host is kept
|
|
set $port '';
|
|
if ($http_host ~ :(\d+)$) {
|
|
set $port :$1;
|
|
}
|
|
rewrite ^/(.*)/$ https://$host$port/$1 permanent;
|
|
|
|
# redirect /donate to the page on the Docs site
|
|
rewrite ^/donate$ https://docs.tildes.net/donate redirect;
|
|
|
|
# redirect /u/username to /user/username
|
|
rewrite ^/u/(.*)$ https://$host$port/user/$1;
|
|
|
|
listen 443 ssl http2;
|
|
listen [::]:443 ssl http2;
|
|
|
|
{% if nginx_enable_hsts %}
|
|
add_header Strict-Transport-Security "max-age={{ hsts_max_age }}; includeSubDomains; preload" always;
|
|
{% endif %}
|
|
|
|
{% if nginx_enable_csp %}
|
|
# Content Security Policy:
|
|
# - "img-src data:" is needed for Spectre.css icons
|
|
# - "https://js.stripe.com" in script-src and frame-src is needed for Stripe
|
|
add_header Content-Security-Policy "default-src 'none'; script-src 'self' https://js.stripe.com; style-src 'self'; img-src 'self' data:; connect-src 'self'; manifest-src 'self'; frame-src 'self' https://js.stripe.com; form-action 'self'; frame-ancestors 'none'; base-uri 'none'" always;
|
|
{% endif %}
|
|
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header X-Frame-Options "DENY" always;
|
|
add_header X-Xss-Protection "1; mode=block" always;
|
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
|
|
|
server_name {{ site_hostname }};
|
|
|
|
keepalive_timeout 5;
|
|
|
|
root {{ app_dir }}/static;
|
|
|
|
# Block access to /metrics except from Prometheus server(s)
|
|
location /metrics {
|
|
{% for ip in prometheus_ips -%}
|
|
allow {{ ip }};
|
|
{% endfor -%}
|
|
deny all;
|
|
|
|
# try_files is unnecessary here, but I don't know the "proper" way
|
|
try_files $uri @proxy_to_app;
|
|
}
|
|
|
|
# add Expires+Cache-Control headers from the mime-type map defined above
|
|
expires $expires_type_map;
|
|
|
|
location / {
|
|
# checks for static file, if not found proxy to app
|
|
try_files $uri @proxy_to_app;
|
|
gzip_static on;
|
|
}
|
|
|
|
location @proxy_to_app {
|
|
{% if nginx_enable_ratelimiting %}
|
|
# apply rate-limiting, allowing a burst above the limit
|
|
limit_req zone=tildes_app burst=5 nodelay;
|
|
{% endif -%}
|
|
|
|
# Cornice adds the X-Content-Type-Options header, so it will end up
|
|
# being duplicated since nginx is also configured to send it (above).
|
|
# It's better to drop the header coming from Gunicorn here than to
|
|
# stop sending it in nginx, since if I ever stop using Cornice I would
|
|
# lose that header (and probably not realize).
|
|
proxy_hide_header X-Content-Type-Options;
|
|
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_redirect off;
|
|
proxy_pass http://app_server;
|
|
}
|
|
}
|
|
|
|
{% if nginx_redirect_www %}
|
|
# redirect www. to base domain
|
|
server {
|
|
listen 80;
|
|
listen 443 ssl http2;
|
|
listen [::]:443 ssl http2;
|
|
|
|
server_name www.{{ site_hostname }};
|
|
return 301 https://{{ site_hostname }}$request_uri;
|
|
}
|
|
{% endif %}
|