<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jeff W</title>
    <description>The latest articles on DEV Community by Jeff W (@whitmojm).</description>
    <link>https://dev.to/whitmojm</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4100378%2Ffc0ad977-95d6-45fc-bd4e-943f72925db6.png</url>
      <title>DEV Community: Jeff W</title>
      <link>https://dev.to/whitmojm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/whitmojm"/>
    <language>en</language>
    <item>
      <title>6 HTTP Security Headers Your Site Is Probably Missing (and how to fix each one)</title>
      <dc:creator>Jeff W</dc:creator>
      <pubDate>Sat, 29 Aug 2026 15:10:25 +0000</pubDate>
      <link>https://dev.to/whitmojm/6-http-security-headers-your-site-is-probably-missing-and-how-to-fix-each-one-52io</link>
      <guid>https://dev.to/whitmojm/6-http-security-headers-your-site-is-probably-missing-and-how-to-fix-each-one-52io</guid>
      <description>&lt;p&gt;Run any random production site through a headers check and odds are it's missing at least half of the headers below. Not because anyone decided against them — usually because nobody ever added them in the first place. They cost nothing, take one line to set, and most frameworks don't set them for you by default.&lt;/p&gt;

&lt;p&gt;Here's what each one actually does, and the one-liner to add it in a few common stacks.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Strict-Transport-Security (HSTS)
&lt;/h2&gt;

&lt;p&gt;Tells the browser "never connect to this site over plain HTTP again, even if someone types &lt;code&gt;http://&lt;/code&gt; or links to it that way." Without it, every visit is one stray link or bookmark away from a downgrade-to-HTTP attack.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Strict-Transport-Security: max-age=63072000; includeSubDomains
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Express (helmet):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;helmet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hsts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;maxAge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;63072000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;includeSubDomains&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;nginx:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Strict-Transport-Security&lt;/span&gt; &lt;span class="s"&gt;"max-age=63072000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;includeSubDomains"&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Content-Security-Policy (CSP)
&lt;/h2&gt;

&lt;p&gt;The single biggest lever against XSS. It tells the browser exactly which origins are allowed to supply scripts, styles, images, etc. — so even if an attacker manages to inject a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag somewhere, the browser refuses to run it if it's not from an allowed source.&lt;/p&gt;

&lt;p&gt;It's also the one people avoid because a bad policy breaks your own site. Start narrow and loosen it as you find real violations (report-only mode is your friend here):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. X-Frame-Options
&lt;/h2&gt;

&lt;p&gt;Stops your pages from being loaded inside a hidden &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; on someone else's site — the setup behind clickjacking ("click here to win a prize" that's secretly your bank's transfer-confirmation button underneath).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;X-Frame-Options: DENY
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(CSP's &lt;code&gt;frame-ancestors&lt;/code&gt; directive supersedes this in modern browsers, but set both — older clients don't understand &lt;code&gt;frame-ancestors&lt;/code&gt;.)&lt;/p&gt;

&lt;h2&gt;
  
  
  4. X-Content-Type-Options
&lt;/h2&gt;

&lt;p&gt;One value, always the same:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;X-Content-Type-Options: nosniff
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without it, some browsers will "sniff" a response's actual content and decide to treat it as something other than what the &lt;code&gt;Content-Type&lt;/code&gt; header says — the classic version being an uploaded file that's technically an image but gets executed as script.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Referrer-Policy
&lt;/h2&gt;

&lt;p&gt;Controls how much of your own URL — including query strings, which sometimes contain tokens or IDs you didn't mean to share — gets sent to whatever site a visitor clicks away to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Referrer-Policy: strict-origin-when-cross-origin
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A reasonable default: full URL on same-origin navigation, just the origin (no path/query) on cross-origin ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Permissions-Policy
&lt;/h2&gt;

&lt;p&gt;Explicitly turns off browser features your site never uses — camera, microphone, geolocation, USB, etc. Even if you never call these APIs yourself, a compromised third-party script embedded on your page could try to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Permissions-Policy: camera=(), microphone=(), geolocation=()
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bonus: stop announcing your stack
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Server: nginx/1.18.0&lt;/code&gt; and &lt;code&gt;X-Powered-By: Express&lt;/code&gt; don't do anything for your visitors — they just hand an attacker a head start on which known vulnerabilities to try first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server_tokens&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-powered-by&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Express&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Checking your own site
&lt;/h2&gt;

&lt;p&gt;Reading headers off a raw response is annoying to do by hand every time — &lt;code&gt;curl -I&lt;/code&gt; and then squinting for six specific header names. I built a small free tool that just does the check and tells you what's missing and why each one matters:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.shieldingress.com/security-headers-checker.html" rel="noopener noreferrer"&gt;Security Headers Checker&lt;/a&gt;&lt;/strong&gt; — no signup, doesn't store anything you enter, checks all six headers above plus flags the &lt;code&gt;Server&lt;/code&gt;/&lt;code&gt;X-Powered-By&lt;/code&gt; leaks.&lt;/p&gt;

&lt;p&gt;(Disclosure: I built this — it's a free tool from &lt;a href="https://www.shieldingress.com" rel="noopener noreferrer"&gt;ShieldIngress&lt;/a&gt;, a WAF/edge security product I run. The checker itself has no paywall or catch, I'd genuinely rather more sites just have these headers set.)&lt;/p&gt;

&lt;p&gt;If you run it against your own site and find gaps, the snippets above should cover fixing most of them in a few minutes.&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>node</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
