Files
frigate/docker/main/rootfs/usr/local/nginx/conf/nginx.conf
T
Josh Hawkins e981ea326f use sub_filter to pass vod playlist args to segment URLs
Home Assistant signs vod playlist URLs with authSig and needs it on every init, segment, and index URL in the playlist. sub_filter appends the request args to the three URL endings vod writes, giving byte-identical playlists to the old secure_token output.
2026-11-07 15:38:23 -06:00

407 lines
13 KiB
Nginx Configuration File

# Loaded with -c from the /tmp/nginx/conf copy: relative includes follow the -c
# file, all other path directives follow --prefix and must stay absolute.
daemon off;
# ignored by a non-root master; keeps workers root under FRIGATE_RUN_AS_ROOT
user root;
worker_processes auto;
error_log /dev/stdout warn;
pid /tmp/nginx/nginx.pid;
events {
worker_connections 1024;
}
http {
map_hash_bucket_size 256;
server_tokens off;
client_body_temp_path /tmp/nginx/client_body;
proxy_temp_path /tmp/nginx/proxy;
fastcgi_temp_path /tmp/nginx/fastcgi;
uwsgi_temp_path /tmp/nginx/uwsgi;
scgi_temp_path /tmp/nginx/scgi;
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'request_time="$request_time" upstream_response_time="$upstream_response_time"';
access_log /dev/stdout main;
# send headers in one piece, it is better than sending them one by one
tcp_nopush on;
sendfile on;
keepalive_timeout 65;
gzip on;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/x-javascript application/javascript text/javascript image/svg+xml image/x-icon image/bmp;
gzip_proxied no-cache no-store private expired auth;
gzip_vary on;
proxy_cache_path /dev/shm/nginx_cache levels=1:2 keys_zone=api_cache:10m max_size=10m inactive=1m use_temp_path=off;
map $sent_http_content_type $should_not_cache {
'application/json' 0;
default 1;
}
# keep an outer proxy's X-Forwarded-Host so the websocket servers can
# match a browser's Origin when that proxy rewrites Host
map $http_x_forwarded_host $forwarded_host {
"" $http_host;
default $http_x_forwarded_host;
}
upstream frigate_api {
server 127.0.0.1:5001;
keepalive 1024;
}
upstream mqtt_ws {
server 127.0.0.1:5002;
keepalive 1024;
}
upstream jsmpeg {
server 127.0.0.1:8082;
keepalive 1024;
}
include go2rtc_upstream.conf;
server {
include listen.conf;
include security_headers.conf;
# enable HTTP/2 for TLS connections to eliminate browser 6-connection limit
http2 on;
# vod settings
vod_base_url '';
vod_segments_base_url '';
vod_mode mapped;
vod_max_mapping_response_size 1m;
vod_upstream_location /api;
vod_align_segments_to_key_frames on;
vod_manifest_segment_durations_mode accurate;
vod_ignore_edit_list on;
# short leading segments at each playlist start; sources start at
# the seek target, so the ladder applies to every seek. Only
# effective when clips declare real keyFrameDurations
vod_bootstrap_segment_durations 1000;
vod_bootstrap_segment_durations 2000;
vod_bootstrap_segment_durations 4000;
vod_segment_duration 10000;
# MPEG-TS settings (not used when fMP4 is enabled, kept for reference)
vod_hls_mpegts_align_frames off;
vod_hls_mpegts_interleave_frames on;
# file handle caching / aio
open_file_cache max=1000 inactive=5m;
open_file_cache_valid 2m;
open_file_cache_min_uses 1;
open_file_cache_errors on;
aio on;
# file upload size
client_max_body_size 20M;
# https://github.com/kaltura/nginx-vod-module#vod_open_file_thread_pool
vod_open_file_thread_pool default;
# vod caches
vod_metadata_cache metadata_cache 512m;
vod_mapping_cache mapping_cache 5m 10m;
# gzip manifests
gzip on;
gzip_types application/vnd.apple.mpegurl;
include auth_location.conf;
include base_path.conf;
location /vod/ {
include auth_request.conf;
aio threads;
vod hls;
# Use fMP4 (fragmented MP4) instead of MPEG-TS for better performance
# Smaller segments, faster generation, better browser compatibility
vod_hls_container_format fmp4;
# fMP4 playlists use EXT-X-MAP, which requires HLS protocol
# version 6 (RFC 8216 section 7); the module default is 4
vod_hls_version 6;
# Pass the playlist's query args (e.g. Home Assistant's authSig) on
# to every URL in it. These match the only URL endings vod writes
# with fmp4 and no subtitles.
sub_filter_types application/vnd.apple.mpegurl;
sub_filter_once off;
sub_filter '.m3u8' '.m3u8$is_args$args';
sub_filter '.m4s' '.m4s$is_args$args';
sub_filter '.mp4"' '.mp4$is_args$args"';
include security_headers.conf;
add_header Cache-Control "no-store";
expires off;
keepalive_disable safari;
}
location /stream/ {
include auth_request.conf;
include security_headers.conf;
add_header Cache-Control "no-store";
expires off;
types {
application/dash+xml mpd;
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
image/jpeg jpg;
}
root /tmp;
}
location /clips/ {
include auth_request.conf;
types {
video/mp4 mp4;
image/jpeg jpg jpeg;
image/png png;
image/webp webp;
}
expires 7d;
include security_headers.conf;
add_header Cache-Control "public";
autoindex on;
root /media/frigate;
# review thumbnails are rewritten in place while a review is active,
# so a cached file size truncates the response
location /clips/review/ {
open_file_cache off;
}
}
location /cache/ {
internal; # This tells nginx it's not accessible from the outside
alias /tmp/cache/;
}
location /recordings/ {
include auth_request.conf;
types {
video/mp4 mp4;
}
autoindex on;
autoindex_format json;
root /media/frigate;
}
location /exports/ {
include auth_request.conf;
types {
video/mp4 mp4;
}
autoindex on;
autoindex_format json;
root /media/frigate;
}
location /ws {
include auth_request.conf;
proxy_pass http://mqtt_ws/;
include proxy.conf;
}
location /live/jsmpeg/ {
include auth_request.conf;
proxy_pass http://jsmpeg/;
include proxy.conf;
}
# frigate lovelace card uses this path
location /live/mse/api/ws {
# tells /auth to apply the go2rtc stream checks
set $go2rtc_stream_proxy 1;
include auth_request.conf;
limit_except GET {
deny all;
}
proxy_pass http://go2rtc/api/ws;
include proxy.conf;
}
location /live/webrtc/api/ws {
# tells /auth to apply the go2rtc stream checks
set $go2rtc_stream_proxy 1;
include auth_request.conf;
limit_except GET {
deny all;
}
proxy_pass http://go2rtc/api/ws;
include proxy.conf;
}
# pass through go2rtc player
location /live/webrtc/webrtc.html {
include auth_request.conf;
limit_except GET {
deny all;
}
proxy_pass http://go2rtc/webrtc.html;
include proxy.conf;
}
# integration uses this to add webrtc candidate
location /api/go2rtc/webrtc {
# tells /auth to apply the go2rtc stream checks
set $go2rtc_stream_proxy 1;
include auth_request.conf;
limit_except POST {
deny all;
}
proxy_pass http://go2rtc/api/webrtc;
include proxy.conf;
}
location ~* /api/.*\.(jpg|jpeg|png|webp|gif)$ {
include auth_request.conf;
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://frigate_api;
include proxy.conf;
}
location /api/ {
include auth_request.conf;
include security_headers.conf;
add_header Cache-Control "no-store";
expires off;
proxy_pass http://frigate_api/;
include proxy.conf;
proxy_cache api_cache;
proxy_cache_key "$scheme$proxy_host$request_uri|$role|$groups|$user";
proxy_cache_lock on;
proxy_cache_use_stale updating;
proxy_cache_valid 200 5s;
proxy_cache_bypass $http_x_cache_bypass;
proxy_no_cache $should_not_cache;
add_header X-Cache-Status $upstream_cache_status;
location /api/vod/ {
include auth_request.conf;
proxy_pass http://frigate_api/vod/;
include proxy.conf;
proxy_cache off;
}
location /api/login {
auth_request off;
rewrite ^/api(/.*)$ $1 break;
proxy_pass http://frigate_api;
include proxy.conf;
}
location /api/logout {
auth_request off;
rewrite ^/api(/.*)$ $1 break;
proxy_pass http://frigate_api;
include proxy.conf;
}
# Allow unauthenticated access to the first_time_login endpoint
# so the login page can load help text before authentication.
location /api/auth/first_time_login {
auth_request off;
limit_except GET {
deny all;
}
rewrite ^/api(/.*)$ $1 break;
proxy_pass http://frigate_api;
include proxy.conf;
}
location /api/stats {
include auth_request.conf;
access_log off;
rewrite ^/api(/.*)$ $1 break;
proxy_pass http://frigate_api;
include proxy.conf;
}
location /api/version {
include auth_request.conf;
access_log off;
rewrite ^/api(/.*)$ $1 break;
proxy_pass http://frigate_api;
include proxy.conf;
}
}
location / {
# do not require auth for static assets
include security_headers.conf;
add_header Cache-Control "no-store";
expires off;
location /assets/ {
access_log off;
expires 1y;
include security_headers.conf;
add_header Cache-Control "public";
}
location /locales/ {
access_log off;
include security_headers.conf;
add_header Cache-Control "public";
}
location ~ ^/.*-([A-Za-z0-9]+)\.webmanifest$ {
access_log off;
expires 1y;
include security_headers.conf;
add_header Cache-Control "public";
default_type application/json;
proxy_set_header Accept-Encoding "";
sub_filter_once off;
sub_filter_types application/json;
sub_filter '"start_url": "/BASE_PATH/"' '"start_url" : "$http_x_ingress_path/"';
sub_filter '"src": "/BASE_PATH/' '"src": "$http_x_ingress_path/';
}
sub_filter 'href="/BASE_PATH/' 'href="$http_x_ingress_path/';
sub_filter 'url(/BASE_PATH/' 'url($http_x_ingress_path/';
sub_filter '"/BASE_PATH/dist/' '"$http_x_ingress_path/dist/';
sub_filter '"/BASE_PATH/js/' '"$http_x_ingress_path/js/';
sub_filter '"/BASE_PATH/assets/' '"$http_x_ingress_path/assets/';
sub_filter '"/BASE_PATH/locales/' '"$http_x_ingress_path/locales/';
sub_filter '"/BASE_PATH/monacoeditorwork/' '"$http_x_ingress_path/assets/';
sub_filter 'return`/BASE_PATH/`' 'return window.baseUrl';
sub_filter '<body>' '<body><script>window.baseUrl="$http_x_ingress_path/";</script>';
sub_filter_types text/css application/javascript;
sub_filter_once off;
root /opt/frigate/web;
try_files $uri $uri.html $uri/ /index.html;
}
}
}