Playwright proxy: tarayıcı ve context yapılandırması
Playwright proxy'sini tarayıcıya veya bir context'e koyun, kimlik bilgilerini geçin, HTTPS CONNECT'i doğrulayın, TLS'i kapatmadan 407 ve sertifika hatalarını giderin.
Bu rehber, Playwright trafiğinin HTTP veya SOCKS5 proxy üzerinden — tüm tarayıcı veya tek bir context için — çıkması gereken geliştiriciler içindir. Laboratuvar Playwright 1.64.0'ı yerel bir origin ve CONNECT konuşan sahte bir proxy'ye karşı çalıştırdı. Ücretsiz anahtar yok; Top Proxy IP satmaz. Geçen yerel bir düzenek üretim hesabı veya bölge değildir.
01Proxy'yi tarayıcıya mı, context'e mi koyarsınız
Playwright proxy nesnesini iki yerde belgeler. Network: HTTP proxy (erişim 14 Ekim 2026) proxy'nin tüm tarayıcı için global veya her browser context için ayrı konabileceğini söyler. browser.newContext() aynı alanları tekrarlar: server, isteğe bağlı bypass, isteğe bağlı username, isteğe bağlı password. Python ağ rehberi aynı şekli chromium.launch(proxy=...) ve browser.new_context(proxy=...) üzerinde kullanır. Bu yürüyüş Node kütüphanesini çalıştırır. Python o belgelerde API olarak benzerdir, ikinci bir laboratuvar değildir.
Her context'in o süreçte aynı çıkışı paylaşması gerekiyorsa proxy'yi chromium.launch üzerine koyun. Tek süreçte iki çıkış, iki çerez deposu veya bir varsayılan artı bir geçersiz kılma gerekiyorsa browser.newContext üzerine koyun.
- İş «çalıştırma için tek çıkış» mı, «tek süreçte iki kimlik» mi karar verin.
- İş tüm tarayıcı süreci için tek çıkışsa
proxy'yichromium.launchüzerine koyun. Playwright Test'teplaywright.config.tsiçindekiuse.proxybir context fixture'ıdır: o testin context'ine ve sayfalarına uygulanır, tarayıcıdaki her context'in paylaştığı bir launch varsayılanı değildir. Configuration (use) sayfasına bakın. - İş iki çıkışsa ikinci kimliği pinlemeden başlatın, sonra her
newContext'e farklı birproxygeçin. browser.close()öncesi oluşturduğunuz her context'i kapatın; bu,browser.newContext()üzerindeki temizlik notudur.
Laboratuvar Chromium 156.0.8078.4'ü tarayıcı düzeyinde 127.0.0.1 HTTP proxy'siyle başlattı ve yerel origin'i çekti. Sayfa HTTP 200 döndü. Proxy günlüğü GET http://127.0.0.1:<port>/ gösterdi. Süreçten HTTP_PROXY, HTTPS_PROXY, ALL_PROXY ve NO_PROXY silindikten sonra proxy seçeneği olmadan doğrudan başlatma aynı origin'e sıfır proxy olayıyla vurdu. Bu temiz bir proxy'siz taban çizgisidir. O değişkenler dururken Playwright'ın ne yapacağını ölçmez.
İkinci tarayıcı ilk proxy'yi launch varsayılanı yaptı, sonra proxy.server'ı başka bir yerel ileticiye bakan bir context açtı. Context isteği 200 döndü. İsabetler ikinci ileticiye düştü. Birinci iletici context'in origin URL'sini görmedi. Context proxy'si o context için tarayıcı varsayılanının yerini aldı. Onunla birleşmedi.
Playwright Test use.proxy test context'ini ve sayfalarını yapılandırır. chromium.launch({ proxy }) ayrı bir tarayıcı varsayılanıdır. Test başına rotasyon API'si değildir. Bu laboratuvarın çalıştırdığı 1.64.0 API'sinde launch veya newContext üzerinde rotateSession alanı yoktur.
02Ana bilgisayar, port ve kimlik bilgilerini sızdırmadan geçirin
server bir URL'dir. Browser API metni açıktır: http://myproxy.com:3128 ve socks5://myproxy.com:3128 desteklenir. Kısa biçim myproxy.com:3128 HTTP sayılır. Sağlayıcı SOCKS5 uç verdiyse socks5:// şeması zorunludur. Kullanıcı adı ve parola, kimlik doğrulama isteyen HTTP proxy için belgelenmiştir. SOCKS5 userpass olarak belgelenmez.
Parolayı ortamda veya bir sır deposunda tutun. Commit edilen bir yapılandırmaya yapıştırmayın. Playwright yine de kabloda Proxy-Authorization göndermek zorundadır. Laboratuvar sahte kullanıcı lab-user ve içinde @, :, /, # ile % bulunan sahte bir parola kullandı. Sonuç günlükleri o değeri kararttı.
Chromium'un HTTP proxy kimlik doğrulaması konuşkandır. Yerel bir Basic proxy'ye karşı:
server'ı kullanıcı adı olmadanhttp://127.0.0.1:<port>yapın. HTTP origin üzerindepage.gotodurum 407 döndü. Proxy günlüğüauthReason: missingkaydetti. Playwright fırlatmadı. Kanıt yanıt nesnesindeki durumdu.- Kullanıcı adı
lab-user, yanlış parola. Yine durum 407. Günlük önce kimlik bilgisi olmayan bir sondaj, sonraauthReason: wronggösterdi. - Sahte parolayı doğru koyun. Durum 200. Günlük aynı eksik sondajı, sonra
authorized: truegösterdi.
Yanlış kimlik bilgileri bu Chromium yapısında asılı soket değildir. Proxy'den gelen HTTP 407'dir, gezinme durumu olarak yüzeye çıkar. Bir sarmalayıcı sonra fırlatırsa 407'yi ve istisna sınıfını tutun. İkisini «proxy çalışmıyor» diye birleştirmeyin.
Aynı Network sayfasındaki HTTP Authentication origin için httpCredentials'tır, proxy.username değildir. Karıştırmak olağan hatadır: origin 401 ile proxy 407 farklı hop'tur.
03HTTPS trafiğinin HTTP proxy üzerinden CONNECT kullandığını doğrulayın
Hedef URL şeması ile proxy URL şeması farklı düğmelerdir. Origin https://example.com/ yine http://127.0.0.1:<port> üzerindeki HTTP proxy'yi kullanır. Proxy yöntemi CONNECT, hedef example.com:443. Laboratuvar o CONNECT'i kaydetti, sonra Chromium genel sertifikaya karşı TLS'i bitirdi ve page.goto 200 döndü. Doğrulama açıkken HTTP proxy üzerinden HTTPS budur.
Laboratuvar CA'sının imzaladığı yerel HTTPS origin diğer yarıdır. Chromium 156.0.8078.4, belgelenen varsayılan false bırakılmış ignoreHTTPSErrors ile aynı HTTP proxy üzerinden https://127.0.0.1:<https-port>/ adresine gitti. Proxy CONNECT 127.0.0.1:<https-port> kaydetti. Sonra Playwright net::ERR_CERT_AUTHORITY_INVALID ile PlaywrightError fırlattı. CONNECT oldu. TLS yine başarısız oldu. Hedefin https:// olması proxy'yi «https proxy» yapmadı; proxy'nin CONNECT kabul etmesi de Chromium'un özel CA'ya güvenmesini sağlamadı.
Aynı origin'e rejectUnauthorized: true ve CA olmadan Node https.request unable to verify the first certificate ile başarısız oldu. Laboratuvar CA PEM'i ile aynı istemci HTTP 200 döndü. Bu belgelenen Node güven yoludur. Playwright'ın v1.46'da newContext üzerine eklenen clientCertificates seçeneği, adlandırılmış origin için istemci sertifikasıdır; özel CA kurmanın yolu değildir. Belgelenen, hatayı atlayan Playwright anahtarı ignoreHTTPSErrors: true'dur. Bu yazı onu kullanmaz. rejectUnauthorized: false da koymayın.
İş «CONNECT'i kanıtla» ise proxy günlüğünde CONNECT host:port okuyun. İş «sayfa yüklendi» ise hâlâ Chromium'un zaten güvendiği bir sertifika veya bu rehberin dışındaki bir süreçle kurulmuş bir CA gerekir. Kişisel tarayıcıdaki bir kontrol sayfası o tarayıcının çıkışıdır, Playwright'ın yolu değil. IP adresim yalnızca tarayıcı çıkışı yardımcısıdır. Playwright'ı yöneltmeden önce bir ucun HTTP veya SOCKS5 kabul edip etmediğini görmek için proxy kontrolü kullanın.

Şekil bu çalıştırmanın sonuç tablosudur; 16:9 kapak değildir, kontrol ekran görüntüsü de değildir.
04SOCKS5'i yalnızca motorun gerçekten desteklediği yerde kullanın
Network rehberi sayfaların HTTP(S) proxy veya SOCKSv5 üzerinden yüklenebileceğini söyler. Chromium 156.0.8078.4, server: "socks5://127.0.0.1:<port>" ve kullanıcı adı olmadan HTTP origin'i durum 200 ile çekti. SOCKS düzeneği CONNECT 127.0.0.1:<origin-port> kaydetti. Kimlik doğrulamasız SOCKS5, bu pinlenmiş Chromium için anlatımdadır.
Kimlik doğrulamalı SOCKS5 değildir. Playwright 1.64.0'da chromium.launch, firefox.launch ve webkit.launch hepsi PlaywrightError fırlattı: Browser does not support socks5 proxy authentication. Fırlatma page.goto'dan önce, launch'ta oldu. Playwright 1.64.0 o başlatmalar için Firefox 157.0 ve WebKit 27.2 indirdi. İleti, 14 Ekim 2026'da hâlâ Open ve P3-collecting-feedback etiketli uzun açık ürün isteği microsoft/playwright#10567 ile uyuşur. Oradaki 2021 bakımcı yorumu, Chromium ve Firefox'un motorların kendisinde SOCKS5 userpass'ten yoksun olduğunu kaydeder.
Sağlayıcı yalnızca kullanıcı adlı SOCKS5 sunuyorsa bu pinlenmiş Playwright yapısı o kullanıcı adını göndermez. username ve password ile HTTP proxy kullanın veya motorun aşağıdaki gibi bir testte gerçekten çevirdiği kimlik doğrulamasız SOCKS5 uç kullanın. Protokol karşılaştırması SOCKS5 ve HTTP proxy yazısındadır. Python Requests başka bir istemci ve başka bir yazıdır: Python requests proxy.
05Seçilmiş ana bilgisayarları atlayın; bypass'ı evrensel kaçış sanmayın
bypass virgülle ayrılmış alan adı listesidir. API örneği ".com, chromium.org, .domain.com" şeklindedir. Her ana bilgisayar için öldürme anahtarı değildir, gizlilik özelliği de değildir.
- Chromium'u açık ileticiye bakan
proxy.servervebypass: "127.0.0.1"ile başlatın. http://127.0.0.1:<origin>/adresine gidin. Sayfa 200 döndü. O pencere için proxy günlüğü boştu. Listelenen ana bilgisayar proxy'yi atladı.bypass: "example.com"ile yeniden başlatın ve aynı127.0.0.1origin'ine gidin. Durum 200, proxyGET http://127.0.0.1:<origin>/kaydetti. Listelenmeyen ana bilgisayar yine proxy'yi kullandı.
Sözün tamamı budur. bypass «bu proxy beni genel olarak göremez» demek değildir. «Bu context'te bu ana bilgisayarlar bu proxy'ye gönderilmez» demektir. Chromium'daki localhost kuralları başka yapılarda şaşırtabilir. Proxy günlüğünü ölçün. Varsaymayın.
06Çerezler ve çıkışlar karışmasın diye iki context'i yalıtın
browser.newContext() başka context'lerle çerez paylaşmayan bir depo oluşturur. İki testin oturumu yanlış çıkışa sızdırmaması gerektiğinde bunu context başına proxy ile birleştirin.
- Chromium'u tarayıcı düzeyinde proxy olmadan başlatın.
- Proxy A ile context A, proxy B ile context B açın.
- İkisini de
labçerezi koyan aynı origin'e götürün. context.cookies()okuyun.
Laboratuvar context A'da t-origin-a-67a984, context B'de t-origin-a-ff4919 çerezini aldı. Depolar eşit değildi. İki gezinme de 200 döndü. Buradaki yalıtım Playwright context yalıtımı artı iki proxy sunucusudur. Sağlayıcı sticky-session pazarlaması değildir, Residential havuzun aynı IP'yi tutacağı iddiası da değildir.
Açtığınız context'leri kapatın. Sızan bir context, tarayıcı ölene kadar çerezleri ve proxy kimlik bilgilerini canlı tutar.
07Reddedilen bağlantıları, 407'yi ve sertifika hatalarını TLS'i kapatmadan teşhis edin
Playwright «çalışmıyorsa» başarısız olan hop'u adlandırın.
| Nerede | Protokol | 1.64.0 laboratuvarının gözlediği | Motor sınırı |
|---|---|---|---|
Doğrudan chromium.launch() | yok | HTTP origin 200, sıfır proxy olayı | yok |
Tarayıcı düzeyinde proxy.server HTTP | HTTP GET | 200, proxy günlüğü absolute-form GET gösterir | yok |
Launch'ı ezen context proxy | HTTP GET | Context proxy'sinde 200, launch proxy'sinde değil | yok |
| HTTP proxy, eksik veya yanlış Basic | HTTP 407 | page.goto durum 407, fırlatma yok | yok |
| HTTP proxy, doğru Basic | HTTP GET | Chromium'un eksik-kimlik sondajından sonra 200 | yok |
| HTTP proxy, genel CA'lı HTTPS origin | CONNECT sonra TLS | CONNECT example.com:443, sayfa 200 | yok |
| HTTP proxy, laboratuvar CA'lı HTTPS origin | CONNECT sonra TLS | CONNECT kaydedildi, sonra net::ERR_CERT_AUTHORITY_INVALID | context CA-dosyası API'si yok |
| SOCKS5, kullanıcı adı yok | SOCKS CONNECT | Chromium 200, düzenek CONNECT | kimlik doğrulama yok |
| SOCKS5 artı kullanıcı adı | launch'ta red | Chromium, Firefox, WebKit'te Browser does not support socks5 proxy authentication | Playwright 1.64.0 + motorlar |
proxy.server http://127.0.0.1:1 | TCP | net::ERR_PROXY_CONNECTION_FAILED | yok |
bypass: "127.0.0.1" | atlama | origin 200, sıfır proxy olayı | liste tamdır, genel değil |
Reddedilen proxy: Chromium yine de başladı. page.goto sonra net::ERR_PROXY_CONNECTION_FAILED ile PlaywrightError fırlattı. Laboratuvardaki varsayılan gezinme zaman aşımı 8000 ms'ti. Reddedilen durum o zaman aşımından hızlı başarısız oldu. Sınıf adı PlaywrightError ve net:: kodunu tutun. Bunu genel bir zaman aşımı diye yeniden adlandırmayın.
Sertifika hatası: TLS'i açık tutun. Başarılı olan yerel CA yolu, ca ayarlı Node https idi. Başarılı olan Playwright tarayıcı yolu, zaten genel CA'sı olan CONNECT üzerinden https://example.com/ idi. Başarısız olan Playwright tarayıcı yolu laboratuvar origin'iydi. ignoreHTTPSErrors false kalır.
Bir yolu sırayla nasıl kontrol edersiniz:
- Proxy portunun bağlantı kabul ettiğini doğrulayın. Reddedilen port
ERR_PROXY_CONNECTION_FAILED'dır, kötü parola değil. - HTTP proxy'lerde 407 ile 200'e bakın. Eksik ve yanlış parolalar bu Chromium'da 407 idi.
- HTTPS hedeflerde proxy günlüğünde CONNECT'e, sonra TLS'e bakın. CONNECT artı
ERR_CERT_AUTHORITY_INVALIDtünelin çalıştığı, sertifikanın çalışmadığı demektir. - Kullanıcı adlı SOCKS5 için 1.64.0'da sayfa hatası değil launch hatası bekleyin.
- Ancak o zaman origin gövdesine bakın.
Aşağıdaki kamuya açık laboratuvar okuyucunun gördüğü düzenecektir. Origin'e, CONNECT proxy'sine ve Chromium sürecine sahiptir; finally içinde, kurucu ve başlatma hatalarında da onları kapatır. public_lab.mjs olarak kaydedin.
npm install playwright@1.64.0
npx playwright install chromium
node public_lab.mjs
#!/usr/bin/env node
/**
* Self-contained loopback lab for Playwright 1.64.0 (Node).
* Dummy credentials only. Local origin and CONNECT proxy. Does not call a paid provider.
*
* npm install playwright@1.64.0
* npx playwright install chromium
* node public_lab.mjs
*
* TLS stays verified. This script never sets ignoreHTTPSErrors.
*/
import { spawnSync } from "node:child_process";
import fs from "node:fs";
import http from "node:http";
import https from "node:https";
import net from "node:net";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { chromium } from "playwright";
const USER = "lab-user";
const PASSWORD = "p@ss:/#%";
const REDACT = "***";
const TIMEOUT_MS = 8000;
const SHUTDOWN_MS = 4000;
const STRIP = [
"HTTP_PROXY",
"HTTPS_PROXY",
"ALL_PROXY",
"NO_PROXY",
"http_proxy",
"https_proxy",
"all_proxy",
"no_proxy",
];
function redact(s) {
return String(s)
.split(PASSWORD)
.join(REDACT)
.split(Buffer.from(`${USER}:${PASSWORD}`).toString("base64"))
.join(REDACT);
}
function isolateEnv() {
for (const k of STRIP) delete process.env[k];
}
function basic() {
return "Basic " + Buffer.from(`${USER}:${PASSWORD}`, "utf8").toString("base64");
}
function listen(server, sockets) {
server._nativeClose = server.close.bind(server);
server.on("connection", (s) => {
sockets.push(s);
s.on("error", () => {});
});
return new Promise((resolve, reject) => {
server.once("error", reject);
server.listen(0, "127.0.0.1", () => resolve(server.address().port));
});
}
function closeServer(server) {
const native = server._nativeClose || server.close.bind(server);
const attempt = (fn) =>
new Promise((resolve, reject) => {
let settled = false;
const done = (err) => {
if (settled) return;
settled = true;
clearTimeout(t);
if (err) reject(err);
else resolve();
};
const t = setTimeout(() => done(new Error("server-close-timeout")), SHUTDOWN_MS);
try {
fn((err) => done(err || null));
} catch (e) {
done(e);
}
});
return (async () => {
try {
if (typeof server.closeAllConnections === "function") server.closeAllConnections();
} catch {}
let lastErr = null;
try {
await attempt((cb) => server.close(cb));
} catch (e) {
lastErr = e;
try {
if (typeof server.closeAllConnections === "function") server.closeAllConnections();
} catch {}
try {
await attempt((cb) => native(cb));
lastErr = lastErr;
} catch (e2) {
lastErr = e2;
}
}
if (server.listening) throw lastErr || new Error("server-still-listening");
if (lastErr) throw lastErr;
})();
}
async function guarded(name, fn, errors) {
try {
await Promise.race([
fn(),
new Promise((_, reject) => setTimeout(() => reject(new Error(`${name}-shutdown-bound`)), SHUTDOWN_MS)),
]);
} catch (e) {
errors.push(`${name}: ${e && e.message ? e.message : e}`);
}
}
function makeCerts(tmp) {
const caKey = path.join(tmp, "ca.key");
const caPem = path.join(tmp, "ca.pem");
const originKey = path.join(tmp, "origin.key");
const originCsr = path.join(tmp, "origin.csr");
const originPem = path.join(tmp, "origin.pem");
const ext = path.join(tmp, "ext.cnf");
fs.writeFileSync(ext, "subjectAltName=IP:127.0.0.1,DNS:localhost\nbasicConstraints=CA:FALSE\n");
const run = (args) => {
const r = spawnSync("openssl", args, { encoding: "utf8" });
if (r.status !== 0) throw new Error(r.stderr || r.stdout);
};
run(["req", "-x509", "-newkey", "rsa:2048", "-keyout", caKey, "-out", caPem, "-days", "1", "-nodes", "-subj", "/CN=pw-public-lab-ca"]);
run(["req", "-newkey", "rsa:2048", "-keyout", originKey, "-out", originCsr, "-nodes", "-subj", "/CN=127.0.0.1"]);
run(["x509", "-req", "-in", originCsr, "-CA", caPem, "-CAkey", caKey, "-CAcreateserial", "-out", originPem, "-days", "1", "-extfile", ext]);
return {
caPem: fs.readFileSync(caPem, "utf8"),
originKey: fs.readFileSync(originKey),
originPem: fs.readFileSync(originPem),
};
}
function startHttpOrigin(sockets) {
const server = http.createServer((req, res) => {
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify({ ok: true, path: req.url, via: "http-origin" }));
});
return listen(server, sockets).then((port) => ({ server, port }));
}
function startHttpsOrigin(certs, sockets) {
const server = https.createServer(
{ key: certs.originKey, cert: certs.originPem, minVersion: "TLSv1.2" },
(req, res) => {
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify({ ok: true, path: req.url, via: "https-origin" }));
},
);
return listen(server, sockets).then((port) => ({ server, port }));
}
function startConnectProxy(sockets) {
const events = [];
const server = net.createServer((socket) => {
let buf = Buffer.alloc(0);
const onData = (chunk) => {
buf = Buffer.concat([buf, chunk]);
const text = buf.toString("latin1");
const split = text.indexOf("\r\n\r\n");
if (split < 0) return;
socket.removeListener("data", onData);
const head = text.slice(0, split);
const lines = head.split("\r\n");
const [method, target] = lines[0].split(" ");
const headers = {};
for (const line of lines.slice(1)) {
const c = line.indexOf(":");
if (c >= 0) headers[line.slice(0, c).trim().toLowerCase()] = line.slice(c + 1).trim();
}
const rec = { method, target, auth: headers["proxy-authorization"] === basic() };
events.push(rec);
if (!rec.auth) {
socket.end("HTTP/1.1 407 Proxy Authentication Required\r\nProxy-Authenticate: Basic realm=\"lab\"\r\nContent-Length: 0\r\n\r\n");
return;
}
if (method === "CONNECT") {
const [host, portStr] = target.split(":");
const dest = net.connect(Number(portStr), host, () => {
socket.write("HTTP/1.1 200 Connection Established\r\n\r\n");
socket.pipe(dest);
dest.pipe(socket);
});
sockets.push(dest);
dest.on("error", () => socket.destroy());
return;
}
let url;
try {
url = new URL(target);
} catch {
socket.end("HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n");
return;
}
const req = http.request(
{
hostname: url.hostname,
port: url.port || 80,
path: url.pathname + url.search,
method,
headers: { host: url.host },
},
(res) => {
socket.write(`HTTP/1.1 ${res.statusCode} ${res.statusMessage}\r\n`);
for (const [k, v] of Object.entries(res.headers)) socket.write(`${k}: ${v}\r\n`);
socket.write("\r\n");
res.pipe(socket);
},
);
req.on("error", () => socket.destroy());
req.end();
};
socket.on("data", onData);
socket.on("error", () => {});
});
return listen(server, sockets).then((port) => ({ server, port, events }));
}
function nodeHttpsWithCa(port, caPem, timeoutMs = TIMEOUT_MS) {
return new Promise((resolve, reject) => {
let settled = false;
let deadline;
const finish = (fn, arg) => {
if (settled) return;
settled = true;
clearTimeout(deadline);
fn(arg);
};
const failTimeout = () => {
const err = new Error("node-https-timeout");
err.name = "NodeHttpsTimeoutError";
try {
req.destroy(err);
} catch {}
finish(reject, err);
};
const req = https.request(
{
hostname: "localhost",
port,
path: "/",
method: "GET",
ca: caPem,
rejectUnauthorized: true,
timeout: timeoutMs,
},
(res) => {
const chunks = [];
res.on("data", (c) => chunks.push(c));
res.on("end", () => finish(resolve, { status: res.statusCode, body: Buffer.concat(chunks).toString("utf8") }));
res.on("error", (e) => finish(reject, e));
},
);
req.on("timeout", failTimeout);
req.on("error", (e) => finish(reject, e));
deadline = setTimeout(failTimeout, timeoutMs);
req.end();
});
}
async function main() {
isolateEnv();
const owned = [];
const sockets = [];
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "pw-public-lab-"));
let browser;
const result = { ok: false, ignoreHTTPSErrors: false, cases: [] };
const wipeTmp = () => {
if (!tmp || !fs.existsSync(tmp)) return;
for (const f of fs.readdirSync(tmp)) {
if (f.endsWith(".key") || f.endsWith(".pem") || f.endsWith(".csr") || f.endsWith(".srl") || f.endsWith(".cnf")) {
fs.rmSync(path.join(tmp, f), { force: true });
}
}
fs.rmSync(tmp, { recursive: true, force: true });
};
try {
const certs = makeCerts(tmp);
const httpOrigin = await startHttpOrigin(sockets);
owned.push(httpOrigin.server);
const httpsOrigin = await startHttpsOrigin(certs, sockets);
owned.push(httpsOrigin.server);
const proxy = await startConnectProxy(sockets);
owned.push(proxy.server);
browser = await chromium.launch({
proxy: {
server: `http://127.0.0.1:${proxy.port}`,
username: USER,
password: PASSWORD,
},
timeout: 30000,
});
const context = await browser.newContext();
const page = await context.newPage();
page.setDefaultTimeout(TIMEOUT_MS);
const httpRes = await page.goto(`http://127.0.0.1:${httpOrigin.port}/`);
const httpBody = await page.textContent("body");
result.cases.push({
name: "browser-http-via-proxy",
status: httpRes.status(),
body: httpBody,
proxyEvents: proxy.events.filter((e) => e.method === "GET").map((e) => ({ method: e.method, target: e.target })),
});
let tlsErr = null;
try {
await page.goto(`https://127.0.0.1:${httpsOrigin.port}/`);
} catch (e) {
tlsErr = { name: e.name, message: redact(e.message) };
}
result.cases.push({
name: "browser-https-untrusted-ca",
error: tlsErr,
connectEvents: proxy.events.filter((e) => e.method === "CONNECT"),
});
const trusted = await nodeHttpsWithCa(httpsOrigin.port, certs.caPem);
result.cases.push({
name: "node-https-with-lab-ca",
status: trusted.status,
body: trusted.body,
});
await context.close();
result.ok =
result.cases[0].status === 200 &&
tlsErr != null &&
trusted.status === 200 &&
result.cases[1].connectEvents.length >= 1;
result.httpOrigin = httpOrigin.port;
result.httpsOrigin = httpsOrigin.port;
result.proxyPort = proxy.port;
} finally {
const errors = [];
try {
await guarded(
"browser",
async () => {
if (browser) await browser.close();
},
errors,
);
await guarded(
"sockets",
async () => {
for (const s of sockets.splice(0)) {
try {
s.destroy();
} catch {}
}
},
errors,
);
await guarded(
"servers",
async () => {
const leftover = [];
for (const s of owned) {
try {
await closeServer(s);
} catch (e) {
errors.push(`server: ${e && e.message ? e.message : e}`);
}
if (s.listening) leftover.push(s.address() && s.address().port);
}
if (leftover.length) throw new Error(`servers-still-listening:${leftover.join(",")}`);
},
errors,
);
} finally {
await guarded("temp-keys", async () => wipeTmp(), errors);
}
if (errors.length) result.cleanupErrors = errors;
result.serversLeftover = owned.filter((s) => s.listening).map((s) => s.address() && s.address().port);
}
console.log(JSON.stringify(result, null, 2));
if (!result.ok || (result.serversLeftover && result.serversLeftover.length) || (result.cleanupErrors && result.cleanupErrors.some((e) => String(e).includes("still-listening")))) {
process.exitCode = 1;
}
}
export { nodeHttpsWithCa, TIMEOUT_MS, makeCerts, SHUTDOWN_MS, closeServer };
const isMain =
process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1]);
if (isMain) {
main().catch((e) => {
console.error(redact(e && e.stack ? e.stack : e));
process.exit(1);
});
}
14 Ekim 2026'da o betik ok: true döndü, proxy üzerinden HTTP 200, yerel HTTPS porta CONNECT, Chromium'da net::ERR_CERT_AUTHORITY_INVALID ve laboratuvar CA'sı ile Node'dan HTTP 200. Yalnızca sahte kimlik bilgileri. openssl PATH'te olmalıdır.
İstemci çalıştıktan sonra sağlayıcı gerekiyorsa Bright Data, Oxylabs, SOAX ve Decodo (Smartproxy) katalog sayfaları 14 Ekim 2026'da HTTP 200 döndü ve HTTP ile SOCKS5'ten bahseder. O sayfalar laboratuvar çıkışı değildi. Katalog uyumu yayımlanmış protokol metnidir, o satıcı üzerinden Playwright girişi değildir.

Bright Data, veri toplama için büyük bir proxy ağıdır: yaklaşık 195 ülkede residential, mobil, ISP ve datacenter IP'ler. Residential trafik abonelik olmadan kullandıkça ödenebilir; datacenter ve ISP adresleri 10 IP'lik paketlerden başlar. Residential ve mobil erişim kimlik doğrulaması ister.

Oxylabs residential, mobil, ISP ve datacenter proxy'leri $30'dan tek aylık abonelik altında satar. Alıcı ödemeleri: kartlar, havale, AliPay, PayPal.
Playwright'ı bağlamadan önce ana bilgisayar, port ve protokol ön kontrolü için proxy kontrolü kullanın. Daha derin «bu proxy sızdırıyor mu» geçişi için Bir proxy'nin çalışıp çalışmadığı yazısına bakın.
Proxy tarayıcıda mı, context'te mi durmalı?
Playwright HTTPS'i HTTP proxy üzerinden gönderir mi?
Proxy kimlik doğrulaması nasıl geçilir?
Chromium, Firefox ve WebKit kullanıcı adlı SOCKS5 kullanabilir mi?
Laboratuvar CA'sının geçmesi için ignoreHTTPSErrors ayarlamalı mıyım?
bypass, proxy'nin hiç kullanılmadığı anlamına mı gelir?
Bir kontrol veya IP adresim sayfası Playwright yolunu kanıtlar mı?
İlgili yazılar
Residential proxy nedir ve nasıl çalışır
Residential proxy, tüketici ISP adreslerinden çıkar. Havuz, sticky oturum, sitenin ne gördüğü ve satın almadan önce sağlayıcıya sorulacaklar.
4 dk2026'da web kazıma için en iyi proxy: alıcı kontrol listesi
2026 kazıma alımı satıcıdan değil hedeften başlar. Ölçeklemeden önce köken, oturum, coğrafya, faturalama ve canlı test için bu listeyi kullanın.
5 dkSticky ve rotating proxy: oturum kontrolü nasıl çalışır
Sticky ve rotating proxy: istek başına ve zamanlayıcılı rotasyon, sticky oturumun yeni IP'den ne zaman üstün olduğu, çerezlerin IP değişimiyle ilişkisi ve oturum kimliğini kendiniz nasıl test edeceğiniz.
13 dk



