API
This page demonstrates various ways to integrate with xhr.dev.
Don't see your codebase/stack? Message us!
TLS Options
xhr.dev man-in-the-middle's (MITM) requests/responses to get past bot challenges. You can use either TLS mode below.
Option 1: Quickstart (disable SSL checks inline)
Use this for fast local testing only.
curl --proxy https://magic.xhr.dev \
-k \
-H "x-xhr-api-key: ${XHR_API_KEY}" \
https://app.example.com/loginprocess.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';requests.get('https://app.example.com/login', verify=False)Option 2: Recommended (trust xhrdev.pem)
curl -s https://docs.xhr.dev/xhrdev.pem -o xhrdev.pemInstall and trust xhrdev.pem in your OS trust store:
macOS
sudo security add-trusted-cert \
-d \
-r trustRoot \
-k /Library/Keychains/System.keychain \
./xhrdev.pemLinux (Debian/Ubuntu)
sudo cp ./xhrdev.pem /usr/local/share/ca-certificates/xhrdev.crt
sudo update-ca-certificatesLinux (RHEL/CentOS/Fedora)
sudo cp ./xhrdev.pem /etc/pki/ca-trust/source/anchors/xhrdev.crt
sudo update-ca-trust extractWindows (PowerShell as Administrator)
Import-Certificate -FilePath .\xhrdev.pem -CertStoreLocation Cert:\LocalMachine\RootRestart your terminal/app/browser after installing the certificate.
curl --proxy https://magic.xhr.dev \
--cacert xhrdev.pem \
-H "x-xhr-api-key: ${XHR_API_KEY}" \
https://app.example.com/loginCustom Headers and Cookies
Headers you pass in are passed to the target server. All x-xhr-* headers are removed before sent to the origin server.
Control headers
Control headers change the behaviour of how the proxy operates
Required control headers:
x-xhr-api-key: Your API key
Optional control headers:
x-xhr-managed-proxy: true- use xhr.dev's managed proxy. default: none (no third-party proxy provided)x-xhr-customer-managed-proxy: [url]- bring your own proxy. default: none (no third-party proxy provided)
managed proxy
x-xhr-managed-proxy: true - use xhr.dev's managed proxy
proxy
x-xhr-customer-managed-proxy: [url] Bring your own proxy. Pass in your proxy URL. Will be validated before use. Cannot be combined with x-xhr-managed-proxy.
Node.js
Choose one:
- Quickstart: set
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'. - Recommended: load and trust
xhrdev.pem(examples below).
Node.js with Fetch
import { readFileSync } from 'node:fs';
import { fetch, ProxyAgent } from 'undici';
const proxyUrl = 'https://magic.xhr.dev';
const url = 'https://app.example.com/login';
const ca = readFileSync('./xhrdev.pem');
// native fetch requires n@18+
const response = await fetch(url, {
dispatcher: new ProxyAgent({
uri: proxyUrl,
requestTls: { ca },
}),
headers: {
'x-xhr-api-key': process.env.XHR_API_KEY,
},
redirect: 'manual',
});
console.log({ response });Node.js with Axios popular
import axios from 'axios';
import { readFileSync } from 'node:fs';
import { HttpsProxyAgent } from 'https-proxy-agent';
import { createCookieAgent } from 'http-cookie-agent/http';
import { CookieJar } from 'tough-cookie';
const HttpsProxyCookieAgent = createCookieAgent(HttpsProxyAgent);
const ca = readFileSync('./xhrdev.pem');
const jar = new CookieJar();
const httpsAgent = new HttpsProxyCookieAgent('https://magic.xhr.dev', {
cookies: { jar },
});
httpsAgent.options.ca = ca;
const { data } = await axios.get('https://app.example.com/login', {
httpsAgent,
headers: { 'x-xhr-api-key': process.env.XHR_API_KEY },
});
console.log({ data });Python popular
import os
import requests
xhr_api_key = os.getenv('XHR_API_KEY')
headers = {
"x-xhr-api-key": xhr_api_key,
}
proxy_url = 'https://magic.xhr.dev'
proxies = {
'http': proxy_url,
'https': proxy_url,
}
session = requests.Session()
verify = 'xhrdev.pem' # quickstart option: False
response = session.get(
'https://core.cro.ie',
headers=headers,
proxies=proxies,
verify=verify
)
print('Response:', response.text)Playwright popular
Recommended: install xhrdev.pem into your OS/browser trust store. Quickstart: set ignoreHTTPSErrors: true.
import { chromium } from 'playwright';
const url = 'https://news.ycombinator.com';
const xhrApiKey = process.env.XHR_API_KEY;
const proxyUrl = 'https://magic.xhr.dev';
const browser = await chromium.launch({
proxy: {
server: proxyUrl,
},
});
const context = await browser.newContext({
ignoreHTTPSErrors: false, // quickstart option: true
extraHTTPHeaders: {
'x-xhr-api-key': xhrApiKey,
},
});
const page = await context.newPage();
try {
await page.goto(url);
const title = await page.title();
const content = await page.content();
console.log({ content, title });
} finally {
await browser.close();
}Puppeteer popular
Recommended: install xhrdev.pem into your OS/browser trust store. Quickstart: set acceptInsecureCerts: true.
import puppeteer from 'puppeteer';
const url = 'https://news.ycombinator.com';
const xhrApiKey = process.env.XHR_API_KEY;
const proxyUrl = 'https://magic.xhr.dev';
const browser = await puppeteer.launch({
acceptInsecureCerts: false, // quickstart option: true
args: [`--proxy-server=${proxyUrl}`],
});
const page = await browser.newPage();
await page.setExtraHTTPHeaders({
'x-xhr-api-key': xhrApiKey,
});
try {
await page.goto(url);
const title = await page.title();
const content = await page.content();
console.log({ content, title });
} finally {
await browser.close();
}Go
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"log"
"net/http"
"net/http/cookiejar"
"net/url"
"os"
)
func main() {
apiKey := os.Getenv("XHR_API_KEY")
if apiKey == "" {
log.Fatal("API key is not defined. Please set the 'XHR_API_KEY' environment variable.")
}
proxyURL, err := url.Parse("https://magic.xhr.dev")
if err != nil {
log.Fatal(err)
}
caCert, err := os.ReadFile("xhrdev.pem")
if err != nil {
log.Fatal(err)
}
certPool := x509.NewCertPool()
if ok := certPool.AppendCertsFromPEM(caCert); !ok {
log.Fatal("failed to append xhrdev.pem to cert pool")
}
jar, err := cookiejar.New(nil)
if err != nil {
log.Fatal(err)
}
client := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(proxyURL),
TLSClientConfig: &tls.Config{
RootCAs: certPool,
InsecureSkipVerify: false, // quickstart option: true
},
},
Jar: jar,
}
req, err := http.NewRequest("POST", "https://app.example.com/login", nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("x-xhr-api-key", apiKey)
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println("Response:", string(body))
}Ruby
require 'net/http'
require 'uri'
require 'http/cookie_jar'
require 'openssl'
api_key = ENV['XHR_API_KEY']
abort("API key is not defined. Please set the 'XHR_API_KEY' environment variable.") if api_key.nil? || api_key.empty?
proxy_uri = URI.parse('https://magic.xhr.dev')
uri = URI.parse('https://app.example.com/login')
jar = HTTP::CookieJar.new
http = Net::HTTP.new(uri.host, uri.port, proxy_uri.host, proxy_uri.port, nil, nil)
http.use_ssl = true
http.ca_file = 'xhrdev.pem'
http.verify_mode = OpenSSL::SSL::VERIFY_PEER # quickstart option: OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new(uri.request_uri)
request['x-xhr-api-key'] = api_key
cookies = jar.cookies(uri)
request['Cookie'] = cookies.map { |c| "#{c.name}=#{c.value}" }.join('; ') unless cookies.empty?
response = http.request(request)
jar.parse(response['Set-Cookie'], uri) if response['Set-Cookie']
puts "Response: #{response.body}"