access_log /var/log/nginx/access.log combined_with_host_and_time; error_log /var/log/nginx/error.log; include /etc/nginx/zulip-include/headers; # Serve a custom error page when the app is down error_page 502 503 504 /static/webpack-bundles/5xx.html; # Serve static files directly location /local-static { alias /home/zulip/local-static; } location /static/ { alias /home/zulip/prod-static/; gzip_static on; include /etc/nginx/zulip-include/headers; add_header Access-Control-Allow-Origin *; add_header Timing-Allow-Origin *; # Set a nonexistent path, so we just serve the nice Django 404 page. error_page 404 /django_static_404.html; # These files are hashed and thus immutable; cache them aggressively. # Django adds 12 hex digits; Webpack adds 20. location ~ '\.[0-9a-f]{12}\.|[./][0-9a-f]{20}\.' { include /etc/nginx/zulip-include/immutable_headers; } } # Canonical URL for the root is /help/. location = /help { return 301 /help/; } # Redirect to links without the trailing slash except for root /help/. location ~ ^(/help(?:/[^/]+)+)/+$ { return 301 $1$is_args$args; } # Serve help center generated by astro/starlight. location /help { alias $help_alias; gzip_static on; include /etc/nginx/zulip-include/headers; add_header Access-Control-Allow-Origin *; add_header Timing-Allow-Origin *; error_page 404 /404.html; index /index.html; try_files $uri $uri/index.html =404; # These files are hashed and thus immutable; cache them aggressively. # https://github.com/Pagefind/pagefind/issues/747#issuecomment-2510564644 # https://github.com/withastro/docs/blob/53603ad048e8aedbca1aed77bac8eb00dcada79d/src/content/docs/en/guides/integrations-guide/node.mdx?plain=1#L304 location ~ ^/help/(pagefind/.*\.(?:pf_fragment|pf_index|pf_meta)|_astro/.*)$ { include /etc/nginx/zulip-include/immutable_headers; } } # Send longpoll requests to Tornado location = /json/events { if ($request_method = 'OPTIONS') { # add_header does not propagate into/out of blocks, so this # include cannot be factored out include /etc/nginx/zulip-include/headers; add_header Allow 'OPTIONS, GET, DELETE' always; return 204; } if ($request_method !~ ^(GET|DELETE)$ ) { # add_header does not propagate into/out of blocks, so this # include cannot be factored out include /etc/nginx/zulip-include/headers; add_header Allow 'OPTIONS, GET, DELETE' always; return 405; } proxy_pass $tornado_server; include /etc/nginx/zulip-include/proxy_longpolling; } # Send longpoll requests to Tornado location = /api/v1/events { if ($request_method = 'OPTIONS') { include /etc/nginx/zulip-include/tornado_cors_headers; add_header Allow 'OPTIONS, GET, DELETE' always; return 204; } if ($request_method !~ ^(GET|DELETE)$ ) { include /etc/nginx/zulip-include/headers; add_header Allow 'OPTIONS, GET, DELETE' always; return 405; } include /etc/nginx/zulip-include/tornado_cors_headers; proxy_pass $tornado_server; include /etc/nginx/zulip-include/proxy_longpolling; } # Handle X-Accel-Redirect from Tornado to Tornado location ~ ^/internal/tornado/(\d+)(/.*)$ { internal; proxy_pass http://tornado$1$2$is_args$args; include /etc/nginx/zulip-include/proxy_longpolling; } location /api/v1/tus { include /etc/nginx/zulip-include/api_headers; include /etc/nginx/zulip-include/proxy; # https://github.com/tus/tusd/blob/main/examples/nginx.conf # Disable request body size limits, and stream the request and # response from tusd directly. client_max_body_size 0; proxy_request_buffering off; proxy_buffering off; proxy_pass http://tusd; } # Send everything else to Django via uWSGI location / { include uwsgi_params; } # These Django routes not under /api are shared between mobile and # web, and thus need API headers added. We can't easily collapse # these blocks with the /api block, because regular expressions take # priority over paths in nginx's order-of-operations, and we don't # want to override the tornado configuration for /api/v1/events. location /thumbnail { include /etc/nginx/zulip-include/api_headers; include uwsgi_params; } location /avatar { include /etc/nginx/zulip-include/api_headers; include uwsgi_params; } location /user_uploads { include /etc/nginx/zulip-include/api_headers; include uwsgi_params; } location /api/internal/ { # These only need be accessed from localhost allow 127.0.0.1; allow ::1; deny all; include /etc/nginx/zulip-include/api_headers; include uwsgi_params; } # Send all API routes not covered above to Django via uWSGI location /api/ { include /etc/nginx/zulip-include/api_headers; include uwsgi_params; } include /etc/nginx/zulip-include/app.d/*.conf;