Skip to content

Serving a Header on Some Paths

The policy HeaderHawk builds from your script decisions (A policy from your decisions) is for your payment pages only. Your decisions cover only the scripts seen there, so served across the site it would block every script that runs only on your other pages. Scripts › Payment pages › Policy lists the paths, as your payment pages’ patterns.

A browser enforces every Content-Security-Policy header a response carries, and a script runs only if each of them allows it. So the two phases are served differently:

  • Report-only. Serve the payment-page header as Content-Security-Policy-Report-Only on those paths, beside whatever your site already serves there. It blocks nothing; it reports what it would.
  • Enforcing. On those paths, serve the payment-page Content-Security-Policy in place of your site-wide one. Served beside it, a pinned script whose host your site-wide policy does not list would still be blocked by that policy.

Keep the Reporting-Endpoints line your site already serves on those paths: the policy’s report-to names it.

An add_header inside a location replaces every add_header the server block sets, rather than adding to them, so repeat the ones the location still needs.

server {
add_header Reporting-Endpoints 'headerhawk="https://your-ingest-code.ingest.us.headerhawk.com/r"' always;
add_header Content-Security-Policy "SITE_POLICY" always;
location /checkout/ {
add_header Reporting-Endpoints 'headerhawk="https://your-ingest-code.ingest.us.headerhawk.com/r"' always;
# While the payment-page policy is report-only, keep the site's:
add_header Content-Security-Policy "SITE_POLICY" always;
add_header Content-Security-Policy-Report-Only "PAYMENT_POLICY" always;
# Once it is enforced, replace both lines above with:
# add_header Content-Security-Policy "PAYMENT_POLICY" always;
try_files $uri $uri/ /index.html;
}
}

Header always set in a <Location> replaces the value the server sets for that header on those paths.

Header always set Content-Security-Policy "SITE_POLICY"
<Location "/checkout/">
# Report-only first:
Header always set Content-Security-Policy-Report-Only "PAYMENT_POLICY"
# Once it is enforced, use this instead:
# Header always set Content-Security-Policy "PAYMENT_POLICY"
</Location>
const PAYMENT_PATHS = ["/checkout/"];
app.use((req, res, next) => {
const payment = PAYMENT_PATHS.some((path) => req.path.startsWith(path));
res.setHeader("Content-Security-Policy", SITE_POLICY);
if (payment) {
// Report-only first; once it is enforced, set Content-Security-Policy to
// PAYMENT_POLICY instead of SITE_POLICY and drop this line.
res.setHeader("Content-Security-Policy-Report-Only", PAYMENT_POLICY);
}
next();
});

In next.config.js, when two entries match a path and set the same header, the later one wins, so list the payment pages after the rest of the site.

module.exports = {
async headers() {
return [
{
source: "/:path*",
headers: [{ key: "Content-Security-Policy", value: SITE_POLICY }],
},
{
source: "/checkout/:path*",
headers: [
// Report-only first; once it is enforced, use
// { key: "Content-Security-Policy", value: PAYMENT_POLICY }.
{ key: "Content-Security-Policy-Report-Only", value: PAYMENT_POLICY },
],
},
];
},
};

A Response Header Transform Rule sets a header on the paths its filter matches. Create one with the filter starts_with(http.request.uri.path, "/checkout/") and the action Set static: Content-Security-Policy-Report-Only to the payment-page policy first, then Content-Security-Policy once it is enforced. Set replaces any value your origin or an earlier rule sent, which is what the enforcing phase needs.

Terminal window
curl -sI https://www.example.com/checkout/ | grep -i content-security-policy

While the payment-page policy is report-only, a payment page answers with your site’s Content-Security-Policy and the payment-page Content-Security-Policy-Report-Only. Once it is enforced, it answers with exactly one Content-Security-Policy, the payment-page one, and any other page still answers with your site’s. The Policy view says when your payment pages first served the policy in each phase, from their reports or the daily scan.