Proxies
0-day streakReview

Your progress

Level 1

0 XP100 XP to level 2
0day streak
0best streak
0topics started
0cards reviewed
    Review cards

    Progress is stored only in this browser. No account is required.

    Layer 4 · Full breakdown · 19 min · 5 figures

    Proxy vs reverse proxy

    A forward proxy acts for clients and a reverse proxy acts for servers. What each is for, how HTTPS passes through them, how the client’s IP survives the trip, and where load balancers, gateways, CDNs and NAT fit.

    A proxy is a server in the middle that makes requests on someone else’s behalf. The difference between the two kinds is whose side they are on: a forward proxy works for clients, so servers see the proxy instead of the user; a reverse proxy works for servers, so clients believe the proxy is the server. Load balancers, API gateways and CDNs are reverse proxies with a speciality.

    Picture it first: the office runner and the kitchen counter

    A small company in Bengaluru has one office runner. Employees hand him errands, such as buying printer paper or depositing a cheque, and the shopkeeper deals only with him, never learning which employee sent him. The company gets control too: the runner has a list of approved shops and writes every errand in a register. That is a forward proxy. The senders hired him.

    Across the road, a cloud kitchen has one order counter on the street and six kitchens behind the wall. The counter decides which kitchen gets each order, turns away what it won’t accept, keeps popular items on a warmer and packs every order. Nobody outside sees the kitchens. That is a reverse proxy. The receivers hired it.

    Map it back: the employees are laptops or backend services, the runner is a forward proxy, and the shops are internet servers. The customers are users, the counter is nginx, HAProxy or Envoy, and the kitchens are your app servers.

    Whose side is the proxy on?

    Ask two questions about any intermediary: who chose it, and who does it pretend to be?

    • Forward proxy. The client chooses it, through configuration, and knows it is talking to a proxy. The origin server sees the proxy’s IP address, not the client’s.
    • Reverse proxy. The server’s owner chooses it, through DNS: shop.example resolves to the proxy. The client thinks it is talking to the origin, and the app servers see the proxy’s address on every connection.

    RFC 9110 names three intermediaries. A proxy is “chosen by the client”. A gateway, “a.k.a. reverse proxy”, acts as the origin server toward the client and forwards requests inward. A tunnel is a blind relay that passes bytes without reading them. One box can play all three roles.

    On the wire, an HTTP/1.1 client sends a forward proxy the whole URL (RFC 9112 calls it absolute-form), because the proxy needs to know where to go. A reverse proxy gets only the path, because the client thinks it is already at the server:

    GET http://catalog.example/items/42 HTTP/1.1
    Host: catalog.example
    
    GET /items/42 HTTP/1.1
    Host: catalog.example

    The first is what a forward proxy receives; the second, what a reverse proxy receives. Two more distinctions:

    • Explicit versus transparent. An explicit proxy is configured on the client. A transparent or interception proxy isn’t: the network silently redirects traffic into it, as public Wi-Fi does for its login page. RFC 9110 warns such boxes are “indistinguishable (at a protocol level) from an on-path attacker”.
    • Every hop is its own connection. With three intermediaries, a request crosses four connections, each with its own TLS, keep-alive and timeouts. Most proxy surprises come from forgetting this.
    The same three-box picture with opposite loyalties. The forward proxy sits inside the clients’ network; the reverse proxy sits inside the servers’ network.Notice Where the dashed zone is drawn. The proxy always lives on the side of the party it works for, and hides that party from the other side.

    In the interview

    One sentence that lands well: “A forward proxy hides the clients from the server; a reverse proxy hides the servers from the clients.” Then give one job for each: stable egress IPs for a payment partner, and TLS termination plus path routing.

    Forward proxies: controlling the way out

    A forward proxy gives an organisation one controlled exit to the outside world. Its jobs:

    • Egress control for people. IT blocks malware domains, requires a login (the proxy answers 407 Proxy Authentication Required until the client sends Proxy-Authorization), and logs who went where.
    • Shared caching. Once the big win, but most traffic is now HTTPS through tunnels the proxy can’t read, so caching has moved to the server side, into CDNs.
    • Anonymity and location. Servers see the proxy’s address, so a proxy abroad changes where you appear to be.
    • Stable egress IPs for partners. Banks and payment networks often accept calls only from allowlisted source IPs, but your pods get new addresses on every deploy. Route those calls through two or three egress proxy nodes with fixed public IPs, and the partner allowlists only those. Stripe’s Smokescreen does this, giving “financial partners stable egress IP addresses”.
    • SSRF defence. A feature that fetches a URL a user typed, such as webhooks, can be tricked into calling an admin endpoint or the cloud metadata service at 169.254.169.254, which hands out credentials. Smokescreen checks each hostname against a per-service allowlist, resolves the name itself, refuses addresses that aren’t publicly routable, and identifies callers by mTLS certificate. For a deny-list, the OWASP SSRF cheat sheet gives the minimum: 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.169.254 and the IPv6 equivalents.

    Checking at the proxy, after DNS, matters: a hostname the app validated can resolve to 10.0.0.7 a second later.

    An egress proxy as the single way out. Partners see two fixed IPs, and a webhook URL that points at the cloud metadata service is refused.Notice The pods behind the proxy can change IPs freely. The partner’s allowlist only ever holds the proxy’s two addresses.

    Forward proxy or NAT gateway? A NAT gateway also gives private servers fixed public IPs, more simply: it rewrites addresses at layers 3 and 4 and never parses HTTP. An AWS NAT gateway handles up to 55,000 simultaneous connections per IP to each unique destination. But it never sees a hostname or knows which service called. Use NAT so outbound traffic works; add an egress proxy for policy per caller and per hostname.

    How clients find a forward proxy

    • Environment variables, for services. curl and most libraries read https_proxy and no_proxy. curl accepts http_proxy only in lower case, because CGI servers turn a visitor’s Proxy: header into an HTTP_PROXY variable.
    • PAC files, for browsers: a JavaScript function, FindProxyForURL(url, host), returning strings such as PROXY proxy.corp:8080; DIRECT.
    • Interception, with no client configuration at all.

    HTTPS through a forward proxy

    A proxy can’t read a TLS stream unless it holds the keys, so there are two ways to handle HTTPS.

    A CONNECT tunnel, the default. The client asks the proxy to open a raw TCP connection to the destination:

    CONNECT api.partner.example:443 HTTP/1.1
    Host: api.partner.example:443
    Proxy-Authorization: Basic dXNlcjpwYXNz
    
    HTTP/1.1 200 Connection established

    After any 2xx answer, the proxy blindly copies bytes both ways (RFC 9110, section 9.3.6), and the client runs TLS with the real server through the tunnel, checking the real certificate. The proxy sees the host, port, usually the server name in the TLS ClientHello, byte counts and timing; never the path, headers or body. The RFC says CONNECT should be limited to known ports: an open CONNECT to port 25 makes the proxy a spam relay.

    TLS interception, or “break and inspect”. The proxy terminates TLS itself, presenting a certificate for api.partner.example minted on the spot and signed by a corporate root CA that IT installed on every managed laptop, so the browser shows no warning. Then it opens its own TLS connection to the real server and can inspect everything. The costs:

    • The client can verify only the proxy. Checking the real server’s certificate is the proxy’s job, and US-CERT alert TA17-075A warned that many products did it badly or hid errors. A 2017 study by Durumeric and colleagues found nearly all the interception products it examined reduced connection security.
    • It breaks pinning and client certificates, since the proxy lacks the client’s private key.
    • The proxy becomes a crown jewel. Its CA key can impersonate any site to every employee.
    Two ways HTTPS crosses a forward proxy. A CONNECT tunnel carries one TLS session from laptop to bank; interception splits it into two sessions with the proxy in the middle.Notice The gap in the bottom bar. Wherever a TLS session ends, someone holds a key and sees plaintext.

    Backend egress: tunnel, don’t intercept

    For an egress proxy in front of your own services, CONNECT is usually enough: you still get per-service hostname allowlists, IP checks and a log of destinations, and the proxy never holds partner credentials in plaintext. Smokescreen works this way.

    Reverse proxies: one front door for many servers

    Point your DNS at a reverse proxy and every request passes one place you control. Its jobs:

    • One address, hidden topology. App servers sit on private addresses, reachable only through the proxy, and can be added or moved without clients noticing.
    • TLS termination. The proxy holds the certificates, decrypts, then forwards plain HTTP or re-encrypts. Where to terminate and what handshakes cost is in HTTP and HTTPS.
    • Routing by host and path. api.shop.example to the API pool, /images/ to static storage, /v2/orders to a new service: how a strangler-fig migration moves traffic one route at a time.
    • Load balancing. Spread requests over backends that pass health checks; algorithms are in L4 vs L7 load balancing.
    • Caching and micro-caching. Even one second of caching on a hot public page helps: at 5,000 requests a second for one URL, each proxy node sends about 1 request a second upstream, provided concurrent misses wait for a single fetch (nginx’s proxy_cache_lock). That is a 5,000× cut, paid for with data up to a second old; see cache stampedes.
    • Compression and static files. Compress text once at the proxy instead of in every app, and serve images and scripts straight from disk.
    • Request limits. nginx’s defaults answer 413 for a body over 1 MB, 414 for a request line over 8 KB, 400 for a header line over 8 KB, and 408 if headers take over 60 seconds.
    • Header hygiene. Strip internal headers, set X-Forwarded-*, and add a request ID for tracing.
    • WAF and DDoS absorption. Block known attack patterns and soak up floods before the app tier sees them. Authentication and per-key rate limiting usually sit one step in, in the API gateway.

    Two jobs deserve a closer look, because they justify a proxy even in front of one app server.

    Buffering and connection reuse

    Buffering slow clients

    Many app servers run a fixed number of workers, and without a proxy a worker that has finished a response stays tied to the client until the last byte is downloaded. A 200 KB page to a phone on a 1 Mbps link takes 200 KB × 8 ÷ 1 Mbps = 1.6 s; over a 1 Gbps link to a proxy it takes 1.6 ms. A buffering proxy takes the response at data-centre speed, frees the worker, and trickles the bytes out itself. An app with 64 workers serving slow phones directly tops out near 64 ÷ 1.6 s = 40 requests a second; behind the proxy, the same workers are limited by CPU and database time instead.

    nginx does this by default. With proxy_buffering on, it reads the upstream response into memory buffers (proxy_buffers 8 4k|8k) and spills the rest into a temporary file of up to 1 GB. proxy_request_buffering on reads the whole request body before sending anything upstream, so a slow upload or a “slowloris” attacker ties up a cheap proxy connection, not an app worker. You pay in proxy memory and disk, and in delay for anything that should stream.

    Many client connections, few upstream ones

    Browsers keep connections open between clicks, so most client connections are idle. The proxy holds them all and keeps a small pool of warm connections to each backend. How many? By Little’s law, requests in flight = arrival rate × time in the backend: 2,000 requests a second × 25 ms = 50 in flight. So 4 backends × 50 pooled connections = 200 leave ample headroom for 10,000 clients.

    The backends then see 200 long-lived connections instead of 10,000 churning ones: no handshake per new client, no memory for idle phones, and no running out of source ports, which caps a proxy that opens and closes a fresh connection per request at about 470 a second to one backend (see TCP vs UDP). It is a connection pool in the network. Check that reuse is on: nginx before 1.29.7 closed every upstream connection after one request unless you set keepalive, proxy_http_version 1.1 and cleared the Connection header; newer versions keep up to 32 idle upstream connections per worker by default.

    Cloudflare’s numbers show what reuse is worth. Moving from its nginx-based origin proxy to Pingora, which shares one pool across all threads instead of one per worker process, raised one large customer’s connection reuse from 87.1% to 99.92%: new connections fell from 12.9% of requests to 0.08%, about 160× fewer. Across all traffic, median time to first byte fell by 5 ms and the 95th percentile by 80 ms, mostly handshake time saved.

    One request through a reverse proxy, and the connection fan-in around it. Ten thousand mostly idle client connections come in; two hundred busy, warm connections go out.Notice The upstream side needs only as many connections as there are requests in flight, not as many as there are clients.

    Passing the client’s identity through

    Every hop is a new TCP connection, so the app sees its proxy’s address as the caller. Rate limits, fraud checks and audit logs need the real one. Proxies pass it along in headers:

    • X-Forwarded-For (XFF), the de facto standard. Each proxy appends the address it received the request from: client, proxy1, proxy2. The last proxy’s own address is the TCP peer, not in the list.
    • X-Forwarded-Proto and X-Forwarded-Host: what the client used. Behind TLS termination, the app needs X-Forwarded-Proto: https to build https:// redirects and mark cookies Secure.
    • Forwarded, the RFC 7239 standard, keeps each hop’s facts together: Forwarded: for=203.0.113.7;proto=https;host=shop.example. Cleaner, but much less used than XFF.
    • Via records hops and protocol versions (Via: 1.1 edge-blr); RFC 9110 requires proxies to add it. It names proxies, not clients.

    Which entry to trust

    Any client can send X-Forwarded-For: 10.0.0.5. An app that believes the leftmost entry lets an attacker look internal and slip past an admin IP allowlist, or rotate fake addresses to dodge a per-IP rate limit. The safe rule:

    1. Only entries your own proxies appended can be trusted, so read from the right.
    2. Skip every entry belonging to a proxy you run or trust, by hop count or by address range. The first address that isn’t yours is the client.
    3. Make the app reachable only through those proxies. If it can be reached directly, no part of the list is trustworthy.
    X-Forwarded-For through a CDN and a load balancer. The client sneaks in a fake internal address; the app reads from the right and skips its own hops.Notice The real client address, 203.0.113.7, was written by the CDN. Each proxy vouches only for the hop just before it.

    In nginx, the realip module implements step 2: it trusts the header only on connections from listed addresses, and with real_ip_recursive on takes the last address in it that isn’t trusted:

    set_real_ip_from  10.0.1.0/24;       # our load balancers
    set_real_ip_from  198.51.100.0/24;   # the CDN's published ranges
    real_ip_header    X-Forwarded-For;
    real_ip_recursive on;

    Envoy calls the same idea xff_num_trusted_hops.

    “Trust all proxies” means “trust the attacker”

    If a framework’s “trust proxy” setting trusts every hop, the walk from the right stops only at the leftmost entry: the string the client typed. Configure the exact hop count or address ranges of your own proxies.

    The PROXY protocol, for proxies that don’t speak HTTP

    An L4 proxy, such as HAProxy in TCP mode or a load balancer passing TLS straight through, can’t add an HTTP header: it never parses HTTP. HAProxy’s PROXY protocol instead sends one short header at the start of each TCP connection, before any client bytes:

    PROXY TCP4 203.0.113.7 10.0.1.10 51234 443

    That is the family, source and destination addresses, and source and destination ports. Version 1 is this text line, at most 107 bytes. Version 2 is binary: a 12-byte signature, 4 bytes of version, family and length, and 12 bytes of IPv4 addresses and ports, 28 bytes in all. nginx reads it before the TLS handshake when its listen line has proxy_protocol.

    Two rules from the spec matter. A receiver must be configured to expect the header, never guess, or any client could send its own PROXY line and pick its address. And a proxy that multiplexes many clients over one upstream connection must not use it, since one header per connection can’t describe many clients. So L7 proxies use headers; L4 proxies use the PROXY protocol.

    The proxy family tree

    Load balancers, API gateways and CDNs are reverse proxies specialised for one job. A service-mesh sidecar is both kinds: a reverse proxy for its service’s incoming calls and a forward proxy for outgoing ones; Envoy runs “alongside every application server”, and each app talks through localhost. A NAT gateway is the odd one out: it never reads HTTP.

    Works for Deployed by Layer What it can see Typical products
    Forward proxy Clients going out The clients’ organisation L7, plus CONNECT tunnels Host and port; everything only if it intercepts TLS Squid, Stripe’s Smokescreen, corporate web gateways
    Reverse proxy Servers The site’s owner L7 The whole request, after TLS termination nginx, HAProxy, Envoy
    Load balancer Servers The site’s owner L4 or L7 L4: addresses and ports; L7: the whole request HAProxy, Envoy, AWS ALB and NLB
    API gateway An API product The API’s owner L7 The whole request plus the caller’s identity Kong, AWS API Gateway, Envoy-based gateways
    CDN Content owners The site’s owner; run by a provider L7, in many cities The whole request; it terminates TLS at the edge Cloudflare, Akamai, Amazon CloudFront
    Service-mesh sidecar One service, both directions The platform team L4 and L7 Every call into and out of its service Envoy in Istio, linkerd2-proxy
    NAT gateway Private hosts going out The network team L3 and L4 Addresses, ports and protocol only AWS NAT gateway, home routers
    The proxy family. Each row is a specialisation of the forward or reverse idea, except NAT, which works below HTTP.

    Products overlap: one Envoy fleet can terminate TLS, balance load and check API keys. In an interview, name each box by the job it does in your diagram.

    In the interview

    Asked “do we need a reverse proxy and a load balancer?”, say they are usually the same box. A managed L7 balancer is a reverse proxy; add a separate nginx or Envoy tier only for features it lacks, such as response caching or buffering.

    Going deeper: where proxies bite

    The front door is now on every request

    One proxy in front of everything is a single point of failure and a capacity ceiling. Run at least two, sized so losing one is survivable: three nodes at 60% carry 3 × 60% = 180% of one node’s capacity, so if one dies the other two run at 180% ÷ 2 = 90%. Four at 75% would leave three at 300% ÷ 3 = 100%, with no headroom.

    Spread them across zones (AWS gives the same advice for NAT gateways), keep them stateless, and health-check both ways: the balancer checks the proxies, the proxies check their upstreams. Every client and upstream connection is a socket, so raise file-descriptor limits. A bad routing rule hits the whole front door at once, so roll configuration out as a canary.

    Timeouts stack up

    Every hop has its own timers. nginx waits up to 60 s for each upstream read (proxy_read_timeout). Picture a mobile app that retries after 10 s, behind nginx at 60 s, in front of a service whose database call can hang for 90 s. When the database stalls, each tap is retried every 10 s, so after a minute six copies of the same slow work are running, and nginx returns 504s to requests the app abandoned long ago. Make the budget shrink inward, such as client 10 s, proxy 9 s, service 8 s, query 2 s, and give backends a longer keep-alive than the proxy (the idle-timeout race). See timeouts, retries and backoff.

    Streams, SSE and WebSockets

    The buffering that protects app workers breaks streaming: server-sent events or a token-by-token LLM answer sit in the buffer and arrive in clumps. For those routes, turn proxy_buffering off (or the app sends X-Accel-Buffering: no) and check compression isn’t holding data back.

    WebSockets need more. Upgrade and Connection are hop-by-hop headers, so nginx forwards them only when told to (proxy_set_header Upgrade $http_upgrade and Connection "upgrade"), and it closes a proxied WebSocket silent for 60 s, so ping every 20 to 30 s. See real-time connections.

    Header limits differ per hop

    The smallest header limit on the path wins. nginx rejects a header line over 8 KB with a 400 by default; your CDN and balancer have their own limits. A bloated cookie jar plus a large JWT can pass the CDN and fail one hop later, as 400s for some users only.

    Request smuggling

    A reverse proxy sends many users’ requests over one reused upstream connection, so it and the backend must agree exactly where each request ends. HTTP/1.1 has two ways to say it, Content-Length and Transfer-Encoding: chunked. In a CL.TE attack, the front end trusts the length and the back end trusts the chunks:

    POST / HTTP/1.1
    Host: shop.example
    Content-Length: 13
    Transfer-Encoding: chunked
    
    0
    
    SMUGGLED

    The front end forwards all 13 body bytes. The back end reads the zero-size chunk, decides the request is over, and leaves SMUGGLED on the connection as the start of the next request, possibly another user’s. RFC 9112 says a message with both headers “ought to be handled as an error”. James Kettle’s 2019 research revived this 2005 technique against real sites, including PayPal’s login page, for over $70,000 in bug bounties. Defences: HTTP/2 end to end, which has one framing mechanism (downgrading to HTTP/1.1 brings the risk back); front ends that normalise or reject ambiguous requests; back ends that reject them and close the connection.

    Open proxies

    A forward proxy that relays for anyone will be found and abused for spam (CONNECT to port 25), scraping and credential stuffing from your IPs; MDN reckons there are likely hundreds of thousands of open proxies. Squid’s recommended configuration denies all but localhost until you add rules, refuses CONNECT except to TLS ports, and blocks link-local metadata addresses. A reverse proxy that picks its upstream from client input, such as the Host header, is an open proxy too: route only to an allowlist.

    Cheat sheet

    Term Remember it as
    Forward proxy Works for clients; the server sees the proxy’s IP
    Reverse proxy (RFC 9110: gateway) Works for servers; the client thinks it is the server
    Tunnel Blind byte relay; CONNECT host:443 opens one
    TLS interception Corporate-CA certificate; proxy sees everything; client can’t verify the server
    Egress proxy Fixed IPs for partners, hostname ACLs, blocks private and metadata addresses
    NAT gateway L3/L4 address rewriting; fixed IPs, no hostname policy
    Reverse proxy jobs TLS, limits, WAF, routing, caching, compression, buffering, pooling
    Buffering Frees workers from slow clients; turn off for SSE and streams
    Fan-in Upstream connections ≈ in-flight requests = rate × latency
    X-Forwarded-For Each hop appends; read from the right, skip your own hops
    PROXY protocol One header per TCP connection for L4 hops; only from trusted senders
    Request smuggling CL vs TE disagreement; normalise, reject, prefer HTTP/2 end to end

    How to say it in the interview

    “A proxy makes requests on someone’s behalf; the question is whose. A forward proxy works for clients: our services’ outbound calls go through an egress proxy, so the payment partner allowlists two fixed IPs, every call is logged, and webhook URLs can’t reach internal addresses. HTTPS crosses it as a CONNECT tunnel, so it never sees payloads. A reverse proxy works for us: clients connect to nginx or Envoy thinking it’s the server. It terminates TLS, enforces limits, routes by path, caches, buffers slow mobile clients, and funnels thousands of client connections into a small warm pool. Our load balancer, gateway and CDN are specialised reverse proxies. The app takes the client IP from X-Forwarded-For, trusting only entries our proxies added, and I’d run at least two proxies per zone.”

    How does the app learn the user’s real IP behind two proxies? Each proxy appends the address it received the connection from to X-Forwarded-For. The app reads from the right, skips its own proxies’ addresses, and takes the first one left, never the leftmost, which the client wrote. L4 hops use the PROXY protocol instead.

    Can the company proxy see my banking password? Through a CONNECT tunnel, no: it sees the hostname, port and volume. If IT installed its own root CA on your laptop and the proxy intercepts TLS, yes: it decrypts, inspects and re-encrypts everything.

    Reverse proxy, load balancer, API gateway: what’s the difference? Emphasis. A load balancer spreads traffic across healthy backends; an API gateway adds authentication, quotas and aggregation; “reverse proxy” is the general tool underneath, and Envoy or nginx can play all three roles.

    Isn’t the reverse proxy a single point of failure? Only if you run one. Run at least two per zone behind DNS, anycast or a cloud L4 balancer, sized N+1, stateless and health-checked.

    Server-sent events arrive in bursts through nginx. Why? Response buffering: nginx holds upstream output until a buffer fills or the response ends. Turn proxy_buffering off for that route or send X-Accel-Buffering: no, and keep proxy_read_timeout above the heartbeat interval.

    Where to go next

    References and further reading

    1. RFC 9110 — HTTP Semantics (sections 3.7, 7.6.3 and 9.3.6)rfc-editor.orgDefinitions of proxy, gateway (reverse proxy), tunnel and interception proxy; the Via header; CONNECT and its port restrictions.
    2. RFC 9112 — HTTP/1.1 (sections 3.2.2, 6.3 and 11.2)rfc-editor.orgAbsolute-form request targets sent to proxies, message-length precedence rules, and request smuggling.
    3. RFC 7239 — Forwarded HTTP Extensionrfc-editor.orgThe standard Forwarded header with for, by, host and proto, and why its contents cannot be trusted blindly.
    4. MDN — X-Forwarded-Fordeveloper.mozilla.orgList order, spoofing risks, and the trusted-proxy-count and trusted-proxy-list methods for picking the client address.
    5. The PROXY protocol, versions 1 and 2 (HAProxy Technologies)haproxy.orgHeader format and sizes, and the rules against guessing its presence or using it on multiplexed connections.
    6. nginx — ngx_http_proxy_modulenginx.orgproxy_buffering, proxy_request_buffering, buffer sizes, read timeouts, proxy_cache_lock and the upstream HTTP version.
    7. Smokescreen, Stripe’s egress proxy (GitHub)github.comA CONNECT proxy that gives partners stable egress IPs, checks hostnames per service and refuses internal addresses.
    8. How we built Pingora, the proxy that connects Cloudflare to the Internetblog.cloudflare.comConnection reuse rising from 87.1% to 99.92% for one customer, and the TTFB gains from a shared upstream pool.
    9. PortSwigger Web Security Academy — HTTP request smugglingportswigger.netCL.TE, TE.CL and TE.TE desyncs between front-end and back-end servers, and the recommended defences.
    10. CISA alert TA17-075A — HTTPS Interception Weakens TLS Securitycisa.govHow TLS-inspecting proxies work and why clients can no longer verify the real server.