I’ve been working on adding security headers to my reverse proxy and so far I believe to have gotten most of them except for Content Security Policies, I honestly can’t find a simplified way to apply a CSP to 20+ docker applications and hope folks of Lemmy know the best way to go about this.
I want to note that I never worked with headers in the past, I tried interpreting the Traefik documentation and Mozilla documentation as well as a bunch of random YT videos but can’t seem to get it right.
headers:
headers:
customRequestHeaders:
X-Forwarded-Proto: https
accessControlAllowMethods:
- GET
- OPTIONS
- PUT
accessControlMaxAge: 100
hostsProxyHeaders:
- "X-Forwarded-Host"
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
forceSTSHeader: true # This is a good thing but it can be tricky. Enable after everything works.
customFrameOptionsValue: SAMEORIGIN # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
contentTypeNosniff: true
browserXssFilter: true
contentSecurityPolicy: ""
referrerPolicy: "same-origin"
permissionsPolicy: "camera=(), microphone=(), geolocation=(), usb=()"
customResponseHeaders:
X-Robots-Tag: "none,noarchive,nosnippet,notranslate,noimageindex," # disable search engines from indexing home server
server: "traefik"
this just in 👉 https://hub.docker.com/r/mhzawadi/csp-report
Ah bless!
I’ve been using Firefox extensions to make up my CSP’s and so far it’s been an absolute nightmare! Thank you for this tool!
will be adding it to my setup on Tuesday, so will see how well it works in anger
Is there any special configuration needed for it? Env variables, network interface, volumes & config files, etc?
I plan to set this up in a docker-compose file later this afternoon once I get some free time.
theres no config for it, it listens on port 8080. I need to expose the logs dir, but not done that yet. So all the logs will only be in the container, but you could point a mount at
/var/www/html/logs
Hey there! Following up on this, i got your application started however, it seems that this is more for folks who actually know PHP an HTML and less for folks like myself who just find Docker applications (Invidious, Homepage, RedLib) spin them up and rig them to work with one another so i don’t think this will work for me personally at the moment in time regardless seems like a great tool nonetheless!
Thanks again!
Oh, you shouldn’t need to do anything other then point a site at it.
Will update the readme when I get home
Edit: thanks for the feedback
Okay so going at it again, i think i now understand the reason for the Docker label now, here is my current
docker-compose.yml
i made some tweaks to the one from your github but i can’t seem to get a log file to generate.I suspected it was a permissions issue on the volume mount so i ran
chmod 777
on the./config/csp
directory but still wont get a log file.Volume directory permissions:
user@debian:~/compose$ ls config/ | grep csp; ls config/csp/; ls config/csp/logs/ drwxrwxrwx 3 user user 4096 Aug 9 09:11 csp total 12 drwxrwxrwx 3 user user 4096 Aug 9 09:11 . drwxr-xr-x 44 user user 4096 Aug 8 16:41 .. drwxrwxrwx 2 user user 4096 Aug 9 09:04 logs total 8 drwxrwxrwx 2 user user 4096 Aug 9 09:04 . drwxrwxrwx 3 user user 4096 Aug 9 09:11 ..
docker-compose.yml:
csp-report: image: mhzawadi/csp-report networks: main: ipv4_address: 172.18.0.38 #ports: # - "8432:8080" ports: - target: 8080 published: 8432 mode: host container_name: csp-report environment: - TZ=America/Vancouver labels: - "csp_report.url=192.168.1.199:3000" volumes: - ./config/csp/logs:/var/www/html/logs
Logs from the docker container:
user@debian:~/compose$ sudo docker compose up -d csp-report --force-recreate; sudo docker logs csp-report -f WARN[0000] The "POSTGRES_DB" variable is not set. Defaulting to a blank string. [+] Running 1/1 ✔ Container csp-report Started 0.5s /config/start.sh: Launching Unit daemon to perform initial configuration... 2025/08/09 16:21:18 [info] 12#12 unit 1.34.1 started 2025/08/09 16:21:18 [info] 14#14 discovery started BusyBox v1.37.0 (2025-08-05 16:42:11 UTC) multi-call binary. Usage: seq [-w] [-s SEP] [FIRST [INC]] LAST Print numbers from FIRST to LAST, in steps of INC. FIRST, INC default to 1. -w Pad with leading zeros -s SEP String separator 2025/08/09 16:21:18 [notice] 14#14 module: php 8.4.2 "/usr/lib/unit/modules/php84.unit.so" 2025/08/09 16:21:18 [info] 13#13 controller started 2025/08/09 16:21:18 [notice] 13#13 process 14 exited with code 0 2025/08/09 16:21:18 [info] 18#18 router started 2025/08/09 16:21:18 [info] 18#18 OpenSSL 3.3.4 1 Jul 2025, 30300040 { "certificates": {}, "config": { "listeners": {}, "routes": [], "applications": {} }, "status": { "modules": { "php": { "version": "8.4.2", "lib": "/usr/lib/unit/modules/php84.unit.so" } }, "connections": { "accepted": 0, "active": 0, "idle": 0, "closed": 0 }, "requests": { "total": 0 }, "applications": {} } } % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 2025/08/09 16:21:18 [info] 20#20 "csp_report" prototype started 2025/08/09 16:21:18 [info] 21#21 "csp_report" application started { "success": "Reconfiguration done." } 100 413 100 43 100 370 2808 24162 --:--:-- --:--:-- --:--:-- 27533 /config/start.sh: Stopping Unit daemon after initial configuration... 2025/08/09 16:21:18 [notice] 13#13 process 17 exited with code 0 2025/08/09 16:21:18 [notice] 20#20 app process 21 exited with code 0 2025/08/09 16:21:18 [alert] 20#20 sendmsg(13, -1, -1, 2) failed (32: Broken pipe) 2025/08/09 16:21:18 [notice] 13#13 process 18 exited with code 0 2025/08/09 16:21:18 [notice] 13#13 process 20 exited with code 0 BusyBox v1.37.0 (2025-08-05 16:42:11 UTC) multi-call binary. Usage: seq [-w] [-s SEP] [FIRST [INC]] LAST Print numbers from FIRST to LAST, in steps of INC. FIRST, INC default to 1. /config/start.sh: Unit initial configuration complete; ready for start up... -w Pad with leading zeros -s SEP String separator 2025/08/09 16:21:18 [info] 1#1 unit 1.34.1 started 2025/08/09 16:21:18 [info] 31#31 discovery started 2025/08/09 16:21:18 [notice] 31#31 module: php 8.4.2 "/usr/lib/unit/modules/php84.unit.so" 2025/08/09 16:21:18 [info] 1#1 controller started 2025/08/09 16:21:18 [notice] 1#1 process 31 exited with code 0 2025/08/09 16:21:18 [info] 33#33 router started 2025/08/09 16:21:18 [info] 33#33 OpenSSL 3.3.4 1 Jul 2025, 30300040 2025/08/09 16:21:18 [info] 34#34 "csp_report" prototype started 2025/08/09 16:21:18 [info] 35#35 "csp_report" application started 127.0.0.1 - - [09/Aug/2025:16:21:23 +0000] "POST / HTTP/1.1" 200 7 "-" "curl/8.12.1"
your in luck, I have been doing this at work.
First have a look at the report header Content-Security-Policy-Report-Only add things you know about, as that will allow you to log what you need to have setup. Then once you have a list of things you know you need, add them and what the logs again. adding in things as you see and verify them.
Also dont just add things in without checking that its in your code, you dont want to add
hack-me.com
by mistake.Do you happen to know of any self hosted report-ingestion, that allows me to check my csp and somewhat visualize them? I know there are services like report-uri and such, but they do cost a monthly fee and probably also have a privacy impact for my visitors
I looked in to this a while back and didn’t find anything complete. I ended up using a collector and feeding the reports in to my existing Loki and making my own Grafana dashboard.
I have started on a docker image that will allow you to collect the CSP reports 👉 https://github.com/mhzawadi/docker-csp-report
Its not working just yet, will work it this week
Sorry I dont, but our devs have some PHP to log requests to a file. On Monday will see if I can get that and make a docker image you can run
First have a look at the report header Content-Security-Policy-Report-Only add things you know about, as that will allow you to log what you need to have setup. Then once you have a list of things you know you need, add them and what the logs again. adding in things as you see and verify them.
Thank you for this! I feel as if I got the idea however, failed on the execution. I added this to my
routers.yml
-contentSecurityPolicyReportOnly: "child-src; connect-src; default-src; font-src; frame-src; img-src; manifest-src; media-src; object-src; script-src; script-src-elem; script-src-attr; style-src; style-src-elem; style-src-attr; worker-src; report-to csp-report"
To my understanding the
report-to
directive at the end adds a endpoint called/csp-report
yet in my testing it doesn’t appear to exist and just leads to 403, I’m likely doing something totally wrong but I appreciate you pointing me in the right direction!Also dont just add things in without checking that its in your code, you dont want to add
hack-me.com
by mistake.That’s fair, I’m certainly no wiz at programming and this is just self-hosted docker applications that I’ve jerry-rigged to work with one another.
Yeah that’s the gist, there need to be something on the end of
/csp-report
or it won’t work.Will have a look and see if I can make something
Is scripting an option? You could script adding the header.
I started with this in 2021 https://mgdm.net/weblog/csp-logging-with-nginx/
At some point in 2024 i got Claude to take over this management. Now i have an agent that maintains this workflow, and all csp reports are in nginx/json