Skip to content

fix(cloudflare): Filter inherited values from Cloudflare preset _headers file#3938

Open
gapple wants to merge 3 commits intonitrojs:v2from
gapple:cf-headers
Open

fix(cloudflare): Filter inherited values from Cloudflare preset _headers file#3938
gapple wants to merge 3 commits intonitrojs:v2from
gapple:cf-headers

Conversation

@gapple
Copy link

@gapple gapple commented Jan 11, 2026

🔗 Linked issue

#2909
Baroshem/nuxt-security#575

❓ Type of change

  • 📖 Documentation (updates to the documentation, readme, or JSdoc annotations)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

Cloudflare's _header file merges all rules that apply to a path, resulting in duplicated or conflicting values. This may prevent a browser from parsing the header value, or to interpret the value incorrectly.
https://developers.cloudflare.com/workers/static-assets/headers/#attach-a-header

Examples

Nuxt provides some nested wildcard path rules. Responses at increasing path depth receive successively appended values.

/_nuxt/builds/meta/*
  cache-control: public, max-age=31536000, immutable
/_nuxt/builds/*
  cache-control: public, max-age=1, immutable
/_nuxt/*
  cache-control: public, max-age=31536000, immutable

A route matching /_nuxt/builds/meta/* will receive all three values appended:

cache-control: public, max-age=31536000, immutable, public, max-age=1, immutable, public, max-age=31536000, immutable

nuxt-security defines some headers to apply to all paths, and also defines those values for each prerendered page.

/
  referrer-policy: no-referrer
  strict-transport-security: max-age=31536000; includeSubDomains; preload;
  content-security-policy: [...]
/*
  Referrer-Policy: no-referrer
  Strict-Transport-Security: max-age=31536000; includeSubDomains; preload;

Will result in the / path receiving the header values:

  referrer-policy: no-referrer, no-referrer
  strict-transport-security: max-age=31536000; includeSubDomains; preload;, max-age=31536000; includeSubDomains; preload;
  content-security-policy: [...]

The change

This PR first builds all routeRules that include headers into a tree before processing the values to be output. This allows checking path rules against matching wildcard rules in order to either:

  • Omit the header value for a path if it matches the closest matching wildcard rule, or
  • First detach the more general rule before defining the value for the path.

With this PR the above examples output:

/_nuxt/*
  cache-control: public, max-age=31536000, immutable
/_nuxt/builds/*
  ! cache-control
  cache-control: public, max-age=1, immutable
/_nuxt/builds/meta/*
  ! cache-control
  cache-control: public, max-age=31536000, immutable
/*
  Referrer-Policy: no-referrer
  Strict-Transport-Security: max-age=31536000; includeSubDomains; preload;
/
  content-security-policy: [...]

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

@gapple gapple requested a review from pi0 as a code owner January 11, 2026 06:06
@vercel
Copy link

vercel bot commented Jan 11, 2026

@gapple is attempting to deploy a commit to the Nitro Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link

coderabbitai bot commented Jan 11, 2026

📝 Walkthrough

Walkthrough

A tree-based header processing system is introduced for the Cloudflare preset. A new CloudflareHeaderTreeNode type is added to support hierarchical header organization. The writeCFHeaders function is refactored to build and traverse a header tree structure instead of using ad-hoc assembly. Tests validate the generated _headers file output.

Changes

Cohort / File(s) Summary
Type Definition
src/presets/cloudflare/types.ts
Adds CloudflareHeaderTreeNode type with fields for path, wildcard headers, per-path headers, and nested children nodes.
Header Processing Refactor
src/presets/cloudflare/utils.ts
Replaces manual header assembly with tree-based approach: introduces buildHeaderTree to construct hierarchical structure from route rules, processHeaderTree for recursive traversal, and processHeaderValues for header diffing and formatting. Updates writeCFHeaders to use new tree generator.
Test Coverage
test/presets/cloudflare-module.test.ts
Extends test scaffolding with routeRules configuration and adds test case validating generated _headers file content against snapshot, including header inheritance and override behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title follows conventional commits format with 'fix' type and includes a specific, descriptive scope about filtering inherited values from the Cloudflare _headers file.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, explaining the problem, rationale, examples, and solution for filtering inherited header values in Cloudflare preset generation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gapple
Copy link
Author

gapple commented Jan 29, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jan 29, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/presets/cloudflare/utils.ts`:
- Around line 130-134: When creating a new child node in the header tree,
initialize all required CloudflareHeaderTreeNode fields: include wildcardHeaders
and headers (both as empty arrays/objects as appropriate) along with path and
children; update the node creation at currentNode.children[part] to set path,
children, wildcardHeaders, and headers so the new node conforms to
CloudflareHeaderTreeNode and passes strict TS type-checking.

Comment on lines +130 to +134
currentNode.children[part] = currentNode.children[part] || {
path: (currentNode.path == "/" ? "" : currentNode.path) + "/" + part,
children: {},
};
currentNode = currentNode.children[part];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Initialize required fields for child nodes.

CloudflareHeaderTreeNode requires wildcardHeaders and headers; omitting them will fail type-checking in strict TS setups.

🛠️ Proposed fix
       currentNode.children[part] = currentNode.children[part] || {
         path: (currentNode.path == "/" ? "" : currentNode.path) + "/" + part,
+        wildcardHeaders: undefined,
+        headers: undefined,
         children: {},
       };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
currentNode.children[part] = currentNode.children[part] || {
path: (currentNode.path == "/" ? "" : currentNode.path) + "/" + part,
children: {},
};
currentNode = currentNode.children[part];
currentNode.children[part] = currentNode.children[part] || {
path: (currentNode.path == "/" ? "" : currentNode.path) + "/" + part,
wildcardHeaders: undefined,
headers: undefined,
children: {},
};
currentNode = currentNode.children[part];
🤖 Prompt for AI Agents
In `@src/presets/cloudflare/utils.ts` around lines 130 - 134, When creating a new
child node in the header tree, initialize all required CloudflareHeaderTreeNode
fields: include wildcardHeaders and headers (both as empty arrays/objects as
appropriate) along with path and children; update the node creation at
currentNode.children[part] to set path, children, wildcardHeaders, and headers
so the new node conforms to CloudflareHeaderTreeNode and passes strict TS
type-checking.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant