This guide provides a comprehensive step-by-step process to verify successful implementation of Content Security Policy headers and additional security headers in the Hunty application.
The CSP headers have been added to next.config.ts via the headers() API with the following features:
-
Report-Only Mode (Staging): Logs CSP violations without blocking resources
-
Enforcement Mode (Production): Blocks resources that violate the policy
-
Trusted Sources:
self- Same origin- Soroban RPC endpoints (testnet and mainnet)
- IPFS Gateways (Pinata, Cloudflare, dweb.link, ipfs.io)
- Resend API (email service)
- Torii Indexer APIs
-
Additional Headers:
X-Frame-Options: DENY- Prevents clickjackingX-Content-Type-Options: nosniff- Prevents MIME type sniffingX-XSS-Protection: 1; mode=block- Legacy XSS protectionReferrer-Policy: strict-origin-when-cross-origin- Referrer controlPermissions-Policy- Restricts geolocation, microphone, camera
cd /workspaces/hunty
pnpm installpnpm devExpected Output:
▲ Next.js 15.3.4
- Local: http://localhost:3000
Why this step matters: This ensures the application compiles without errors and the headers() configuration is valid.
curl -I http://localhost:3000Expected Output:
HTTP/1.1 200 OK
...
Content-Security-Policy-Report-Only: script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https: https://gateway.pinata.cloud https://*.mypinata.cloud https://cloudflare-ipfs.com https://dweb.link https://ipfs.io; connect-src 'self' https://api.resend.com https://torii-indexer.stellar-mainnet.public.blastapi.io https://indexer.testnet.torii.com https://soroban-testnet.stellar.org https://rpc.testnet.soroban.stellar.org https://soroban-mainnet.stellar.org https://rpc.mainnet.soroban.stellar.org wss: https:; font-src 'self' data: https:; frame-ancestors 'none'; default-src 'self'; base-uri 'self'; form-action 'self'
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(self), microphone=(), camera=()
Verification Checklist:
-
Content-Security-Policy-Report-Onlyheader is present (dev mode) - All Soroban RPC endpoints are included
- All IPFS gateways are listed
-
X-Frame-Options: DENYis present -
X-Content-Type-Options: nosniffis present -
X-XSS-Protection: 1; mode=blockis present -
Referrer-Policy: strict-origin-when-cross-originis present -
Permissions-Policyis configured correctly
curl -I http://localhost:3000 | grep -E "Content-Security-Policy|X-Frame-Options|X-Content-Type-Options"Expected Output:
Content-Security-Policy-Report-Only: script-src 'self' 'unsafe-inline' 'unsafe-eval'; ...
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Steps:
- Open the application in Chrome:
http://localhost:3000 - Open DevTools:
F12orCtrl+Shift+I(Windows/Linux) orCmd+Option+I(Mac) - Go to the Network tab
- Refresh the page:
Ctrl+R(Windows/Linux) orCmd+R(Mac) - Click on the main document request (usually the first one)
- Go to the Response Headers section
Expected Headers Visible:
content-security-policy-report-onlyx-content-type-options: nosniffx-frame-options: DENYreferrer-policy: strict-origin-when-cross-originpermissions-policy: geolocation=(self), microphone=(), camera=()
Verification Checklist:
- All security headers are visible in Response Headers
- CSP is in report-only mode (in development)
- No resources are blocked (all load successfully)
Steps:
- Open the application in Safari
- Enable DevTools: Develop menu (Develop > Show Web Inspector) or
Cmd+Option+U - Go to the Network tab
- Refresh the page:
Cmd+R - Click on the main document request
- View the Response Headers
Expected: Same headers as Chrome
Steps:
- Open the application in Firefox:
http://localhost:3000 - Open DevTools:
F12orCtrl+Shift+I(Windows/Linux) orCmd+Option+I(Mac) - Go to the Inspector tab → Network subtab
- Refresh the page:
Ctrl+RorCmd+R - Click the main document request
- View Response Headers
Expected: Same headers as Chrome
Action: Navigate to a page that loads images from IPFS gateways
- Load the Hunt Cards component or any page with IPFS images
- Open DevTools Network tab
- Look for requests to:
gateway.pinata.cloudcloudflare-ipfs.comdweb.linkipfs.io
Expected: Images load successfully with HTTP 200 status
Verification Checklist:
- IPFS images load without CSP violations
- No warnings in console about blocked resources
Action: Connect wallet and perform a blockchain operation
- Click "Connect Wallet" button
- Open DevTools Console
- Monitor Network tab for Soroban RPC calls
- Look for requests to
soroban-testnet.stellar.orgor similar
Expected: Blockchain calls succeed without CSP violations
Verification Checklist:
- Wallet connects successfully
- Blockchain calls reach the Soroban RPC endpoint
- No CSP-related errors in console
Steps:
- Keep DevTools open in Console tab
- Interact with the application
- Look for messages like:
[Report Only] Refused to load the script because...
Expected in Report-Only Mode: No CSP violations reported for legitimate resources
Expected Violations (should be blocked in production):
- Scripts from unauthorized external sources
- Styles from unauthorized external sources
- Connections to unauthorized APIs
cd /workspaces/hunty
pnpm build
pnpm startExpected Output:
▲ Next.js 15.3.4
- Listening on 0.0.0.0:3000
Steps:
-
Set production environment:
NODE_ENV=production pnpm start
-
Check headers:
curl -I http://localhost:3000
Expected Output:
Content-Security-Policy: script-src 'self' 'unsafe-inline' 'unsafe-eval'; ...
Note: Header should now be Content-Security-Policy (not Report-Only)
Verification Checklist:
-
Content-Security-Policyheader present (not Report-Only) - All security headers still present
- Application functions normally with enforcement mode
# Development mode uses report-only by default
pnpm devExpected:
- Header:
Content-Security-Policy-Report-Only - Violations logged but not blocked
- All resources load successfully
NODE_ENV=production pnpm startExpected:
- Header:
Content-Security-Policy - Policy actively enforced
- Unauthorized resources blocked
NODE_ENV=production CSP_REPORT_ONLY=true pnpm startExpected:
- Header:
Content-Security-Policy-Report-Only - Even in production, violations are logged but not blocked
- Allows safe monitoring before enforcement
cd /workspaces/hunty
pnpm test:e2eExpected: All existing E2E tests pass with CSP headers active
What to verify:
-
test:dashboardpasses (no CSP violations) -
test:hunt-creationpasses (IPFS uploads work) -
test:claim-rewardpasses (blockchain calls work) -
test:wallet-connectionpasses (Freighter integration works)
pnpm testExpected: All unit tests pass
# Open Chrome DevTools while running the application
# Go to Lighthouse tab
# Run audit for "Performance", "Security", "Best Practices"Expected:
- Security score: 90+
- No CSP-related issues reported
Using:
- OWASP ZAP (free security scanner)
- Burp Suite (free community edition)
Steps:
- Run security scanner on
http://localhost:3000 - Check for CSP-related findings
- Verify no high-risk security issues
Steps:
- Open DevTools Console
- Interact with the application
- Look for messages:
Refused to load the script from ...(report-only mode will show)Refused to load the stylesheet from ...Refused to connect to ... because it violates the following CSP directive
Expected in Development:
- No violations from legitimate resources
- Any violations should be from external/unauthorized sources
Add this to your app for monitoring (in production):
// app/providers.tsx or similar
if (typeof window !== 'undefined') {
window.addEventListener('securitypolicyviolation', (e) => {
console.warn('CSP Violation:', {
blockedURI: e.blockedURI,
violatedDirective: e.violatedDirective,
sourceFile: e.sourceFile,
lineNumber: e.lineNumber,
});
});
}Navigate through the application and verify these work with CSP enabled:
Hunt Management:
- Create a new hunt
- Upload images to IPFS (via hunt creation)
- View hunt details
- IPFS images load correctly
Player Experience:
- Register for a hunt
- Submit clues
- Complete a hunt
- View leaderboard
Wallet & Blockchain:
- Connect Freighter wallet
- View wallet balance
- Initiate blockchain transactions
- Claim NFT rewards
Admin Features:
- Access dashboard
- View analytics
- Manage hunts
Verification Checklist:
- All features work without console errors
- No CSP violations reported
- Images from all IPFS gateways load
- Blockchain calls reach Soroban endpoints
-
Chrome/Edge (latest)
# Already tested -
Firefox (latest)
# Test by opening http://localhost:3000 in Firefox -
Safari (latest on macOS/iOS)
# Test by opening http://localhost:3000 in Safari -
Mobile Browsers (iOS Safari, Chrome Android)
# Get local IP: ipconfig getifaddr en0 (macOS) or hostname -I (Linux) # Access via: http://<LOCAL_IP>:3000 from mobile device
Verification Checklist for Each Browser:
- All headers present
- No console errors
- All features functional
- Images load correctly
cd /workspaces/hunty
cat next.config.ts | grep -A 50 "async headers()"Expected: Should see:
- CSP header configuration
- Soroban RPC endpoints
- IPFS gateways
- Resend API
- X-Frame-Options
- X-Content-Type-Options
git diff next.config.tsExpected: Shows new headers() function with all security configurations
Possible Causes:
- Application not running with updated config
- Cache not cleared
Solution:
# Stop the dev server (Ctrl+C)
# Clear Next.js cache
rm -rf .next
# Restart
pnpm devSymptoms:
- Images not loading
- API calls failing
- Console shows CSP violation warnings
Solution:
- Identify the blocked resource URL
- Add to appropriate CSP directive in
next.config.ts - Rebuild and test
Solution:
# Verify NODE_ENV
echo $NODE_ENV
# Should be empty (development) or "development"
# NOT "production"Solution:
- Check the actual RPC URL being used
- Verify it's in the
sorobanRpcEndpointsarray - Add if missing
-
Content-Security-PolicyorContent-Security-Policy-Report-Only -
X-Frame-Options: DENY -
X-Content-Type-Options: nosniff -
X-XSS-Protection: 1; mode=block -
Referrer-Policy: strict-origin-when-cross-origin -
Permissions-Policyconfigured
-
script-src- Contains self and necessary scripts -
style-src- Contains self and inline styles -
img-src- Contains self, data URLs, and IPFS gateways -
connect-src- Contains self, Soroban RPC, APIs -
font-src- Contains self and font sources -
frame-ancestors 'none'- Prevents clickjacking -
default-src 'self'- Safe default
- All pages load without errors
- IPFS images display correctly
- Blockchain operations work
- Wallet connection successful
- All E2E tests pass
- Console is clean (no CSP violations for legitimate resources)
- Development = Report-Only mode
- Production = Enforcement mode
- CSP_REPORT_ONLY=true = Report-Only even in production (safe rollout)
This CSP implementation protects the Hunty application from:
✅ Script Injection Attacks - Only self-hosted scripts and whitelisted origins
✅ Data Exfiltration - Limited connection sources
✅ Clickjacking - X-Frame-Options prevents framing
✅ MIME Type Sniffing - X-Content-Type-Options prevents misinterpretation
✅ XSS Attacks - XSS protection headers in place
The staged rollout approach ensures:
- ✅ Report-only mode captures violations without breaking functionality
- ✅ Smooth transition to enforcement after monitoring
- ✅ All legitimate operations (blockchain, IPFS, emails) continue working
For issues or questions about CSP:
- Check the Troubleshooting Guide above
- Review OWASP CSP documentation: https://owasp.org/www-community/attacks/xss/#prevention-measures
- Reference MDN CSP Guide: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP