Back to Blog
Lesson 14 of the Cloudflare: Cloudflare for Developers: DNS to CDN course
Cloud NativeJuly 15, 20263 min read

Dynamic Header Manipulation: Securing Apps with Cloudflare Workers

Learn how to use Cloudflare Workers to dynamically inject security headers like HSTS and CSP. Master request interception to harden your web applications today.

CloudflareWorkersSecurityHTTPHeaders
A hand holding a smartphone displaying a VPN app screen for secure online browsing.

Previously in this course, we covered handling HTTP requests, where you learned how to access headers and return custom status codes. Now, we’ll move from basic manipulation to proactive security by using Workers as middleware to inject essential security headers into every response.

Why Dynamic Header Manipulation?

When you host an application, your origin server might be behind an older stack or misconfigured, leaving gaps in your security posture. While you could configure these at the origin (as discussed in Nginx Security Headers: A Practical Guide to Hardening Your Server), doing it at the edge via Workers ensures these headers are applied globally, consistently, and independently of your backend framework.

Headers act as instructions for the browser, telling it how to handle your site's content safely. By injecting them at the edge, you ensure that even if your origin fails to send a security policy, the browser still receives one.

First Principles: The Response Object

In a Cloudflare Worker, the Response object is immutable by default. To modify headers, you must create a new Response object based on the original, passing in a new Headers object.

Here is the flow:

  1. Fetch the original response from the origin.
  2. Clone the response headers.
  3. Add or overwrite security headers.
  4. Return the new Response to the browser.

Worked Example: Implementing Security Headers

We will implement two critical security headers:

  • Strict-Transport-Security (HSTS): Forces the browser to use HTTPS.
  • Content-Security-Policy (CSP): Restricts the sources from which content (scripts, images) can be loaded, mitigating XSS risks.
JAVASCRIPT
export default {
  async fetch(request, env, ctx) {
    // 1. Get the original response from the origin
    const response = await fetch(request);

    // 2. Create a new Headers object from the original response
    const newHeaders = new Headers(response.headers);

    // 3. Inject Security Headers
    newHeaders.set("Strict-Transport-Security", "max-age=63072000; includeSubDomains; preload");
    newHeaders.set("Content-Security-Policy", "default-src CE9178">'self'; script-src CE9178">'self' https://trusted.cdn.com;");
    newHeaders.set("X-Content-Type-Options", "nosniff");

    // 4. Return the modified response
    return new Response(response.body, {
      status: response.status,
      statusText: response.statusText,
      headers: newHeaders,
    });
  },
};

Hands-on Exercise

  1. Deploy: Update your current Worker project from the previous lesson to include the code snippet above.
  2. Verify: Open your browser's Developer Tools (F12) and navigate to the Network tab.
  3. Inspect: Refresh your page and click on the initial document request. Look at the "Response Headers" section. You should see Strict-Transport-Security and Content-Security-Policy present.
  4. Test: Try changing a header value (e.g., change max-age to 3600) and redeploy. Refresh the browser to see the updated header reflected in real-time.

Common Pitfalls

  • Overwriting vs. Appending: Use .set() if you want to ensure a header exists and has exactly one value. Use .append() if you need to add multiple values to the same header key (like Set-Cookie).
  • CSP Complexity: CSP is powerful but strict. If your site uses third-party analytics or fonts, a generic 'self' policy will break them. Always test in "Report-Only" mode first using Content-Security-Policy-Report-Only before enforcing a strict policy.
  • Immutable Responses: Remember that response.headers.set(...) will throw an error because the response object returned by fetch() is read-only. Always construct a new Response as shown in the example.

FAQ

Can I remove headers from the origin? Yes. Use newHeaders.delete("X-Powered-By") to strip sensitive information about your backend technology stack before the response leaves the edge.

Does this affect performance? Minimal impact. Workers execute at the edge, and header manipulation is a lightweight operation. It is significantly faster than proxying through an origin server that requires complex Nginx or Apache configuration.

Do these headers work for static assets? Yes, but be careful. If you apply CSP to every request, you might break image loading or CSS files. You can add an if condition to your Worker to only apply specific headers to text/html responses.

Recap

We've moved from simple request handling to active security hardening. By using Headers manipulation, you've gained the ability to enforce modern security standards like HSTS and CSP globally. This approach ensures your application remains secure regardless of the underlying infrastructure.

Up next: Debugging Workers with Wrangler — learn how to troubleshoot your edge logic effectively.

Similar Posts