Skip to content

Commit b7a22c7

Browse files
CopilotRPRX
andcommitted
Xray-core: Dynamic Chrome User-Agent for all HTTP requests by default (overwriteable through config) (#5658)
#4996 (comment) #5658 (comment) --------- Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com> Co-authored-by: Fangliding <63339210+Fangliding@users.noreply.github.com>
1 parent 8c3f246 commit b7a22c7

13 files changed

Lines changed: 63 additions & 10 deletions

File tree

app/dns/nameserver_doh.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
214214

215215
req.Header.Add("Accept", "application/dns-message")
216216
req.Header.Add("Content-Type", "application/dns-message")
217+
req.Header.Set("User-Agent", utils.ChromeUA)
217218
req.Header.Set("X-Padding", utils.H2Base62Pad(crypto.RandBetween(100, 1000)))
218219

219220
hc := s.httpClient

app/observatory/burst/ping.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"time"
88

99
"github.com/xtls/xray-core/common/net"
10+
"github.com/xtls/xray-core/common/utils"
1011
"github.com/xtls/xray-core/features/routing"
1112
"github.com/xtls/xray-core/transport/internet/tagged"
1213
)
@@ -61,6 +62,7 @@ func (s *pingClient) MeasureDelay(httpMethod string) (time.Duration, error) {
6162
if err != nil {
6263
return rttFailed, err
6364
}
65+
req.Header.Set("User-Agent", utils.ChromeUA)
6466

6567
start := time.Now()
6668
resp, err := s.httpClient.Do(req)

app/observatory/observer.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/xtls/xray-core/common/session"
1616
"github.com/xtls/xray-core/common/signal/done"
1717
"github.com/xtls/xray-core/common/task"
18+
"github.com/xtls/xray-core/common/utils"
1819
"github.com/xtls/xray-core/core"
1920
"github.com/xtls/xray-core/features/extension"
2021
"github.com/xtls/xray-core/features/outbound"
@@ -162,7 +163,9 @@ func (o *Observer) probe(outbound string) ProbeResult {
162163
if o.config.ProbeUrl != "" {
163164
probeURL = o.config.ProbeUrl
164165
}
165-
response, err := httpClient.Get(probeURL)
166+
req, _ := http.NewRequest(http.MethodGet, probeURL, nil)
167+
req.Header.Set("User-Agent", utils.ChromeUA)
168+
response, err := httpClient.Do(req)
166169
if err != nil {
167170
return errors.New("outbound failed to relay connection").Base(err)
168171
}

common/utils/browser.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package utils
2+
3+
import (
4+
"math/rand"
5+
"strconv"
6+
"time"
7+
8+
"github.com/klauspost/cpuid/v2"
9+
)
10+
11+
func ChromeVersion() int {
12+
// Use only CPU info as seed for PRNG
13+
seed := int64(cpuid.CPU.Family + cpuid.CPU.Model + cpuid.CPU.PhysicalCores + cpuid.CPU.LogicalCores + cpuid.CPU.CacheLine)
14+
rng := rand.New(rand.NewSource(seed))
15+
// Start from Chrome 144 released on 2026.1.13
16+
releaseDate := time.Date(2026, 1, 13, 0, 0, 0, 0, time.UTC)
17+
version := 144
18+
now := time.Now()
19+
// Each version has random 25-45 day interval
20+
for releaseDate.Before(now) {
21+
releaseDate = releaseDate.AddDate(0, 0, rng.Intn(21)+25)
22+
version++
23+
}
24+
return version - 1
25+
}
26+
27+
// ChromeUA provides default browser User-Agent based on CPU-seeded PRNG.
28+
var ChromeUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/" + strconv.Itoa(ChromeVersion()) + ".0.0.0 Safari/537.36"

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/golang/mock v1.7.0-rc.1
1010
github.com/google/go-cmp v0.7.0
1111
github.com/gorilla/websocket v1.5.3
12+
github.com/klauspost/cpuid/v2 v2.0.12
1213
github.com/miekg/dns v1.1.72
1314
github.com/pelletier/go-toml v1.9.5
1415
github.com/pires/go-proxyproto v0.9.2
@@ -39,7 +40,6 @@ require (
3940
github.com/google/btree v1.1.2 // indirect
4041
github.com/juju/ratelimit v1.0.2 // indirect
4142
github.com/klauspost/compress v1.17.4 // indirect
42-
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
4343
github.com/kr/text v0.2.0 // indirect
4444
github.com/pmezard/go-difflib v1.0.0 // indirect
4545
github.com/quic-go/qpack v0.6.0 // indirect

infra/conf/transport_authenticators.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"sort"
55

66
"github.com/xtls/xray-core/common/errors"
7+
"github.com/xtls/xray-core/common/utils"
78
"github.com/xtls/xray-core/transport/internet/headers/http"
89
"github.com/xtls/xray-core/transport/internet/headers/noop"
910
"google.golang.org/protobuf/proto"
@@ -40,11 +41,8 @@ func (v *AuthenticatorRequest) Build() (*http.RequestConfig, error) {
4041
Value: []string{"www.baidu.com", "www.bing.com"},
4142
},
4243
{
43-
Name: "User-Agent",
44-
Value: []string{
45-
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
46-
"Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46",
47-
},
44+
Name: "User-Agent",
45+
Value: []string{utils.ChromeUA},
4846
},
4947
{
5048
Name: "Accept-Encoding",

proxy/http/client.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/xtls/xray-core/common/session"
2222
"github.com/xtls/xray-core/common/signal"
2323
"github.com/xtls/xray-core/common/task"
24+
"github.com/xtls/xray-core/common/utils"
2425
"github.com/xtls/xray-core/core"
2526
"github.com/xtls/xray-core/features/policy"
2627
"github.com/xtls/xray-core/transport"
@@ -219,6 +220,9 @@ func setUpHTTPTunnel(ctx context.Context, dest net.Destination, target string, u
219220
for _, h := range header {
220221
req.Header.Set(h.Key, h.Value)
221222
}
223+
if req.Header.Get("User-Agent") == "" {
224+
req.Header.Set("User-Agent", utils.ChromeUA)
225+
}
222226

223227
connectHTTP1 := func(rawConn net.Conn) (net.Conn, error) {
224228
req.Header.Set("Proxy-Connection", "Keep-Alive")

transport/internet/grpc/dial.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/xtls/xray-core/common/errors"
1111
"github.com/xtls/xray-core/common/net"
1212
"github.com/xtls/xray-core/common/session"
13+
"github.com/xtls/xray-core/common/utils"
1314
"github.com/xtls/xray-core/transport/internet"
1415
"github.com/xtls/xray-core/transport/internet/grpc/encoding"
1516
"github.com/xtls/xray-core/transport/internet/reality"
@@ -167,9 +168,11 @@ func getGrpcClient(ctx context.Context, dest net.Destination, streamSettings *in
167168
dialOptions = append(dialOptions, grpc.WithInitialWindowSize(grpcSettings.InitialWindowsSize))
168169
}
169170

170-
if grpcSettings.UserAgent != "" {
171-
dialOptions = append(dialOptions, grpc.WithUserAgent(grpcSettings.UserAgent))
171+
userAgent := grpcSettings.UserAgent
172+
if userAgent == "" {
173+
userAgent = utils.ChromeUA
172174
}
175+
dialOptions = append(dialOptions, grpc.WithUserAgent(userAgent))
173176

174177
var grpcDestHost string
175178
if dest.Address.Family().IsDomain() {

transport/internet/httpupgrade/dialer.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/xtls/xray-core/common"
1111
"github.com/xtls/xray-core/common/errors"
1212
"github.com/xtls/xray-core/common/net"
13+
"github.com/xtls/xray-core/common/utils"
1314
"github.com/xtls/xray-core/transport/internet"
1415
"github.com/xtls/xray-core/transport/internet/stat"
1516
"github.com/xtls/xray-core/transport/internet/tls"
@@ -86,6 +87,9 @@ func dialhttpUpgrade(ctx context.Context, dest net.Destination, streamSettings *
8687
for key, value := range transportConfiguration.Header {
8788
AddHeader(req.Header, key, value)
8889
}
90+
if req.Header.Get("User-Agent") == "" {
91+
req.Header.Set("User-Agent", utils.ChromeUA)
92+
}
8993
req.Header.Set("Connection", "Upgrade")
9094
req.Header.Set("Upgrade", "websocket")
9195

transport/internet/reality/reality.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/xtls/xray-core/common/crypto"
2828
"github.com/xtls/xray-core/common/errors"
2929
"github.com/xtls/xray-core/common/net"
30+
"github.com/xtls/xray-core/common/utils"
3031
"github.com/xtls/xray-core/core"
3132
"github.com/xtls/xray-core/transport/internet/tls"
3233
"golang.org/x/crypto/hkdf"
@@ -222,7 +223,7 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati
222223
if req == nil {
223224
return
224225
}
225-
req.Header.Set("User-Agent", fingerprint.Client) // TODO: User-Agent map
226+
req.Header.Set("User-Agent", utils.ChromeUA)
226227
if first && config.Show {
227228
fmt.Printf("REALITY localAddr: %v\treq.UserAgent(): %v\n", localAddr, req.UserAgent())
228229
}

0 commit comments

Comments
 (0)