Repository navigation
Conversation
Previously, the only address attribute HTTP/1 and HTTP/2 server spans set was server.address, from the Host header or the :authority pseudo-header. Now they also set server.port, client.address, network.peer.address, network.peer.port, and user_agent.original, per the HTTP semantic conventions. server.address and server.port come from the first of the Forwarded header's host parameter, the first X-Forwarded-Host entry, and the Host header or :authority, each parsed for both the host and the port, including bracketed IPv6. When the chosen value omits the port, the default comes from the Forwarded proto parameter, the first X-Forwarded-Proto entry, or, for the authority, the request scheme, in any letter case. client.address comes from the first Forwarded for parameter or the first X-Forwarded-For entry, and otherwise from the immediate peer. The network.peer attributes come from the socket. The proxy headers are followed the way OpenTelemetry's own HTTP instrumentation follows them, although a client can spoof them. The package has no runtime dependencies, so the Forwarded header is parsed by a small RFC 7239 parser in lib/forwarded.js. A port that is not a decimal number from 0 to 65535 is now omitted on client spans too, rather than exported as NaN, Infinity, or a rounded number. Signed-off-by: Bryan English <bryan@bryanenglish.com>
bengl
marked this pull request as ready for review
November 3, 2026 19:50
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed.
HTTP/1 and HTTP/2 server spans now set the address attributes the HTTP
semantic conventions ask for:
server.port,client.address,network.peer.address,network.peer.port, anduser_agent.original.server.addressandserver.portcome from the first of theForwardedheader's
hostparameter,X-Forwarded-Host, and theHostheader or:authority, each parsed for both the host and the port, includingbracketed IPv6. When the chosen value omits the port, the default comes
from the
Forwardedprotoparameter,X-Forwarded-Proto, or, for theauthority, the request scheme.
client.addresscomes from the firstForwardedforparameter or the firstX-Forwarded-Forentry, andotherwise from the immediate peer. The peer attributes come from the
socket, which is
stream.session.socketfor HTTP/2.The proxy headers are followed the way OpenTelemetry's own HTTP
instrumentation follows them, although a client can spoof them. The
package has no runtime dependencies, so
Forwardedis parsed by a smallRFC 7239 parser in
lib/forwarded.js. It matches theforwarded-parsepackage the official instrumentation uses on a corpus of valid and
malformed values plus 200k fuzzed inputs.
Not taken.
X-Forwarded-Protodoes not changeurl.scheme. The conventions preferthe scheme of the original request when it is known, but the official
instrumentation does not do that, so this change matches the
instrumentation instead.
Where this differs from the official instrumentation. Node joins repeated headers with commas, and proxies append to
X-Forwarded-HostandX-Forwarded-Proto, so their first entry is the original, the way the firstX-Forwarded-Forentry is. The official instrumentation passes both through whole, which would makeserver.addressa list. The proto matches in any letter case, since URI schemes are case-insensitive. A port that is not a decimal number from 0 to 65535 is omitted rather than exported asNaN,Infinity, or a rounded number, and client spans now use the same rule. EmptyX-Forwarded-HostandX-Forwarded-Forentries are treated as absent. A multi-colon authority without IPv6 brackets, whichthe HTTP grammar does not allow, goes through the existing
splitAuthorityinstead of being kept whole.Tests.
test/test-forwarded.jstable-tests the parser directly, coveringquoted values, IPv6, escapes, case handling, and malformed input.
test/test-http-server.jsandtest/test-http2.jscover the spanattributes with no proxy headers, with
Forwarded, and withX-Forwarded-ForandX-Forwarded-Host, for both protocols. Every newtest was proven to fail by breaking the behavior it covers and
restoring the file.
Measurements. On a quiet machine, against a copy of main at 72ffbae, on Node 26.12.1:
Each throughput row is the median of 8 interleaved node:bench runs. The untraced rows never load microtel, so they show the noise. The CPU row is the median of 8 CPU profiles. Recording up to five more attributes per server span costs about 300 ns of microtel's own time per request, and 1 to 1.7 µs per request end to end. Leaving the new keys out of the preallocated attribute set made no difference.
Raw samples