Direct answer: of 25 homepages, 11 return a Content-Security-Policy and 14 do not. But none actually restricts scripts: nine set only frame-ancestors, block-all-mixed-content or upgrade-insecure-requests, and two (E13, E17) use * with 'unsafe-inline' and 'unsafe-eval' in script-src. Across all 25 sites there are no nonce, hash or strict-dynamic values, and none of 151 inline scripts carries a nonce.
This study reads homepage response headers and inline script tags only; it does not execute page scripts or test actual blocking.
Method: response headers plus inline script tags
On 2026-11-08 between 16:41 and 16:43 each of the 25 homepages from study 4 received one GET (15-second timeout); we read Content-Security-Policy (plus Report-Only and meta forms), parsed directives and tokens, and counted inline <script> tags and their nonce attributes. Page scripts are not executed.
| Item | Rule | Boundary |
|---|---|---|
| CSP present | Header or meta Content-Security-Policy | Browser enforcement not tested |
| Actually restricts scripts | script-src/default-src without unsafe-inline/nonce/hash | No block test |
| Non-script only | Only frame-ancestors/mixed-content/upgrade | Scripts are not constrained |
| Inline script | A script tag without src | Dynamic insertion excluded |
Result: 11 CSPs, nine with non-script directives only
| CSP situation | Sites | Note |
|---|---|---|
| No CSP | 14 | — |
| Non-script directives only | 9 | frame-ancestors, block-all-mixed-content, upgrade-insecure-requests |
| Broad script-src | 2 | E13, E17: * plus unsafe-inline and unsafe-eval |
| Actually restricts scripts | 0 | — |
Nine of the 11 CSPs only address framing, mixed content or protocol upgrade, not script origins; the other two do write script-src but allow any origin with *, inline scripts with 'unsafe-inline' and string evaluation with 'unsafe-eval'. That is closer to “having a CSP” than to “restricting scripts”.
nonce/hash: zero across all 25 sites
| Script allowlist | Sites | Note |
|---|---|---|
| nonce-* | 0 | — |
| sha256/sha384 hash | 0 | — |
| strict-dynamic | 0 | — |
| unsafe-inline (regardless of origin scope) | 2 | E13, E17 |
Without a nonce or hash, tightening script-src while keeping the current inline scripts is not possible: removing 'unsafe-inline' would block them. The 25 sites carry 151 inline scripts (about six each, E04 alone at 41), so the migration cost is not trivial.
What this means for acceptance
- Separate “has a CSP” from “restricts scripts”. Nine of 11 only set non-script directives; acceptance should name the directives, not just check for a header.
- Do not settle for * plus unsafe-inline. E13 and E17 allow any origin and inline execution, so XSS protection is effectively gone.
- Introduce nonce/hash gradually. No site uses one; start with new pages or templates, then migrate old inline scripts.
- Reduce inline scripts. 151 inline scripts are the main obstacle to tightening CSP; move what can be externalised into files.
- Add CSP to security acceptance. Check default-src/script-src, unsafe-inline/unsafe-eval, nonce/hash and Report-Only; see the homepage security-header pilot. For website acceptance, see the website development service.
Limits
- No execution: actual browser blocking and Report-Only reporting are not tested.
- Homepage only: other paths may use different CSPs; meta parsing covers the common form only.
- Dynamic insertion excluded: inline scripts added by JavaScript are not in the HTML.
- One snapshot: 2026-11-08 16:41–16:43; CDN or configuration changes alter results.
- 25-site pilot: no industry claim.
Data and reproduction
Anonymised data: equipment-homepage-csp-2026.csv (25 rows × 13 columns: CSP presence, Report-Only, script directive, unsafe-inline, nonce/hash, inline scripts, nonce count, class, time). The repository keeps docs/research/2026-11-homepage-csp.csv.
Parser: scripts/csp_inline_probe.py; unit tests: scripts/test_content_csp_inline.py (16 tests, no network). Run:
python -m unittest scripts.test_content_csp_inline
Related: the homepage security-header pilot, the homepage blocking-resource pilot, and the homepage HTTPS certificate and TLS pilot.
Sources and definitions
- Sample: the 25 homepages from study 4 (same sample_id).
- Fetch: 2026-11-08 16:41–16:43 Asia/Shanghai, serial, 0.5 s apart, 15-second timeout.
- CSP: response header first, then meta; directives and tokens compared after removing quotes.
- Inline scripts: script tags without src; total and nonce-bearing counts.
Frequently asked questions
Does a CSP mean the page is secure?
No. Nine of the CSP sites set only frame-ancestors, block-all-mixed-content or upgrade-insecure-requests, with no script restriction.
What is wrong with * plus unsafe-inline in script-src?
It allows scripts from any origin and inline scripts to run, so XSS protection is effectively gone; unsafe-eval also permits string evaluation.
What does zero nonce/hash mean?
No site uses a nonce or hash allowlist; tightening CSP while keeping the current inline scripts usually requires template changes first.
Are 151 inline scripts a lot?
That is about six per site, with E04 alone at 41; the more inline scripts, the higher the cost of tightening script-src.
Do 25 sites represent the industry?
No. This is the same 25 equipment-company homepages from study 4; it describes only these pages.
Can the data be re-checked?
Yes. The public CSV has 25 rows by 13 columns; scripts/csp_inline_probe.py has 16 offline unit tests.