{# Initialize an array to collect each CSP directive line #} {%- set csp_parts = [] %} {# List of all directives to process dynamically (except img-src) #} {%- set directives = [ 'default-src', 'connect-src', 'frame-ancestors', 'frame-src', 'script-src', 'style-src', 'font-src' ] %} {# Loop over each directive and build its value from 'self', any unsafe flags, whitelist URLs, and optional Matomo #} {%- for directive in directives %} {# Start with the 'self' source #} {%- set tokens = ["'self'"] %} {# Add any unsafe flags (unsafe-eval, unsafe-inline) from csp.flags. #} {%- for flag in applications | get_csp_flags(application_id, directive) %} {%- set tokens = tokens + [flag] %} {%- endfor %} {# If Matomo feature is enabled, whitelist its script and connect sources #} {%- if applications | is_feature_enabled('matomo', application_id) and directive in ['script-src','connect-src'] %} {%- set tokens = tokens + ['{{ web_protocol }}://{{ domains.matomo }}'] %} {%- endif %} {# Add any extra hosts/URLs from csp.whitelist. #} {%- for url in applications | get_csp_whitelist(application_id, directive) %} {%- set tokens = tokens + [url] %} {%- endfor %} {# Combine into a single directive line and append to csp_parts #} {%- set csp_parts = csp_parts + [directive ~ ' ' ~ (tokens | join(' ')) ~ ';'] %} {%- endfor %} {# Preserve original img-src directive logic (do not loop) #} {%- set img_src = 'img-src * data: blob:' %} {%- set csp_parts = csp_parts + [img_src ~ ';'] %} {# Emit the assembled Content-Security-Policy header and hide any upstream CSP header #} add_header Content-Security-Policy "{{ csp_parts | join(' ') }}" always; proxy_hide_header Content-Security-Policy;