From a8ccb5e0cdcac027c59f1d88907763d44d5ba17a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 26 Aug 2026 20:36:35 +0200 Subject: [PATCH] :whale: Add configurable CSP and HSTS headers to the frontend image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ship both headers from the image so that every deployment starts from a sensible default instead of each installation deriving its own policy. Report-only mode never blocks a request, so this changes no behaviour for existing deployments, and HSTS stays absent unless PENPOT_PUBLIC_URI declares an https scheme. The policy can be narrow because the frontend already reverse proxies its own external dependencies, so 'self' covers them. What it must permit beyond that comes from the code: 'wasm-unsafe-eval' for the render engine, 'unsafe-inline' styles for the inline style attributes of the UI, and blob:/data: for thumbnails, exports and fonts. Closes #11374 Signed-off-by: David Barragán Merino AI-assisted-by: Claude --- docker/images/Dockerfile.frontend | 2 +- docker/images/files/nginx-entrypoint.sh | 69 +++++++++++++++++++ ...f => nginx-security-headers.conf.template} | 2 + docs/technical-guide/configuration.md | 54 +++++++++++++++ 4 files changed, 126 insertions(+), 1 deletion(-) rename docker/images/files/{nginx-security-headers.conf => nginx-security-headers.conf.template} (83%) diff --git a/docker/images/Dockerfile.frontend b/docker/images/Dockerfile.frontend index a134f15f27..6565e519c1 100644 --- a/docker/images/Dockerfile.frontend +++ b/docker/images/Dockerfile.frontend @@ -27,7 +27,7 @@ COPY ./files/nginx.conf.template /tmp/nginx.conf.template COPY ./files/nginx-resolvers.conf.template /tmp/resolvers.conf.template COPY ./files/nginx-admin-console-locations.conf.template /tmp/nginx-admin-console-locations.conf.template COPY ./files/nginx-mcp-locations.conf.template /tmp/nginx-mcp-locations.conf.template -COPY ./files/nginx-security-headers.conf /etc/nginx/nginx-security-headers.conf +COPY ./files/nginx-security-headers.conf.template /tmp/nginx-security-headers.conf.template COPY ./files/nginx-mime.types /etc/nginx/mime.types COPY ./files/nginx-external-locations.conf /etc/nginx/overrides/location.d/external-locations.conf COPY ./files/nginx-entrypoint.sh /entrypoint.sh diff --git a/docker/images/files/nginx-entrypoint.sh b/docker/images/files/nginx-entrypoint.sh index 47a8a5544b..06ccd65f3e 100644 --- a/docker/images/files/nginx-entrypoint.sh +++ b/docker/images/files/nginx-entrypoint.sh @@ -84,4 +84,73 @@ export PENPOT_INTERNAL_RESOLVER=${PENPOT_INTERNAL_RESOLVER:-$PENPOT_DEFAULT_INTE envsubst "\$PENPOT_INTERNAL_RESOLVER" \ < /tmp/resolvers.conf.template > /etc/nginx/overrides/http.d/resolvers.conf +######################################### +## Security Headers Config +######################################### + +# The default policy describes what a stock Penpot deployment actually +# needs: 'wasm-unsafe-eval' for the render engine, 'unsafe-inline' styles +# for the inline style attributes emitted by the UI, and blob:/data: for +# thumbnails, exports and font handling. Everything else is same-origin, +# because the Google Fonts and GitHub templates endpoints are reverse +# proxied by this very server. +# +# It ships in report-only mode: the inline