How to Implement Content Security Policy (CSP) via HTTP Headers to Stop XSS Attacks

Introduction

Data validation and output escaping are fundamental security practices in web development. However, even with strict coding standards and code reviews, modern applications that rely on multiple third-party scripts can still be vulnerable to Cross-Site Scripting (XSS) attacks.

If an attacker injects malicious JavaScript into your website, they can steal session cookies, hijack user accounts, or manipulate your UI. To mitigate these risks, modern browsers support Content Security Policy (CSP), a powerful HTTP security header that restricts which resources the browser is allowed to execute.

This guide explains how to implement and deploy a strict CSP policy using PHP and server-side configuration.


How Content Security Policy Prevents XSS Attacks

By default, browsers trust all scripts loaded on a web page. If malicious code is injected, it executes immediately.

With CSP enabled, you define trusted sources for scripts, styles, images, and other assets. Any resource that is not explicitly permitted is automatically blocked by the browser.

This significantly reduces the risk of script injection attacks and strengthens overall application security.


Prerequisites

  • A running web application (PHP, Symfony, WordPress, etc.)
  • Access to server configuration (Nginx, Apache) or application middleware

Step 1: Creating a Secure CSP Policy

A basic CSP policy defines strict rules for loading external resources:


Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:;

Key Directives Explained:

  • default-src ‘self’: Restricts all resources to your own domain by default.
  • script-src: Allows JavaScript only from trusted sources.
  • img-src: Permits images from your domain or base64 encoded data.

Step 2: Implementing CSP in PHP

You can apply CSP headers directly using PHP before any output is sent to the browser:

<?php

$cspDirectives = [
    "default-src 'self'",
    "script-src 'self' https://www.google-analytics.com",
    "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
    "font-src 'self' https://fonts.gstatic.com",
    "img-src 'self' data: https:",
    "connect-src 'self' https://www.google-analytics.com",
    "frame-ancestors 'none'"
];

header("Content-Security-Policy: " . implode('; ', $cspDirectives));

Note: In Symfony applications, CSP should be handled using security bundles or response event listeners.


Step 3: Testing CSP with Report-Only Mode

Before enforcing CSP in production, you can use report-only mode. This allows you to detect violations without blocking resources.

header("Content-Security-Policy-Report-Only: " . implode('; ', $cspDirectives));

This helps identify which scripts or assets need to be added to your trusted sources before full deployment.


Technical Verdict & Best Practices

Content Security Policy is one of the strongest defenses against XSS attacks. However, it requires careful maintenance as your application evolves.

For high-security systems, consider using CSP nonces or hashes instead of broad domain-based trust rules to further strengthen security.


Conclusion

Implementing CSP transforms your application security model from passive protection to active browser-level enforcement. It significantly reduces the risk of XSS attacks and improves overall web security posture.