How to work-around firefox lack of respect for the CSP specification for CSP reports to Sentry


As stated in this issue Firefox doesn't respect the specification and doesn't include the fields effective-directive or status-code.

Sentry expect them, and then refuse the reports because of that.

To workaround the issue, I used Nginx LUA module to manipulate the JSON body before it is send to the uwsgi backend of Sentry.

Note: use it at your own risks.

CSP Headers config

The CSP should contains:

Content-Security-Policy: whatever-you; want;... report-uri

Nginx thing

Makes sures the module is enabled, on Debian it's something like:

# cat /etc/nginx/modules-enabled/00-mod-http-ndk.conf

load_module modules/;
# cat /etc/nginx/modules-enabled/50-mod-http-lua.conf

load_module modules/;

# Makes sure there is:

include /etc/nginx/modules-enabled/*.conf;
# before the http {} directive in /etc/nginx/nginx.conf

I also needed to do in /etc/nginx/nginx.conf because the paths seems wrong by default:

http {
    lua_package_path "/usr/share/lua/5.1/?.lua;;";
    lua_package_cpath '/usr/lib/x86_64-linux-gnu/lua/5.1/?.so;;';

Packages needed are: nginx-extras libnginx-mod-http-lua libnginx-mod-http-ndk lua-cjson

Add LUA call in the virtual host of your Sentry:

    location ~ ^/api/(?<projet>[0-9]+)/csp-report/ {
        access_by_lua_file /etc/nginx/proxy_csp.lua;
        include uwsgi_params;

    location / {
        include uwsgi_params;

And the most useful file, /etc/nginx/proxy_csp.lua:

if ngx.req.get_method() == "POST" then
    local cjson = require "cjson"

    -- read body and set local variables, also dump into logs for debugging if needed
    local body = ngx.req.get_body_data()
    --ngx.log(ngx.STDERR, body)
    -- read json body
    local json =

    -- We need to manipulate the JSON body to add if missing:
    -- effective-directive: the violated directive name
    -- status-code: HTTP status code of the violated directive

    if (not json['csp-report']['effective-directive']) then
    -- ugly split thing to get the directive name
        words = {}
        local vd = json['csp-report']['violated-directive']
        for word in vd:gmatch("[a-zA-Z0-9-]+") do table.insert(words, word) end

        if (words[1]) then
            json['csp-report']['effective-directive'] = words[1]
            json['csp-report']['effective-directive'] = 'Unknown violation wrong format string'

    if (not json['csp-report']['status-code']) then
        json['csp-report']['status-code'] = 200

    -- reencode new body
    new_json = cjson.encode(json)
    -- set new body

    -- we are done