Skip to content

fix: strip IPv6 zone ID from Host header per RFC 6874 section 4 - #13492

Closed
Archlie wants to merge 4 commits into
aio-libs:masterfrom
Archlie:fix-strip-ipv6-zone-id-from-host-header
Closed

fix: strip IPv6 zone ID from Host header per RFC 6874 section 4#13492
Archlie wants to merge 4 commits into
aio-libs:masterfrom
Archlie:fix-strip-ipv6-zone-id-from-host-header

Conversation

@Archlie

@Archlie Archlie commented Aug 18, 2026

Copy link
Copy Markdown

Per RFC 6874 section 4, a zone ID in an IPv6 link-local address has only local significance at the sending host and must be stripped by the client before sending the Host header:

an HTTP client, proxy, or other intermediary MUST remove any ZoneID attached to an outgoing URI, as it has only local significance at the sending host.

Currently, ClientRequestBase._update_headers() builds the Host header from self.url.host_port_subcomponent, which preserves the zone ID (e.g. Host: [fe80::1%25eth0]:8080). Servers that validate the Host header strictly against RFC 3986, such as nginx from 1.29.4 onward, reject these requests with 400 Bad Request.

Changes

  • Added _strip_ipv6_zone_id() helper that removes the zone ID from bracketed IPv6 hosts (both literal %eth0 and percent-encoded %25eth0 forms).
  • Applied it in _update_headers() for the Host header.
  • Applied it for the CONNECT request authority form (which also embeds the host).
  • Added tests covering the literal and percent-encoded zone IDs, no-zone IPv6, IPv4/domain hosts, and None.

Reproduction

import asyncio
from aiohttp import ClientSession

async def main():
    async with ClientSession() as session:
        async with session.get("http://[fe80::1%25eth0]:8080/") as resp:
            ...

Before: Host: [fe80::1%25eth0]:8080 (rejected by nginx >= 1.29.4 with 400).
After: Host: [fe80::1]:8080.

Closes aio-libs/yarl#1862.

When a request is made to a URL containing an IPv6 link-local address
with a zone id (e.g. zone eth0), aiohttp sends the zone id verbatim as
part of the Host header.

Per RFC 6874 section 4, a zone id only has local significance to the
sending host and must be stripped by the client. Servers that validate
the Host header strictly against RFC 3986, such as nginx from 1.29.4
onward, reject such requests with 400 Bad Request.

Strips the zone id from host_port_subcomponent and host_subcomponent
before they are used in the Host header and CONNECT request target.

Closes #13401
@psf-chronographer psf-chronographer Bot added the bot:chronographer:provided There is a change note present in this PR label Aug 18, 2026
@greptile-apps

greptile-apps Bot commented Aug 18, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains; the previously reported missing changelog attribution is present and follows the repository’s required syntax.

Reviews (2): Last reviewed commit: "fix: preserve RST double-backtick format..." | Re-trigger Greptile

Comment thread CHANGES/13401.bugfix.rst Outdated
@codecov

codecov Bot commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.00%. Comparing base (069e9cf) to head (6384bb3).
⚠️ Report is 1 commits behind head on master.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##           master   aio-libs/aiohttp#13492   +/-   ##
=======================================
  Coverage   99.00%   99.00%           
=======================================
  Files         132      132           
  Lines       49626    49655   +29     
  Branches     2575     2577    +2     
=======================================
+ Hits        49131    49161   +30     
+ Misses        371      370    -1     
  Partials      124      124           
Flag Coverage Δ
Autobahn 22.04% <22.72%> (-0.01%) ⬇️
CI-GHA 98.91% <100.00%> (+<0.01%) ⬆️
OS-Linux 98.68% <100.00%> (+<0.01%) ⬆️
OS-Windows 97.01% <100.00%> (+<0.01%) ⬆️
OS-macOS 97.93% <100.00%> (-0.01%) ⬇️
Py-3.10 98.13% <100.00%> (-0.01%) ⬇️
Py-3.11 98.37% <100.00%> (+<0.01%) ⬆️
Py-3.12 98.45% <100.00%> (-0.01%) ⬇️
Py-3.13 98.45% <100.00%> (+<0.01%) ⬆️
Py-3.14 98.46% <100.00%> (+<0.01%) ⬆️
Py-3.14t 97.55% <100.00%> (+<0.01%) ⬆️
Py-pypy-3.11 97.43% <100.00%> (+0.03%) ⬆️
VM-macos 97.93% <100.00%> (-0.01%) ⬇️
VM-ubuntu 98.68% <100.00%> (+<0.01%) ⬆️
VM-windows 97.01% <100.00%> (+<0.01%) ⬆️
cython-coverage 82.24% <37.50%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

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

@codspeed-hq

codspeed-hq Bot commented Aug 18, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 84 untouched benchmarks
⏩ 83 skipped benchmarks1


Comparing Archlie:fix-strip-ipv6-zone-id-from-host-header (6384bb3) with master (8c8906a)

Open in CodSpeed

Footnotes

  1. 83 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@Archlie

Archlie commented Aug 20, 2026

Copy link
Copy Markdown
Author

Note: the changelog attribution flagged by the greptile review is present in the latest commit — CHANGES/13401.bugfix.rst ends with -- by :user:Archlie``, per repo convention. All CI checks are green; this is ready for maintainer review.

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

Labels

bot:chronographer:provided There is a change note present in this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Host header field contains scope of IPv6 link-local address

2 participants