77interface HeadersConfig {
88 paths : Array < {
99 pattern : string ;
10- headers : Record < string , string | Record < string , string [ ] > > ;
10+ headers : Record < string , string [ ] | Record < string , string [ ] > > ;
1111 } > ;
1212}
1313
@@ -17,8 +17,10 @@ const config: HeadersConfig = {
1717 pattern : '/*' ,
1818 headers : {
1919 'Content-Security-Policy' : {
20+ // Allow AJAX/fetch requests to status page, marketing site, HubSpot,
21+ // and Google services for analytics and tag management
2022 'connect-src' : [
21- '\' self\'' ,
23+ "' self'" ,
2224 'https://status.maxmind.com' ,
2325 'https://www.maxmind.com' ,
2426 'https://api.hubspot.com' ,
@@ -30,37 +32,32 @@ const config: HeadersConfig = {
3032 'https://*.g.doubleclick.net' ,
3133 'https://*.google.com' ,
3234 ] ,
33- 'default-src' : [
34- '\'self\'' ,
35- ] ,
36- 'font-src' : [
37- '\'self\'' ,
38- 'https://fonts.gstatic.com' ,
39- ] ,
40- 'form-action' : [
41- '\'self\'' ,
42- ] ,
43- 'frame-ancestors' : [
44- '\'self\'' ,
45- ] ,
35+ // Fallback for resources not covered by other directives
36+ 'default-src' : [ "'self'" ] ,
37+ // Allow fonts from our site and Google Fonts
38+ 'font-src' : [ "'self'" , 'https://fonts.gstatic.com' ] ,
39+ // Only allow form submissions to our own domain
40+ 'form-action' : [ "'self'" ] ,
41+ // Prevent this site from being embedded in iframes on other domains
42+ 'frame-ancestors' : [ "'self'" ] ,
43+ // Allow embedding content from HubSpot and Google services
4644 'frame-src' : [
47- '\' self\'' ,
45+ "' self'" ,
4846 'https://app.hubspot.com' ,
4947 'https://www.google.com' ,
5048 'https://www.googletagmanager.com' ,
5149 ] ,
52- 'img-src' : [
53- '\'self\'' ,
54- 'data:' ,
55- 'https:' ,
56- ] ,
57- 'object-src' : [
58- '\'none\'' ,
59- ] ,
50+ // Allow images from our site, data URIs, and any HTTPS source
51+ 'img-src' : [ "'self'" , 'data:' , 'https:' ] ,
52+ // Block all plugins (Flash, Java, etc.)
53+ 'object-src' : [ "'none'" ] ,
54+ // Allow scripts from our site, HubSpot, Google services, and inline scripts
55+ // 'unsafe-inline' needed for HubSpot and Google Tag Manager
56+ // 'report-sample' includes script sample in violation reports
6057 'script-src' : [
61- '\' self\'' ,
62- '\' report-sample\'' ,
63- '\' unsafe-inline\'' ,
58+ "' self'" ,
59+ "' report-sample'" ,
60+ "' unsafe-inline'" ,
6461 'https://js.hs-scripts.com' ,
6562 'https://js.hs-analytics.net' ,
6663 'https://js.hs-banner.com' ,
@@ -72,35 +69,70 @@ const config: HeadersConfig = {
7269 'https://www.google.com' ,
7370 'https://*.googletagmanager.com' ,
7471 ] ,
72+ // Allow styles from our site, Google Fonts, and inline styles
73+ // 'unsafe-inline' needed for dynamic styling
7574 'style-src' : [
76- '\' self\'' ,
77- '\' unsafe-inline\'' ,
75+ "' self'" ,
76+ "' unsafe-inline'" ,
7877 'https://fonts.googleapis.com' ,
7978 'https://www.gstatic.com' ,
8079 ] ,
8180 } ,
82- 'Feature-Policy' :
83- 'accelerometer \'none\'; autoplay \'none\'; camera \'none\'; ' +
84- 'encrypted-media \'none\'; fullscreen \'none\'; geolocation \'none\'; ' +
85- 'gyroscope \'none\'; magnetometer \'none\'; microphone \'none\'; ' +
86- 'midi \'none\'; payment \'none\'; picture-in-picture \'none\'; ' +
87- 'usb \'none\'; sync-xhr \'none\'' ,
88- 'Permissions-Policy' :
89- 'accelerometer=(), ambient-light-sensor=(), autoplay=(), ' +
90- 'battery=(), camera=(), display-capture=(), document-domain=(), ' +
91- 'encrypted-media=(), execution-while-not-rendered=(), ' +
92- 'execution-while-out-of-viewport=(), fullscreen=(), gamepad=(), ' +
93- 'geolocation=(), gyroscope=(), hid=(), idle-detection=(), ' +
94- 'magnetometer=(), microphone=(), midi=(), payment=(), ' +
95- 'picture-in-picture=(), publickey-credentials-get=(), ' +
96- 'screen-wake-lock=(), serial=(), speaker-selection=(), usb=(), ' +
97- 'web-share=(), xr-spatial-tracking=()' ,
98- 'Referrer-Policy' : 'strict-origin-when-cross-origin' ,
99- 'Strict-Transport-Security' :
100- 'max-age=63072000; includeSubDomains; preload' ,
101- 'X-Content-Type-Options' : 'nosniff' ,
102- 'X-Frame-Options' : 'DENY' ,
103- 'X-XSS-Protection' : '1; mode=block' ,
81+ 'Feature-Policy' : [
82+ "accelerometer 'none'" ,
83+ "autoplay 'none'" ,
84+ "camera 'none'" ,
85+ "encrypted-media 'none'" ,
86+ "fullscreen 'none'" ,
87+ "geolocation 'none'" ,
88+ "gyroscope 'none'" ,
89+ "magnetometer 'none'" ,
90+ "microphone 'none'" ,
91+ "midi 'none'" ,
92+ "payment 'none'" ,
93+ "picture-in-picture 'none'" ,
94+ "usb 'none'" ,
95+ "sync-xhr 'none'" ,
96+ ] ,
97+ 'Permissions-Policy' : [
98+ 'accelerometer=()' ,
99+ 'ambient-light-sensor=()' ,
100+ 'autoplay=()' ,
101+ 'battery=()' ,
102+ 'camera=()' ,
103+ 'display-capture=()' ,
104+ 'document-domain=()' ,
105+ 'encrypted-media=()' ,
106+ 'execution-while-not-rendered=()' ,
107+ 'execution-while-out-of-viewport=()' ,
108+ 'fullscreen=()' ,
109+ 'gamepad=()' ,
110+ 'geolocation=()' ,
111+ 'gyroscope=()' ,
112+ 'hid=()' ,
113+ 'idle-detection=()' ,
114+ 'magnetometer=()' ,
115+ 'microphone=()' ,
116+ 'midi=()' ,
117+ 'payment=()' ,
118+ 'picture-in-picture=()' ,
119+ 'publickey-credentials-get=()' ,
120+ 'screen-wake-lock=()' ,
121+ 'serial=()' ,
122+ 'speaker-selection=()' ,
123+ 'usb=()' ,
124+ 'web-share=()' ,
125+ 'xr-spatial-tracking=()' ,
126+ ] ,
127+ 'Referrer-Policy' : [ 'strict-origin-when-cross-origin' ] ,
128+ 'Strict-Transport-Security' : [
129+ 'max-age=63072000' ,
130+ 'includeSubDomains' ,
131+ 'preload' ,
132+ ] ,
133+ 'X-Content-Type-Options' : [ 'nosniff' ] ,
134+ 'X-Frame-Options' : [ 'DENY' ] ,
135+ 'X-XSS-Protection' : [ '1' , 'mode=block' ] ,
104136 } ,
105137 } ,
106138 ] ,
0 commit comments