Kayra
June 11, 2026, 10:29am
1
In the latest PHPMaker 2026 release, there is a CSP-related issue when using ParagonIE CSPBuilder with WebSocket sources.
When adding:
ws://localhost:2000
wss://localhost:2000
into CSP.connect-src.allow, the final generated CSP header is incorrect:
connect-src 'self' https://*.googleapis.com https://*.gstatic.com https://*.google.com *.google.com ws://:2000 wss://:2000
arbei
June 11, 2026, 11:43pm
4
Your approach is correct, but how did you add them into CSP.connect-src.allow?
If you add them correctly, they will work, for example, you can follow the examples of Global Code :
$config = Config();
$config->append('CSP.connect-src.allow', 'ws://localhost:2000');
$config->append('CSP.connect-src.allow', 'wss://localhost:2000');
Kayra
June 12, 2026, 5:14pm
5
In Global code ('userfn.php') we add the following:
$host = $_SERVER['HTTP_HOST'] ?? null;
if ($host) {
$config = Config();
$config->append('CSP.connect-src.allow', "ws://{$host}:2000");
$config->append('CSP.connect-src.allow', "wss://{$host}:2000");
}
Elsewhere, for example in a custom file:
dump(Config("CSP.connect-src.allow"));
exit();
Output:
array:5 [▼
0 => "https://*.googleapis.com"
1 => "https://*.gstatic.com"
2 => "*.google.com"
3 => "ws://127.0.0.1:2000"
4 => "wss://127.0.0.1:2000"
]
In CspListener.php:
public function onKernelResponse(ResponseEvent $event): void
{
//...
// Retrieve the headers
$cspHeaders = $this->builder->getHeaderArray(false);
dump($this->builder);
exit();
//...
}
Output:
CspListener.php on line 55:
ParagonIE\CSPBuilder\CSPBuilder {#930 ▼
-policies: array:12 [▼
"report-only" => false
"font-src" => array:3 [▶]
"form-action" => array:2 [▶]
"object-src" => array:1 [▶]
"frame-ancestors" => array:1 [▶]
"frame-src" => array:3 [▶]
"script-src" => array:7 [▶]
"connect-src" => array:4 [▼
"self" => true
"blob" => true
"data" => true
"allow" => array:5 [▼
0 => "https://*.googleapis.com"
1 => "https://*.gstatic.com"
2 => "*.google.com"
3 => "ws://:2000"
4 => "wss://:2000"
]
...
]
]
}
Issue Summary:
The HTTP_HOST value (127.0.0.1) is correctly stored in the config, but when CSPBuilder processes it, the host part is lost — only ws://:2000 and wss://:2000 remain (the host is missing between :// and :2000).
Kayra
June 12, 2026, 5:44pm
6
If you do it this way: (in Global Code)
$host = Request()?->getHost() ?: ($_SERVER['HTTP_HOST'] ?? '');
//...
$config = Config();
$config->append('CSP.connect-src.allow', "ws://{$host}:2000");
$config->append('CSP.connect-src.allow', "wss://{$host}:2000");
It will remove the famous $host line. (It worked without any issues until version 2025.10)
Only Container(CSPBuilder::class)?->addSource directly, which is an unofficial method, works for this version (2026.10):
// Page Unloaded event
function Page_Unloaded(): void
{
//...
$host = $_SERVER['HTTP_HOST'] ?? null;
if ($host) {
$id = CSPBuilder::class;
$csp = Container($id);
$csp->addSource('connect', "ws://{$host}:2000");
$csp->addSource('connect', "wss://{$host}:2000");
}
//...
}
arbei
June 13, 2026, 7:46am
9
Real cause is that there is no $_SERVER['HTTP_HOST'] when Symfony builds the cache via CLI. Your approach of getting it during runtime is viable, addSource() is also a public method and the correct method to use.
Kayra
June 14, 2026, 12:31pm
10
The issue is that the current implementation is fundamentally failing to handle dynamic hosts in a way that respects CSP best practices.
mobhar
June 15, 2026, 1:57am
12
Instead of expecting the dynamic host, why don't you use logical if-else that will handle both host for localhost and host for production server?