diff --git a/internal/http/response/html/html.go b/internal/http/response/html/html.go
index 923ce2e3..001b7ede 100644
--- a/internal/http/response/html/html.go
+++ b/internal/http/response/html/html.go
@@ -37,7 +37,7 @@ func ServerError(w http.ResponseWriter, r *http.Request, err error) {
builder := response.New(w, r)
builder.WithStatus(http.StatusInternalServerError)
- builder.WithHeader("Content-Security-Policy", `sandbox`)
+ builder.WithHeader("Content-Security-Policy", response.ContentSecurityPolicyForUntrustedContent)
builder.WithHeader("Content-Type", "text/html; charset=utf-8")
builder.WithHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store")
builder.WithBody(err)
@@ -61,7 +61,7 @@ func BadRequest(w http.ResponseWriter, r *http.Request, err error) {
builder := response.New(w, r)
builder.WithStatus(http.StatusBadRequest)
- builder.WithHeader("Content-Security-Policy", `sandbox`)
+ builder.WithHeader("Content-Security-Policy", response.ContentSecurityPolicyForUntrustedContent)
builder.WithHeader("Content-Type", "text/html; charset=utf-8")
builder.WithHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store")
builder.WithBody(err)
diff --git a/internal/http/response/response.go b/internal/http/response/response.go
new file mode 100644
index 00000000..b3c20ede
--- /dev/null
+++ b/internal/http/response/response.go
@@ -0,0 +1,14 @@
+// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package response // import "miniflux.app/v2/internal/http/response"
+
+// ContentSecurityPolicyForUntrustedContent is the default CSP for untrusted content.
+// default-src 'none' disables all content sources
+// form-action 'none' disables all form submissions
+// sandbox enables a sandbox for the requested resource
+// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
+// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/form-action
+// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/sandbox
+// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src
+const ContentSecurityPolicyForUntrustedContent = `default-src 'none'; form-action 'none'; sandbox;`
diff --git a/internal/ui/feed_icon.go b/internal/ui/feed_icon.go
index c22b6ed8..216bb2ce 100644
--- a/internal/ui/feed_icon.go
+++ b/internal/ui/feed_icon.go
@@ -26,7 +26,7 @@ func (h *handler) showFeedIcon(w http.ResponseWriter, r *http.Request) {
}
response.New(w, r).WithCaching(icon.Hash, 72*time.Hour, func(b *response.Builder) {
- b.WithHeader("Content-Security-Policy", `sandbox`)
+ b.WithHeader("Content-Security-Policy", response.ContentSecurityPolicyForUntrustedContent)
b.WithHeader("Content-Type", icon.MimeType)
b.WithBody(icon.Content)
if icon.MimeType != "image/svg+xml" {
diff --git a/internal/ui/proxy.go b/internal/ui/proxy.go
index 8973af67..f1f96765 100644
--- a/internal/ui/proxy.go
+++ b/internal/ui/proxy.go
@@ -144,7 +144,7 @@ func (h *handler) mediaProxy(w http.ResponseWriter, r *http.Request) {
response.New(w, r).WithCaching(etag, 72*time.Hour, func(b *response.Builder) {
b.WithStatus(resp.StatusCode)
- b.WithHeader("Content-Security-Policy", `sandbox`)
+ b.WithHeader("Content-Security-Policy", response.ContentSecurityPolicyForUntrustedContent)
b.WithHeader("Content-Type", resp.Header.Get("Content-Type"))
if filename := path.Base(parsedMediaURL.Path); filename != "" {