Skip to content

api: add ClientTrafficPolicy option to emit Retry-After header on rate-limit 429 responses - #9770

Open
devilleweppenaar wants to merge 12 commits into
envoyproxy:mainfrom
devilleweppenaar:retry-after-header-ctp
Open

api: add ClientTrafficPolicy option to emit Retry-After header on rate-limit 429 responses#9770
devilleweppenaar wants to merge 12 commits into
envoyproxy:mainfrom
devilleweppenaar:retry-after-header-ctp

Conversation

@devilleweppenaar

@devilleweppenaar devilleweppenaar commented Aug 17, 2026

Copy link
Copy Markdown

What this PR does / why we need it:

Envoy's global and local rate limit HTTP filters now support enable_retry_after_header (envoyproxy/envoy#46289), emitting a Retry-After header on rate-limited 429 responses. This adds ClientTrafficPolicy.spec.headers.enableRetryAfterHeader (bool, default false) to expose it, mirroring the existing disableRateLimitHeaders field end to end (API type -> IR -> both rate limit filter translators).

Requires bumping go-control-plane to a commit that includes the new field; the three companion go.mod/go.sum commits (root, examples/extension-server, test) are that bump propagating to each module in the repo.

Per-rule granularity (mirroring xRateLimitHeaders, #8279) is tracked separately in #9078 and needs further upstream Envoy work; out of scope here.

Which issue(s) this PR fixes:

Fixes #9769


PR Checklist

  • Authorship & ownership: Coding agents / AI assistants are welcome, but I have reviewed every change, understand how and why it works, can explain and maintain it, and take full responsibility for this PR. I have not submitted generated output I do not understand.
  • DCO: All commits are signed off (git commit -s).
  • API agreed first: Not yet explicitly agreed by a maintainer. The proposed shape was posted in ClientTrafficPolicy: option to emit Retry-After header on rate-limit 429 responses #9769 prior to implementation and mirrors already-accepted precedent (disableRateLimitHeaders, Feature Request: Granular Control for X-RateLimit Headers in BackendTrafficPolicy #8279); open to adjusting on review.
  • Required checks pass: make generate gen-check, make lint, and go test ./... pass locally.
  • Tests added/updated: gatewayapi golden-file case plus xds translator golden-file cases for both the global and local rate limit filters.
  • Docs: API reference doc is auto-generated (make generate); no additional user-facing docs page exists for the sibling disableRateLimitHeaders field either.
  • Release notes: added under release-notes/current/new_features/.
  • Generated files committed: CRDs, deepcopy, helm-template golden output all regenerated and committed.
  • Scope & compatibility: additive, opt-in, default false.
  • Codex review: not yet requested.
  • Copilot review: not yet requested.

Bumps github.com/envoyproxy/go-control-plane/envoy to a commit built
after envoyproxy/envoy#46289 merged, which added
enable_retry_after_header to the global and local rate limit HTTP
filter protos. Needed before those fields can be referenced from Go.

extensions.gen.go picks up a few newly available extension packages
as an incidental side effect of the bump; unrelated to this change.

Signed-off-by: De Ville Weppenaar <11049609+devilleweppenaar@users.noreply.github.com>
Adds ClientTrafficPolicy.spec.headers.enableRetryAfterHeader, mirroring
the existing disableRateLimitHeaders field. Defaults to false.

Fixes envoyproxy#9769

Signed-off-by: De Ville Weppenaar <11049609+devilleweppenaar@users.noreply.github.com>
Threads ClientTrafficPolicy.spec.headers.enableRetryAfterHeader through
the IR and sets enable_retry_after_header on the generated global and
local rate limit HTTP filter configs, mirroring how
disableRateLimitHeaders is wired to enable_x_ratelimit_headers.

Signed-off-by: De Ville Weppenaar <11049609+devilleweppenaar@users.noreply.github.com>
Adds a ClientTrafficPolicy golden-file case exercising the new field
through the CRD-to-IR translation, and two xds-ir golden-file cases
covering the global and local rate limit filters.

Signed-off-by: De Ville Weppenaar <11049609+devilleweppenaar@users.noreply.github.com>
Signed-off-by: De Ville Weppenaar <11049609+devilleweppenaar@users.noreply.github.com>
Follow-up to the enableRetryAfterHeader CRD field: make helm-template
picks up the new field in the rendered gateway-crds-helm test fixtures.

Signed-off-by: De Ville Weppenaar <11049609+devilleweppenaar@users.noreply.github.com>
Companion to the root module bump so 'make gen-check' stays clean.

Signed-off-by: De Ville Weppenaar <11049609+devilleweppenaar@users.noreply.github.com>
Companion to the root module bump so the test module's go.mod stays
tidy and 'make lint' can load it.

Signed-off-by: De Ville Weppenaar <11049609+devilleweppenaar@users.noreply.github.com>
@devilleweppenaar
devilleweppenaar requested a review from a team as a code owner August 17, 2026 20:07
@netlify

netlify Bot commented Aug 17, 2026

Copy link
Copy Markdown

Deploy Preview for cerulean-figolla-1f9435 ready!

Name Link
🔨 Latest commit a21c7cf
🔍 Latest deploy log https://app.netlify.com/projects/cerulean-figolla-1f9435/deploys/6a93cd668bf4c300089bbbe2
😎 Deploy Preview https://deploy-preview-9770--cerulean-figolla-1f9435.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 29a11ce516

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread api/v1alpha1/clienttrafficpolicy_types.go
Match the disableRateLimitHeaders convention instead of restating the
default, which the boolean type and the generated docs table already
convey.

Signed-off-by: De Ville Weppenaar <11049609+devilleweppenaar@users.noreply.github.com>
Adds conformance coverage for enableRetryAfterHeader against a real
Envoy, for both the global and local rate limit filters, addressing
review feedback that the existing coverage stopped at IR/xDS golden
serialization and never exercised runtime behavior.

Mirrors RateLimitHeadersDisabled's structure (ratelimit.go) and the
eventual-consistency retry idiom used elsewhere in local_ratelimit.go.
Retry-After's value is asserted with a small tolerance rather than an
exact match, since it counts down from the window size.

Signed-off-by: De Ville Weppenaar <11049609+devilleweppenaar@users.noreply.github.com>
The API doc comment said "when rate limiting is enabled", copied from
disableRateLimitHeaders, but that's inaccurate here: X-RateLimit-*
headers are considered on every response the filter evaluates, while
Retry-After is scoped to rate-limited 429 responses only, per Envoy's
own field doc. Every other place we described this option (IR,
translator, e2e tests, release note) already said "rate-limited 429
responses" -- bring the public API doc in line with that instead of
the other way around. Also trims a redundant "if explicitly enabled"
from the translator comment.

Signed-off-by: De Ville Weppenaar <11049609+devilleweppenaar@users.noreply.github.com>
@devilleweppenaar

Copy link
Copy Markdown
Author

I did a review of each edited file, including generated ones.

I also tested the changes locally using a kind cluster with all of the latest changes to ensure that the intended behavior:

  • disableRateLimitHeaders: true: no x-ratelimit-* headers on either the 200s or the 429.
  • enableRetryAfterHeader: true: no retry-after on the 200 responses; present on the 429 (retry-after: 1794) once the 2-request/hour local rate limit was exhausted.

This was running the built code from the PR branch (docker.io/envoyproxy/gateway-dev:19668e189, the current branch HEAD) against a real Envoy proxy in the local kind cluster.

@codecov

codecov Bot commented Aug 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 76.30%. Comparing base (ebae1a6) to head (19668e1).
⚠️ Report is 52 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #9770   +/-   ##
=======================================
  Coverage   76.30%   76.30%           
=======================================
  Files         261      261           
  Lines       44351    44356    +5     
=======================================
+ Hits        33842    33847    +5     
- Misses       8271     8272    +1     
+ Partials     2238     2237    -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Resolve go.mod/go.sum conflicts in examples/extension-server and test
modules by taking main's dependency versions and re-running
'go mod tidy', which resolves consistently with the go-control-plane
bump via each module's local replace directive.

Signed-off-by: De Ville Weppenaar <11049609+devilleweppenaar@users.noreply.github.com>
@arkodg

arkodg commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

can we build on top of #8742 instead of adding the setting in CTP

@arkodg
arkodg requested a review from zirain August 31, 2026 04:37
@devilleweppenaar

Copy link
Copy Markdown
Author

can we build on top of #8742 instead of adding the setting in CTP

@arkodg I believe we could.

The main reason I went with the CTP option first is that I was copying the pattern of how disableRateLimitHeaders is accessed under HeaderSettings.

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.

ClientTrafficPolicy: option to emit Retry-After header on rate-limit 429 responses

3 participants