Defense-in-Depth HTTP Security Headers
Context
HTTP security headers are the first layer of defense against common web attacks: XSS (Cross-Site Scripting), clickjacking, MIME-type sniffing, and information leakage. Next.js provides no security headers by default — the developer must explicitly configure them in next.config.js. A portfolio site has a smaller attack surface than an authenticated application, but it's still publicly accessible and serves as a professional representation. A security vulnerability on a CTO's portfolio site would be particularly ironic and reputationally damaging. The security headers must balance protection against real-world attack vectors with compatibility across browsers and embedded contexts (social media previews, link unfurling bots, search engine rendering).
Decision
Configure seven security headers applied to all routes (/:path*) via next.config.js async headers. X-DNS-Prefetch-Control: on — enables DNS prefetching for faster navigation to linked domains. Strict-Transport-Security: max-age=63072000; includeSubDomains; preload — enforces HTTPS for 2 years across all subdomains, with HSTS preload eligibility. X-Content-Type-Options: nosniff — prevents MIME-type sniffing attacks where browsers reinterpret response content types. X-Frame-Options: SAMEORIGIN — prevents clickjacking by disallowing iframe embedding from other origins while allowing same-origin embedding. X-XSS-Protection: 1; mode=block — enables legacy XSS filter in older browsers (modern browsers use CSP instead, but this provides backward compatibility). Referrer-Policy: strict-origin-when-cross-origin — sends full referrer for same-origin requests, origin-only for cross-origin requests, protecting URL paths from leaking to external sites. Permissions-Policy: camera=(), microphone=(), geolocation=(), interest-cohort=() — explicitly disables unnecessary browser APIs and opts out of FLoC/Topics API tracking.
Consequences
Positive: SecurityHeaders.com score of A+. HSTS preload eligibility protects against SSL stripping attacks and ensures all requests use HTTPS after the first visit. nosniff prevents a class of attacks where malicious content disguises its MIME type. SAMEORIGIN frame protection prevents the site from being embedded in phishing pages while allowing legitimate same-origin embedding. The Permissions-Policy explicitly opts out of surveillance capitalism APIs (FLoC/Topics) — a principled stance for a privacy-aware technologist. The configuration is applied globally via next.config.js, so new pages automatically inherit all headers. Negative: X-Frame-Options: SAMEORIGIN prevents the site from being embedded in third-party tools that might legitimately want to preview it (e.g., link unfurling services that render in iframes). However, most modern unfurling services use server-side rendering, so this is rarely an issue. The HSTS preload submission is irreversible at the browser level — once a domain is on the preload list, removing HTTPS support would break the site for all users with updated preload lists. The X-XSS-Protection header is technically deprecated in modern browsers and can cause false positives in edge cases, but the mode=block directive minimizes this risk. No Content-Security-Policy (CSP) header is configured — this is a deliberate omission because styled-components generates inline styles that would require 'unsafe-inline' in the CSP, negating much of its XSS protection value.
Predictions at Decision Time
Expected a SecurityHeaders.com score of A or higher. Predicted the headers would not break any legitimate functionality (social media previews, search engine rendering, browser compatibility). Assumed the missing CSP header would not be a significant security gap for a static portfolio without user-generated content. Predicted the HSTS preload commitment would be acceptable given no plans to ever revert to HTTP.
Measured Outcomes
SecurityHeaders.com score is A+ as targeted. No legitimate functionality has been broken — social media previews (Twitter, LinkedIn, Slack) work correctly because they use server-side rendering for unfurling, not iframe embedding. Search engines render the site correctly despite the headers. The missing CSP has not been exploited (no XSS vectors exist on a static site without user input), confirming the prediction. HSTS preload commitment is comfortable — there is zero scenario where the site would revert to HTTP. The unexpected observation: the Permissions-Policy header disabling interest-cohort=() is now partially redundant because Google deprecated FLoC in favor of Topics API, but the header still serves as a documented privacy stance.
Unknowns at Decision Time
Did not know at decision time whether CSP nonce-based approach would become compatible with styled-components' runtime injection model. As of early 2026, styled-components v6 has experimental CSP nonce support, but the portfolio uses v5.3 and migration would require significant effort. Also unknown: whether X-XSS-Protection would be removed from browser implementations entirely, potentially causing unexpected behavior. Chrome has already removed it, but the header is harmless when unsupported. Unknown: whether future portfolio features (comment system, contact form with file upload) would require relaxing any of these headers.
Reversibility Classification
Each header is independently removable by editing the securityHeaders array in next.config.js. Adding, modifying, or removing individual headers requires a single line change plus redeploy. The only exception is HSTS preload: once submitted to the preload list, removal requires a formal delisting process that takes months. However, the HSTS header itself can be removed immediately — the preload list effect persists independently in browser distributions. Estimated effort: 5 minutes for any header change.
Strongest Counter-Argument
For a static portfolio site with zero user input and no authentication, the security header configuration is over-engineered. The actual attack surface is minimal: there are no forms to exploit (except a contact form that submits to a third-party), no cookies to steal, no sessions to hijack. The headers protect against theoretical attacks on a site with no practical attack vectors. A simpler approach: rely on Vercel's default headers and Next.js's built-in protections. The counter-counter: security headers are cheap insurance — the configuration took 30 minutes to implement and adds zero runtime cost. More importantly, for a CTO's portfolio, demonstrating security awareness through headers is a professional signal, not just a technical measure.
Technical Context
- No CSP due to styled-components inline styles
- HSTS preload is effectively irreversible
- X-Frame-Options may block legitimate embedding