We wrote earlier about hardening the HTTP server. This post is the other half of the story. A client is normally treated as the trusted side of the conversation, but it is not: it sends your API keys, it follows whatever redirect the remote host returns, it reads the whole response into memory, and it stores whatever cookies the response asks it to store. A hostile or simply compromised endpoint can turn all four of those behaviours against you.
sgcWebSockets 2026.7 adds a set of client-side switches that close each of those holes. Every one of them is off by default, so upgrading changes nothing in an existing application until you decide to opt in. They are available on the low level HTTP client, on every ready-made REST and AI client, and on the HTTP/2 and HTTP/3 clients.
Credentials that follow a redirect
The classic client-side leak. You call an API with an Authorization header, the server answers with a 302 pointing at a different host, and the client dutifully repeats the request there, header and cookies included. Your bearer token has just been handed to a machine you never intended to talk to. The redirect does not even need to be malicious, an attacker who can influence an open redirect on the legitimate host is enough.
StripAuthOnCrossHostRedirect drops the Authorization and Cookie headers as soon as a redirect leaves the original host. The request still follows the redirect, it just arrives without your credentials.
uses
sgcHTTP_Client;
var
oHTTP: TsgcIdHTTP;
begin
oHTTP := TsgcIdHTTP.Create(nil);
try
oHTTP.StripAuthOnCrossHostRedirect := True;
oHTTP.Request.CustomHeaders.Values['Authorization'] := 'Bearer ' + vToken;
vResponse := oHTTP.Get('https://api.example.com/v1/account');
finally
oHTTP.Free;
end;
end;
Redirects that downgrade to plain HTTP
The second redirect problem. You start on https://, the server responds with a Location that begins with http://, and the client happily repeats the request in clear text. Anything on the path can now read the body and rewrite the response. This is exactly the downgrade that HSTS exists to prevent in browsers, and until now the HTTP client had no equivalent.
NoInsecureRedirect refuses a redirect that would move the request from HTTPS to HTTP. The redirect is rejected instead of being followed.
oHTTP.NoInsecureRedirect := True;
// A 302 from https://api.example.com to http://api.example.com
// is now blocked instead of silently downgraded.
vResponse := oHTTP.Get('https://api.example.com/v1/data');
The two redirect switches are independent, and in practice you want both. Turn on the first one and a cross-host redirect no longer carries your token. Turn on the second one and a redirect can no longer strip your transport security.
Responses with no size limit
A default HTTP client reads whatever the server sends. If the server sends a response body of ten gigabytes, or a chunked response where a single chunk announces a length of two gigabytes, the client tries to allocate room for it. That is a one-line denial of service against your own process, and it does not require the attacker to break anything, only to answer your request with more data than you expected.
Two caps close this. MaxResponseSize bounds the total size of a response body, and MaxChunkSize bounds a single chunk of a chunked response. Both take zero as "unlimited", which is the default, so nothing changes until you set them. When a limit is exceeded, the read is aborted and an exception is raised, the client does not keep growing the buffer.
uses
sgcHTTP_Client;
var
oHTTP: TsgcIdHTTP;
begin
oHTTP := TsgcIdHTTP.Create(nil);
try
oHTTP.MaxResponseSize := 10 * 1024 * 1024; // 10 MB total, 0 = unlimited
oHTTP.MaxChunkSize := 1024 * 1024; // 1 MB per chunk, 0 = unlimited
vResponse := oHTTP.Get('https://api.example.com/v1/report');
finally
oHTTP.Free;
end;
end;
Pick a number that reflects what the endpoint is actually supposed to return. If you are calling a JSON API that answers with a few kilobytes, a cap of a few megabytes is generous and still turns an unbounded read into a bounded one.
Cookies claimed for a whole TLD
A response can send a cookie with a Domain attribute. Nothing in the wire format stops it from claiming Domain=com or Domain=co.uk. A client that accepts that is now willing to send the cookie to every host under that suffix, which is the cookie equivalent of handing out a skeleton key. Browsers have refused this for years using the Public Suffix List.
RejectPublicSuffixCookies brings the same rule to the component. A cookie whose domain is a public suffix rather than a real registrable domain is discarded. Note that enabling it also turns on cookie management on the client, because a cookie jar is what it filters.
oHTTP.RejectPublicSuffixCookies := True;
// Set-Cookie: session=abc; Domain=co.uk -> rejected
// Set-Cookie: session=abc; Domain=example.co.uk -> accepted
2026.7 also fixes two related cookie and redirect bugs that were quietly wrong before. Relative Location headers are now resolved into absolute URLs correctly, a 307 or 308 redirect now preserves the request method and body as the spec requires, and a cookie whose expiry date cannot be parsed is treated as a session cookie instead of being thrown away. Max-Age still wins over Expires when both are present.
Uploading a big body: Expect: 100-continue and chunked requests
Not every new option is about attacks, two of them are about not wasting bandwidth. If you POST a large body to an endpoint that is going to reject it (bad credentials, wrong content type, a quota that is already exhausted), the default behaviour is to push the entire body up the wire first and read the 401 afterwards.
UseExpect100Continue sends the request headers with an Expect: 100-continue and waits for the server to say it is willing to take the body. If the server answers with a final status instead, you never send the payload. If no go-ahead arrives at all, the body is sent anyway, so a server that ignores the header still works.
UseChunkedTransferEncoding covers the opposite case, an upload whose size you do not know in advance. The body is sent in chunks instead of with a fixed Content-Length, so you can stream it out of a generator, a compressor or a pipe without buffering the whole thing first.
uses
Classes, sgcHTTP_Client;
var
oHTTP: TsgcIdHTTP;
oBody: TStream;
begin
oHTTP := TsgcIdHTTP.Create(nil);
try
oHTTP.UseExpect100Continue := True; // ask before uploading
oHTTP.UseChunkedTransferEncoding := True; // size not known up front
oBody := TFileStream.Create('large_upload.bin', fmOpenRead or fmShareDenyWrite);
try
vResponse := oHTTP.Post('https://api.example.com/v1/upload', oBody);
finally
oBody.Free;
end;
finally
oHTTP.Free;
end;
end;
The same switches on the ready-made API clients
The interesting part is where these properties live. They are re-exposed on TsgcHTTPAPI_client, the base class of every ready-made REST client in the library. That means you set them directly on the component you are already using, without reaching for the low level HTTP object underneath.
That covers the AI and LLM clients (OpenAI, Anthropic, Gemini, DeepSeek, Grok, Mistral, Ollama), the OAuth2 client, Google Cloud, WhatsApp, AWS SQS and WebPush, and the Amazon AWS and Google Cloud API units.
uses
sgcHTTP_API_OpenAI;
var
oOpenAI: TsgcHTTP_API_OpenAI;
begin
oOpenAI := TsgcHTTP_API_OpenAI.Create(nil);
try
oOpenAI.StripAuthOnCrossHostRedirect := True; // never leak the API key
oOpenAI.NoInsecureRedirect := True; // never fall back to http
oOpenAI.RejectPublicSuffixCookies := True;
oOpenAI.MaxResponseSize := 32 * 1024 * 1024;
oOpenAI.MaxChunkSize := 1024 * 1024;
// ... use the client as usual
finally
oOpenAI.Free;
end;
end;
An API key sent to an LLM provider is exactly the kind of credential you do not want following a redirect off the provider's domain, so this is the place where turning the switches on costs you nothing and buys you a lot.
HTTP/2 and HTTP/3
The HTTP/2 client gets the same protections through its options object, TsgcWSHTTP2Client_Options, and the HTTP/3 client exposes the response cap directly.
uses
sgcHTTP;
var
oHTTP2: TsgcHTTP2Client;
begin
oHTTP2 := TsgcHTTP2Client.Create(nil);
try
oHTTP2.HTTP2Options.StripAuthOnCrossHostRedirect := True;
oHTTP2.HTTP2Options.NoInsecureRedirect := True;
oHTTP2.HTTP2Options.MaxResponseSize := 10 * 1024 * 1024;
finally
oHTTP2.Free;
end;
end;
// HTTP/3
oHTTP3.MaxResponseSize := 10 * 1024 * 1024;
One line for a secure TLS baseline
None of the above helps if the TLS layer is not verifying anything. sgcWebSockets 2026.7 adds a preset for that. Every TLS options object in the library, on WebSocket and HTTP components alike, publishes a Preset property of type TsgcTLSPreset. It defaults to tlspCustom, which keeps whatever you configured by hand.
Setting it to tlspSecureDefaults switches on certificate verification, TLS 1.2 or higher, and hostname verification in a single line.
uses
sgcWebSocket_Types;
begin
oHTTP.TLSOptions.Preset := tlspSecureDefaults;
// certificate verification on, TLS 1.2+, hostname verification on
end;
Combine the preset with the redirect switches and the response caps and you have a client that verifies who it is talking to, refuses to downgrade, does not hand its credentials to a third party, and cannot be made to allocate unbounded memory.
.NET
The managed edition ships the same protections in 2026.7. Redirect credential stripping, the HTTPS downgrade block, the per-chunk and total response caps, public suffix cookie rejection, Expect: 100-continue and chunked request bodies are all there, including the HTTP/2 client set. Same names, C# casing.
client.StripAuthOnCrossHostRedirect = true;
client.NoInsecureRedirect = true;
client.RejectPublicSuffixCookies = true;
client.MaxResponseSize = 10 * 1024 * 1024;
client.MaxChunkSize = 1024 * 1024;
Availability
The HTTP client hardening options are available in sgcWebSockets 2026.7 across Delphi 7 through 13 and C++Builder (Win32/Win64, Linux64, macOS, Android and iOS), and in the .NET edition. Everything described here is opt-in and off by default, so an upgrade is a drop-in.
Customers with an active subscription can download the new build from the customer area. Trial users can grab the updated installer at esegece.com/products/websockets/download.
Questions, feedback or help deciding which of these switches your application should turn on? Get in touch, you will get a reply from the people who wrote the code.
