
Since merge_slashes gets rid of the redundant slashes, a no-op rewrite will get rid of all of them at once instead of requiring one redirect to get rid of each redundant slash.
469 lines
15 KiB
Nginx Configuration File
469 lines
15 KiB
Nginx Configuration File
# nginx 1.20.x
|
||
|
||
load_module modules/ngx_http_brotli_static_module.so;
|
||
|
||
worker_processes auto;
|
||
worker_rlimit_nofile 16384;
|
||
|
||
events {
|
||
worker_connections 4096;
|
||
}
|
||
|
||
http {
|
||
include mime.types;
|
||
default_type application/octet-stream;
|
||
|
||
charset utf-8;
|
||
charset_types text/css text/plain text/xml application/atom+xml application/javascript;
|
||
|
||
sendfile on;
|
||
sendfile_max_chunk 512k;
|
||
tcp_nopush on;
|
||
keepalive_timeout 3m;
|
||
server_tokens off;
|
||
msie_padding off;
|
||
|
||
client_max_body_size 1k;
|
||
client_body_buffer_size 1k;
|
||
client_header_buffer_size 1k;
|
||
large_client_header_buffers 4 4k;
|
||
http2_recv_buffer_size 128k;
|
||
|
||
client_body_timeout 30s;
|
||
client_header_timeout 30s;
|
||
send_timeout 30s;
|
||
|
||
http2_max_concurrent_streams 32;
|
||
limit_conn_status 429;
|
||
limit_conn_zone $binary_remote_addr zone=addr:10m;
|
||
limit_conn addr 256;
|
||
|
||
ssl_protocols TLSv1.2 TLSv1.3;
|
||
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256;
|
||
ssl_prefer_server_ciphers on;
|
||
ssl_conf_command Options PrioritizeChaCha;
|
||
|
||
ssl_certificate /etc/letsencrypt/live/grapheneos.org/fullchain.pem;
|
||
ssl_certificate_key /etc/letsencrypt/live/grapheneos.org/privkey.pem;
|
||
|
||
ssl_session_cache shared:SSL:10m;
|
||
ssl_session_timeout 1d;
|
||
# maintained by nginx-rotate-session-ticket-keys in ramfs
|
||
ssl_session_ticket_key session-ticket-keys/4.key;
|
||
ssl_session_ticket_key session-ticket-keys/3.key;
|
||
ssl_session_ticket_key session-ticket-keys/2.key;
|
||
ssl_session_ticket_key session-ticket-keys/1.key;
|
||
ssl_buffer_size 4k;
|
||
|
||
ssl_trusted_certificate /etc/letsencrypt/live/grapheneos.org/chain.pem;
|
||
ssl_stapling on;
|
||
ssl_stapling_verify on;
|
||
# maintained by certbot-ocsp-fetcher
|
||
ssl_stapling_file ocsp-cache/grapheneos.org.der;
|
||
|
||
log_format main '$remote_addr - $remote_user [$time_local] '
|
||
'"$request_method $scheme://$host$request_uri $server_protocol" $status $body_bytes_sent '
|
||
'"$http_referer" "$http_user_agent"';
|
||
access_log /var/log/nginx/access.log main buffer=64k flush=1m;
|
||
error_log syslog:server=unix:/dev/log,nohostname;
|
||
log_not_found off;
|
||
|
||
gzip_proxied any;
|
||
gzip_vary on;
|
||
|
||
if_modified_since before;
|
||
|
||
map $uri $preload_resources_uri {
|
||
/index.html ", </phone.png>; rel=preload; as=image";
|
||
}
|
||
|
||
map $http_cookie $clear_legacy_push_cookie {
|
||
~__Host-preload= "__Host-preload=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0";
|
||
}
|
||
|
||
map $http_cookie $nopush {
|
||
~__Host-push=1 1;
|
||
default 0;
|
||
}
|
||
|
||
map $nopush $push_cookie {
|
||
0 "__Host-push=1; HttpOnly; Secure; SameSite=Lax; Path=/";
|
||
}
|
||
|
||
map $nopush $push_stylesheet {
|
||
0 "{{path|/main.css}}";
|
||
}
|
||
|
||
map $nopush $push_font_regular {
|
||
0 /fonts/roboto-v29-regular-latin.woff2;
|
||
}
|
||
|
||
map $nopush $push_font_bold {
|
||
0 /fonts/roboto-v29-bold-latin.woff2;
|
||
}
|
||
|
||
map $nopush $push_mask_icon {
|
||
0 "{{path|/mask-icon.svg}}";
|
||
}
|
||
|
||
map $nopush $push_phone {
|
||
0 /phone.png;
|
||
}
|
||
|
||
server {
|
||
listen 80 backlog=4096;
|
||
listen [::]:80 backlog=4096;
|
||
server_name grapheneos.org www.grapheneos.org grapheneos.app www.grapheneos.app grapheneos.ca www.grapheneos.ca grapheneos.com www.grapheneos.com grapheneos.info www.grapheneos.info grapheneos.net www.grapheneos.net grapheneos.ovh www.grapheneos.ovh grapheneos.page www.grapheneos.page vanadium.app www.vanadium.app;
|
||
|
||
root /var/empty;
|
||
|
||
# use $host to set up HSTS for redirect domains
|
||
return 301 https://$host$request_uri;
|
||
}
|
||
|
||
server {
|
||
listen 443 ssl http2;
|
||
listen [::]:443 ssl http2;
|
||
server_name 0.grapheneos.org;
|
||
|
||
root /var/empty;
|
||
|
||
location /.well-known/acme-challenge/ {
|
||
root /srv/certbot;
|
||
}
|
||
|
||
location / {
|
||
return 301 https://grapheneos.org$request_uri;
|
||
}
|
||
}
|
||
|
||
server {
|
||
listen 443 ssl http2 backlog=4096;
|
||
listen [::]:443 ssl http2 backlog=4096;
|
||
server_name www.grapheneos.org grapheneos.app www.grapheneos.app grapheneos.ca www.grapheneos.ca grapheneos.com www.grapheneos.com grapheneos.info www.grapheneos.info grapheneos.net www.grapheneos.net grapheneos.ovh www.grapheneos.ovh grapheneos.page www.grapheneos.page;
|
||
|
||
root /var/empty;
|
||
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
|
||
location ^~ /.well-known/acme-challenge/ {
|
||
return 301 https://0.grapheneos.org$request_uri;
|
||
}
|
||
|
||
location / {
|
||
return 301 https://grapheneos.org$request_uri;
|
||
}
|
||
}
|
||
|
||
server {
|
||
listen 443 ssl http2;
|
||
listen [::]:443 ssl http2;
|
||
server_name vanadium.app www.vanadium.app;
|
||
|
||
root /var/empty;
|
||
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
|
||
location ^~ /.well-known/acme-challenge/ {
|
||
return 301 https://0.grapheneos.org$request_uri;
|
||
}
|
||
|
||
location / {
|
||
return 302 https://github.com/GrapheneOS/Vanadium;
|
||
}
|
||
}
|
||
|
||
server {
|
||
listen 443 ssl http2;
|
||
listen [::]:443 ssl http2;
|
||
server_name grapheneos.org;
|
||
|
||
root /srv/grapheneos.org;
|
||
error_page 403 =404 /404;
|
||
error_page 404 /404;
|
||
|
||
open_file_cache max=2048 inactive=1d;
|
||
open_file_cache_valid 1d;
|
||
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
gzip_static on;
|
||
brotli_static on;
|
||
|
||
if ($request_uri ~ //) {
|
||
rewrite ^(.*)$ $1 permanent;
|
||
}
|
||
|
||
location = /, {
|
||
return 301 /;
|
||
}
|
||
|
||
location = /security.txt {
|
||
return 301 /.well-known/security.txt;
|
||
}
|
||
|
||
location = /bitcoin-address.png {
|
||
return 301 /bitcoin-donation.png;
|
||
}
|
||
|
||
location = /pdfviewer_privacy_policy {
|
||
return 301 /pdfviewer-privacy-policy;
|
||
}
|
||
|
||
# mangled backlinks to /install
|
||
location = /installMinimal {
|
||
return 301 /install/;
|
||
}
|
||
|
||
# mangled backlink to /faq
|
||
location = /fa {
|
||
return 301 /faq;
|
||
}
|
||
|
||
location = /FAQ {
|
||
return 301 /faq;
|
||
}
|
||
|
||
# mangled backlinks to /usage#updates
|
||
location = /updates {
|
||
return 301 /usage#updates;
|
||
}
|
||
|
||
# broken link (now fixed) on https://noagendaphone.com/ with UTF-8 object replacement character
|
||
location = "/" {
|
||
return 301 /;
|
||
}
|
||
|
||
location = /web-install {
|
||
return 301 /install/web;
|
||
}
|
||
|
||
location = /install-web {
|
||
return 301 /install/web;
|
||
}
|
||
|
||
location = /cli/install {
|
||
return 301 /install/cli;
|
||
}
|
||
|
||
location = /web/install {
|
||
return 301 /install/web;
|
||
}
|
||
|
||
location = /LICENSE {
|
||
return 301 /LICENSE.txt;
|
||
}
|
||
|
||
location = /generate_204 {
|
||
return 301 /faq#default-connections;
|
||
}
|
||
|
||
# redirect away from the old SVG favicon location
|
||
location = /mask-icon.svg {
|
||
return 301 /favicon.svg;
|
||
}
|
||
|
||
location = /404 {
|
||
internal;
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
include snippets/preload.conf;
|
||
try_files $uri.html =404;
|
||
}
|
||
|
||
location = /favicon.ico {
|
||
if ($http_accept ~ "image/svg\+xml") {
|
||
rewrite ^ /favicon.svg last;
|
||
}
|
||
include snippets/security-headers.conf;
|
||
# avoid breaking image hotlinking such as https://github.com/TryGhost/Ghost/issues/12880
|
||
add_header Cross-Origin-Resource-Policy "cross-origin" always;
|
||
add_header Cache-Control "public, max-age=604800";
|
||
}
|
||
|
||
location = /favicon.svg {
|
||
include snippets/security-headers.conf;
|
||
# avoid breaking image hotlinking such as https://github.com/TryGhost/Ghost/issues/12880
|
||
add_header Cross-Origin-Resource-Policy "cross-origin" always;
|
||
add_header Cache-Control "public, max-age=604800";
|
||
}
|
||
|
||
location = /bimi.svg {
|
||
include snippets/security-headers.conf;
|
||
# allow https://bimigroup.org/bimi-generator/ to hotlink the image
|
||
add_header Cross-Origin-Resource-Policy "cross-origin" always;
|
||
add_header Cache-Control "public, max-age=604800";
|
||
}
|
||
|
||
location = {{path|/mask-icon.svg}} {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||
}
|
||
|
||
location = /.well-known/matrix/client {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "cross-origin" always;
|
||
add_header Access-Control-Allow-Origin "*";
|
||
default_type application/json;
|
||
}
|
||
|
||
location = /.well-known/matrix/server {
|
||
default_type application/json;
|
||
}
|
||
|
||
location = / {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, no-cache";
|
||
include snippets/preload.conf;
|
||
http2_push $push_phone;
|
||
try_files /index.html =404;
|
||
}
|
||
|
||
location = /install/web {
|
||
include snippets/security-headers-base.conf;
|
||
add_header Content-Security-Policy "default-src 'none'; child-src 'self'; connect-src 'self' https://releases.grapheneos.org/; font-src 'self'; img-src 'self'; manifest-src 'self'; script-src 'self'; style-src 'self'; form-action 'none'; frame-ancestors 'none'; block-all-mixed-content; base-uri 'none'" always;
|
||
add_header Permissions-Policy "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), clipboard-read=(), clipboard-write=(), display-capture=(), document-domain=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), hid=(), idle-detection=(), interest-cohort=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), serial=(), sync-xhr=(), xr-spatial-tracking=()" always;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, no-cache";
|
||
include snippets/preload.conf;
|
||
try_files $uri.html =404;
|
||
}
|
||
|
||
location ^~ /.well-known/acme-challenge/ {
|
||
return 301 https://0.grapheneos.org$request_uri;
|
||
}
|
||
|
||
location ^~ /fonts/ {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||
gzip_static off;
|
||
brotli_static off;
|
||
}
|
||
|
||
location ~ "/$" {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, no-cache";
|
||
include snippets/preload.conf;
|
||
try_files ${uri}index.html @noslash;
|
||
}
|
||
|
||
# redirect /path/ to /path if /path.html exists
|
||
location @noslash {
|
||
rewrite ^(.*)/$ $1;
|
||
if (-f $request_filename.html) {
|
||
rewrite ^(.*) $1 permanent;
|
||
}
|
||
return 404;
|
||
}
|
||
|
||
location ~ "\.(?:css|js|map|mjs)$" {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||
}
|
||
|
||
location ~ "\.webmanifest$" {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, max-age=604800";
|
||
}
|
||
|
||
location ~ "\.png$" {
|
||
include snippets/security-headers.conf;
|
||
# avoid breaking image hotlinking such as https://github.com/TryGhost/Ghost/issues/12880
|
||
add_header Cross-Origin-Resource-Policy "cross-origin" always;
|
||
add_header Cache-Control "public, max-age=31536000";
|
||
gzip_static off;
|
||
brotli_static off;
|
||
}
|
||
|
||
location ~ "\.(?:atom|pdf)$" {
|
||
include snippets/security-headers.conf;
|
||
# Chromium PDF range requests use wrong origin: https://bugs.chromium.org/p/chromium/issues/detail?id=1074261
|
||
# Thunderbird uses wrong origin for feeds: https://bugzilla.mozilla.org/show_bug.cgi?id=1698755
|
||
add_header Cross-Origin-Resource-Policy "cross-origin" always;
|
||
add_header Cache-Control "public, max-age=1800";
|
||
}
|
||
|
||
location ~ "\.(?:json|txt|xml)$" {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, max-age=1800";
|
||
}
|
||
|
||
# https://www.twipu.com/GrapheneOS doesn't handle links with fragments properly
|
||
location ~ "^/([^\s]*)<a href=$" {
|
||
return 301 /$1;
|
||
}
|
||
|
||
location ~ "/index|\.(?:br|gz|html)$" {
|
||
internal;
|
||
}
|
||
|
||
location / {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, no-cache";
|
||
include snippets/preload.conf;
|
||
try_files $uri.html $uri/ =404;
|
||
}
|
||
}
|
||
|
||
server {
|
||
listen 80;
|
||
listen [::]:80;
|
||
server_name mta-sts.grapheneos.org mta-sts.mail.grapheneos.org mta-sts.grapheneos.app mta-sts.grapheneos.ca mta-sts.grapheneos.com mta-sts.grapheneos.info mta-sts.grapheneos.net mta-sts.grapheneos.ovh mta-sts.grapheneos.page mta-sts.vanadium.app;
|
||
|
||
root /var/empty;
|
||
|
||
return 301 https://$host$request_uri;
|
||
}
|
||
|
||
server {
|
||
listen 443 ssl http2;
|
||
listen [::]:443 ssl http2;
|
||
server_name mta-sts.grapheneos.org mta-sts.mail.grapheneos.org mta-sts.grapheneos.app mta-sts.grapheneos.ca mta-sts.grapheneos.com mta-sts.grapheneos.info mta-sts.grapheneos.net mta-sts.grapheneos.ovh mta-sts.grapheneos.page mta-sts.vanadium.app;
|
||
|
||
root /srv/mta-sts;
|
||
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
|
||
location = / {
|
||
return 301 https://grapheneos.org/articles/grapheneos-servers;
|
||
}
|
||
|
||
location = /.well-known/mta-sts.txt {}
|
||
|
||
location ^~ /.well-known/acme-challenge/ {
|
||
return 301 https://0.grapheneos.org$request_uri;
|
||
}
|
||
|
||
location / {
|
||
return 404;
|
||
}
|
||
}
|
||
|
||
server {
|
||
listen 127.0.0.1:81;
|
||
listen [::1]:81;
|
||
|
||
root /var/empty;
|
||
|
||
location = /nginx_status {
|
||
stub_status;
|
||
access_log off;
|
||
}
|
||
|
||
location / {
|
||
return 404;
|
||
}
|
||
}
|
||
}
|