Net-Base Maġazin

23.05.2026

Reverse Proxy ma' nginx u Delphi: maniġġjar nadif tal-header Forwarded, indirizz IP veru tal-klijent u bażijiet URL robusti

Meta Delphi-REST-server jitħaddmu wara nginx, spiss jintilfu l-Client-IP, id-detekzzjoni ta' HTTPS u l-URLs assoluti. Dan is-snipet tas-sors juri immaniġġjar robust ta' Forwarded/X-Forwarded (inkl. lista ta' Trust-Proxy), konfigurazzjonijiet tipċi ta' nginx u suġġerimenti għall-debugging fil-operazzjoni.

23.05.2026

Reverse Proxy ma‘ nginx u Delphi huwa fil-prattika spiss mhux biss „nice to have“, iżda t-tqassim nadif bejn il-bord tal-Internet u l-applikazzjoni: terminazzjoni TLS (HTTPS-Offloading), regoli ċentrali tal-header/CORS, Rate-Limits, logs uniformi, Blue/Green-Rollouts jew sempliċiment il-hosting ta‘ diversi servizzi taħt domain waħda. Dak li ħafna drabi jiġi sottovalutat: ladarba nginx ikun „quddiemu“, is-server Delphi jara biss l-IP tal-proxy, spiss biss „http“ minflok „https“ u jġġenera links assoluti żbaljati (Redirects, Callback-URLs, OpenAPI-Server-URL). Dawn eżattament tliet punti jikkawżaw aktar tard ħin ta‘ debugging fil-produzzjoni.

Din is-snippet tas-sors turi mudell robust kif tista‘ f’Delphi Forwarded jew X-Forwarded-* tivvaluta b’mod nadif – inkluż Trust-Proxy-Liste (importanti kontra Header-Spoofing) u Request-Base-URL konsistenti. Hemm ukoll konfigurazzjonijiet nginx prattiċi u għajnuniet għal każijiet tal-fruntiera bħal WebSockets, upload kbar u timeouts.

Għaliex setups ta‘ Reverse Proxy jikkonfondu s-server Delphi

nginx jaħdem bħala Reverse Proxy u tipikament jitkellem mas-servizz Delphi mingħajr enkrizzjoni (HTTP) fil-nett intern jew fuq localhost, filwaqt li l-klijent jasal minn barra permezz ta‘ HTTPS. Mingħajr header addizzjonali, Delphi ma jifhimx:

  • Skema oriġinali (https vs. http) – rilevanti għal Redirects u URLs assoluti.
  • Host oriġinali (dominja speċifika għall-klijent, port) – rilevanti għal setups Multi-Tenant, CORS u Callback-URLs.
  • IP tal-klijent oriġinali – rilevanti għal audit, Rate-Limits, Geo-Checks u analiżi tas-sigurtà.

nginx jista‘ jġorr din l-informazzjoni permezz ta‘ header. Komuni huma X-Forwarded-For, X-Forwarded-Proto u X-Forwarded-Host; standardizzat hemm ukoll l-RFC-header Forwarded. Importanti: dawn il-header mhumiex awtomatikament f’pożizzjoni li tiġi fdata mill-applikazzjoni, għax klijent jista‘ jżomm jew jibgħathom direttament – isiru affidabbli biss meta jiġu minn proxy magħruf.

nginx-Konfiguration: die minimal sinnvollen Proxy-Header

Punt solidu ta‘ bidu (HTTP/1.1, Keep-Alive, Upgrade għal WebSockets) jidher hekk. Is-snippet hu maħsub biex ikun kompatt; inti żżid skont l-ambjent HSTS, Rate-Limits u Access-Logs.

Delphi
# (nginx-Konfiguration, kein Delphi)
server {
  listen 443 ssl http2;
  server_name api.example.com;

  # ssl_certificate ...; ssl_certificate_key ...;

  location / {
    proxy_http_version 1.1;
    proxy_set_header Host              $host;
    proxy_set_header X-Real-IP         $remote_addr;
    proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    # optional, aber praktisch für absolute URLs
    proxy_set_header X-Forwarded-Host  $host;
    proxy_set_header X-Forwarded-Port  $server_port;

    # WebSockets / Upgrade
    proxy_set_header Upgrade           $http_upgrade;
    proxy_set_header Connection        $connection_upgrade;

    proxy_pass http://127.0.0.1:8080;

    # Timeouts passend zu Delphi-Backend (lange Reports/Exports)
    proxy_connect_timeout 5s;
    proxy_send_timeout    60s;
    proxy_read_timeout    60s;

    # große Uploads explizit steuern
    client_max_body_size 50m;
  }
}

Skop: L-applikazzjoni tirċievi b’mod affidabbli l-Host, l-IP tal-klijent u l-iskema. Kundizzjoni limiti: $proxy_add_x_forwarded_for iżżid l-IP tal-proxy attwali ma‘ katina possibbli; dan huwa utli għal setups b’ħafna proxy, iżda jagħmilha saħansitra iktar importanti li jiġi eżaminat b’mod korrett fuq il-paġna Delphi. Falliment komuni: Jekk fl-nginx ma tissettjax il-header Host, jista‘ jkun li Delphi jarah biss il-Upstream-Host (127.0.0.1), li jikkawża problemi f’redirects u origin-checks.

Delphi Snippett tas-Sors: Valutazzjoni robusta ta‘ Forwarded/X-Forwarded (bi lista ta‘ proxy affidabbli)

Il-kodiċi li ġej huwa intenzjonalment neutru rigward il-framework: jaħdem fuq interface minimali (Header + RemoteIP) u jista‘ jiġi adattat għal WebBroker, RAD Server jew Horse. Punti ewlenin:

  • Prijorità: RFC Forwarded (jekk jinstab) qabel X-Forwarded-*.
  • Trust: Eżamina l-Forwarded-Header biss meta l-peer dirett (RemoteIP) huwa proxy magħruf.
  • Parsing: Ikkunsidra IPv6, quotes, ports u katini f’X-Forwarded-For.
  • Output: Base-URL li tista‘ tuża għal linkijiet assoluti, redirects jew OpenAPI.
Delphi
unit Net-Base.ProxyForwarding;

interface

uses
  System.SysUtils, System.Classes, System.Generics.Collections, System.NetEncoding;

type
  // Interface minimu għal adapter: implemmentah għal WebBroker/Horse/etc.
  IHeaderReader = interface
    ['{C2D2E5B9-2C2E-4D37-9D73-3CDB5A7E7EEA}']
    function GetHeaderValue(const AName: string): string;
    function GetRemoteIP: string; // kontra-parti TCP diretta (normalment nginx)
  end;

  TForwardedInfo = record
    ClientIP: string;
    Proto: string; // http/https
    Host: string;
    Port: Integer;
    function EffectiveScheme: string;
    function EffectiveHostPort: string;
    function BaseUrl: string; // eż. https://api.example.com
  end;

  TTrustedProxyList = class
  private
    FSet: TDictionary;
    class function NormalizeIp(const AIP: string): string; static;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Add(const AIP: string);
    function Contains(const AIP: string): Boolean;
  end;

function ResolveForwardedInfo(const Req: IHeaderReader; const TrustedProxies: TTrustedProxyList): TForwardedInfo;

implementation

function StripBrackets(const S: string): string;
begin
  Result := S.Trim;
  if (Result.StartsWith('[')) and (Result.EndsWith(']')) then
    Result := Result.Substring(1, Result.Length - 2);
end;

function RemoveQuotes(const S: string): string;
var
  T: string;
begin
  T := S.Trim;
  if (T.Length >= 2) and (((T[1] = '"') and (T[T.Length] = '"')) or ((T[1] = '''') and (T[T.Length] = ''''))) then
    Result := T.Substring(1, T.Length - 2)
  else
    Result := T;
end;

function FirstCsvToken(const S: string): string;
var
  P: Integer;
begin
  // X-Forwarded-For jista' jkun 'client, proxy1, proxy2'
  P := S.IndexOf(',');
  if P >= 0 then
    Result := S.Substring(0, P).Trim
  else
    Result := S.Trim;
end;

procedure SplitHostPort(const HostPort: string; out Host: string; out Port: Integer);
var
  S: string;
  P: Integer;
begin
  Host := '';
  Port := 0;
  S := HostPort.Trim;

  // IPv6 in []: [2001:db8::1]:443
  if S.StartsWith('[') then
  begin
    P := S.IndexOf(']');
    if P >= 0 then
    begin
      Host := StripBrackets(S.Substring(0, P + 1));
      if (P + 1 < S.Length) and (S[P + 2] = ':') then
        Port := StrToIntDef(S.Substring(P + 2), 0);
      Exit;
    end;
  end;

  // IPv4/Host: host:port (Attenzjoni: ma jkunx affidabbli ma' IPv6 mingħajr [])
  P := S.LastIndexOf(':');
  if (P > 0) and (S.IndexOf(':') = P) then
  begin
    Host := S.Substring(0, P);
    Port := StrToIntDef(S.Substring(P + 1), 0);
  end
  else
    Host := S;
end;

function ParseForwardedHeader(const ForwardedValue: string; out ClientIP, Proto, Host: string): Boolean;
var
  // Eżempju: Forwarded: for=203.0.113.43;proto=https;host=api.example.com
  Parts: TArray<string>;
  I: Integer;
  KV: TArray<string>;
  K, V: string;
  FirstElement: string;
begin
  Result := False;
  ClientIP := '';
  Proto := '';
  Host := '';

  if ForwardedValue.Trim = '' then
    Exit;

  // Bosta elementi jinqasmu b'virgola; nieħdu l-ewwel (l-iktar qrib il-klijent)
  FirstElement := FirstCsvToken(ForwardedValue);
  Parts := FirstElement.Split([';']);
  for I := 0 to High(Parts) do
  begin
    KV := Parts[I].Split(['='], 2);
    if Length(KV) <> 2 then
      Continue;
    K := KV[0].Trim.ToLower;
    V := RemoveQuotes(KV[1]);

    if K = 'for' then
    begin
      // for jista' jkun IP jew 'unknown'; IPv6 jista' jkun fi []
      V := V.Trim;
      if SameText(V, 'unknown') then
        Continue;
      // for=1.2.3.4:5678 jista' jidher
      if V.Contains(':') and (not V.StartsWith('[')) then
        V := FirstCsvToken(V); // defensiv
      ClientIP := StripBrackets(V.Split([':'])[0]);
    end
    else if K = 'proto' then
      Proto := V.Trim.ToLower
    else if K = 'host' then
      Host := V.Trim;
  end;

  Result := (ClientIP <> '') or (Proto <> '') or (Host <> '');
end;

{ TForwardedInfo }

function TForwardedInfo.EffectiveScheme: string;
begin
  if Proto <> '' then
    Exit(Proto);
  Result := 'http';
end;

function TForwardedInfo.EffectiveHostPort: string;
begin
  if Host = '' then
    Exit('');

  if (Port > 0) and not ((EffectiveScheme = 'https') and (Port = 443)) and not ((EffectiveScheme = 'http') and (Port = 80)) then
    Result := Host + ':' + Port.ToString
  else
    Result := Host;
end;

function TForwardedInfo.BaseUrl: string;
var
  HP: string;
begin
  HP := EffectiveHostPort;
  if HP = '' then
    Exit('');
  Result := EffectiveScheme + '://' + HP;
end;

{ TTrustedProxyList }

constructor TTrustedProxyList.Create;
begin
  inherited Create;
  FSet := TDictionary<string, Boolean>.Create;
end;

destructor TTrustedProxyList.Destroy;
begin
  FSet.Free;
  inherited;
end;

class function TTrustedProxyList.NormalizeIp(const AIP: string): string;
begin
  // Għall-normalizzazzjoni reali ta' IPv6 ikun meħtieġ parser tal-IP; hawnhekk nieħdu approċċ intenzjonatament pragmatiku.
  Result := AIP.Trim;
  Result := StripBrackets(Result);
end;

procedure TTrustedProxyList.Add(const AIP: string);
begin
  FSet.AddOrSetValue(NormalizeIp(AIP), True);
end;

function TTrustedProxyList.Contains(const AIP: string): Boolean;
begin
  Result := FSet.ContainsKey(NormalizeIp(AIP));
end;

function ResolveForwardedInfo(const Req: IHeaderReader; const TrustedProxies: TTrustedProxyList): TForwardedInfo;
var
  RemoteIP: string;
  Fwd, XFF, XProto, XHost, XPort: string;
  ClientIP, Proto, Host: string;
  Port: Integer;
begin
  Result := Default(TForwardedInfo);

  RemoteIP := Req.GetRemoteIP;
  Result.ClientIP := RemoteIP; // Fallback: kontra-parti diretta

  // Jekk biss il-kontra-parti diretta hi proxy magħruf, nipproċessaw il-header Forwarded.
  if (TrustedProxies <> nil) and TrustedProxies.Contains(RemoteIP) then
  begin
    Fwd := Req.GetHeaderValue('Forwarded');
    if ParseForwardedHeader(Fwd, ClientIP, Proto, Host) then
    begin
      if ClientIP <> '' then Result.ClientIP := ClientIP;
      if Proto <> '' then Result.Proto := Proto;
      if Host <> '' then
      begin
        SplitHostPort(Host, Result.Host, Result.Port);
      end;
    end;

    // X-Forwarded-* bħala fallback/komplement
    XFF := Req.GetHeaderValue('X-Forwarded-For');
    if (Result.ClientIP = RemoteIP) and (XFF.Trim <> '') then
      Result.ClientIP := StripBrackets(FirstCsvToken(XFF));

    XProto := Req.GetHeaderValue('X-Forwarded-Proto');
    if (Result.Proto = '') and (XProto.Trim <> '') then
      Result.Proto := FirstCsvToken(XProto).ToLower;

    XHost := Req.GetHeaderValue('X-Forwarded-Host');
    if (Result.Host = '') and (XHost.Trim <> '') then
      Result.Host := FirstCsvToken(XHost);

    XPort := Req.GetHeaderValue('X-Forwarded-Port');
    Port := StrToIntDef(FirstCsvToken(XPort), 0);
    if (Result.Port = 0) and (Port > 0) then
      Result.Port := Port;
  end;

  // Jekk kollha jfallu, tieħu Host mill-header 'Host' (jekk m'hemmx header ta' proxy)
  if Result.Host = '' then
  begin
    Host := Req.GetHeaderValue('Host');
    if Host <> '' then
      SplitHostPort(Host, Result.Host, Result.Port);
  end;
end;

end.

Skop: Inti tirċievi minn kull request veduta konsistenti ta‘ ClientIP, Proto u Host kif ukoll BaseUrl. Dawn l-informazzjonijiet jistgħu jintużaw ċentralment għall-logging, deċiżjonijiet tas-sigurtà (eż. IP-Allowlist) u ġenerazzjoni ta‘ links.

Għaliex il-lista tal-Trust-Proxy hija meħtieġa: Mingħajr verifika tal-Trust jista‘ attakkant jilħaq direttament il-port Delphi tiegħek (konfigurazzjoni żbaljata, routing intern, VPN) u sempliċiment jibgħat X-Forwarded-For: 127.0.0.1. B’hekk il-audit-trails, il-rate-limits jew endpoints “permessi biss internament” ikunu attakkabbli. Tafda l-Forwarded-Header biss jekk il-peer dirett (RemoteIP) huwa proxy li tikkontrolla (eż. 127.0.0.1, Load-Balancer-IP, Kubernetes-Ingress).

Perikli: IPv6 mingħajr parentesijiet karrati mhix ċara fil-notazzjoni Host:port. Fil-HTTP-Host-Header IPv6 normalment ikun indikjat f‘[]; żomm din il-konvenzjoni. Għal IP-range kumplessi (CIDR) għandek tespandi l-lista tal-Trust (per eżempju billi tapplika parsing veru tal-IP).

Integration in WebBroker/Horse/RAD Server: fejn jinqabad il-kodiċi „andockt“

F’WebBroker (TWebRequest) il-headers tipikament jaslu permezz ta‘ ContentFields jew GetFieldByName, u l-Remote-IP jiddependi fuq il-backend tas-server. F’Horse (jew f’frameworks oħra HTTP) normalment issib Req.Headers u proprjetà Remote-IP. Il-prinċipju importanti: RemoteIP trid tkun il-kontra-parti TCP, mhux xi valur mill-header.

Prattikament provat: fil-bidu tas-servizz toħloq TTrustedProxyList mill-konfigurazzjoni (INI/ENV), pereżempju „127.0.0.1“ għal setups lokali ta‘ nginx jew l-IP tal-load balancer tiegħek. Imbagħad sejħu ResolveForwardedInfo għal kull request u kittbu l-kampjiet fil-logging strutturat tiegħek (JSON-Log, Syslog jew Windows Event Log).

Debugging im Betrieb: so finden Sie Fehler in Minuten statt Stunden

Jekk il-requests jidhru “strambi”, rari tkun il-problema f’Delphi-HTTP innifsu; aktar komunement huwa kombinazzjoni ta‘ proxy-headers, redirect-logic u timeouts. Tlieta kontrolli ta‘ debugging li jissalvaw ħin fil-prattika:

  1. Header-Dump (mirat): Ieħdu log meta jseħħu 4xx/5xx u inkludi addizzjonalment Host, Forwarded, X-Forwarded-For, X-Forwarded-Proto, User-Agent u Request-URI. Iżda biss f’każ ta‘ żball – inkella l-log jinqala‘ u jsir iktar diffiċli biex jiġi analizzat.
  2. Iċċekkja l-Base-URL: Jekk redirects jew callback-URLs jonqsu, itella‘ fil-log ForwardedInfo.BaseUrl. Ħafna żbalji jidhru immedjatament (“http://127.0.0.1” minflok “https://api…”).
  3. Korelazzjoni tat-Timeouts: 504 mill-proxy mhux l-istess bħal timeout fuq Delphi. nginx proxy_read_timeout u t-timeouts ta‘ idle/read fuq in-naħa ta‘ Delphi iridu jaqblu.

Randfälle: WebSockets, Streaming und große Requests

WebSockets hinter nginx

Għal WebSockets nginx jeħtieġ li Upgrade u Connection ikunu korretti. Barra minn hekk il-backend ma jistax jagħlaq “qabel iż-żmien”. Minn naħa ta‘ Delphi huwa rilevanti li l-komponent WebSocket tiegħek (jew endpoint SSE/streaming) ikun kapaċi jimmaniġġja reverse proxies u jkollu implimentati b’mod sod il-heartbeats/keep-alives.

Große Uploads und 413-Fehler

Klassiċi: Delphi jaċċetta upload, imma nginx jibblokka qabel b‘413 Request Entity Too Large. Immaniġġja dan b’mod espliċitu permezz ta‘ client_max_body_size u adattaw il-limits tal-requests fuq in-naħa ta‘ Delphi. Għal soluzzjonijiet tas-softwer viċin il-proċess li jimmaniġġjaw dokumenti jew immaġini dan mhux każ straordinarju, imma operazzjoni normali.

HTTPS-Offloading und „Secure Cookies“

Jekk is-servizz Delphi tiegħek jissettja cookies tas-session, dawn normalment ikunu meħtieġa li jkunu markati bħala Secure meta HTTPS estern ikun involut. Jekk l-applikazzjoni tiegħek tagħmel dan spiss jiddependi fuq jekk hijiex „tafu“ li r-reqwastru inizjali kien HTTPS. Hawnhekk l-analiżi konsistenti ta‘ X-Forwarded-Proto/Forwarded tgħin.

Meta l-isforz jiswa – u fejn jista‘ jinkiser

L-approċċ muri jiswa partikolarment meta s-servizz Delphi m’għadux iżolat fil-LAN iżda jkun parti minn kantuniera tal-produzzjoni: bosta dominii, interfaċċji SSO/SAML, Public APIs, appoġġ għal multi-tenant jew rekwiżiti ta‘ audit aktar stretti. Jista‘ jinkiser fejn wieħed jiffida bl-Forwarded headers mingħajr verifika jew meta topology ta‘ proxy mhijiex dokumentata (multipli livelli ta‘ ingress, Cloud-LB plus nginx plus sidecar). F’dawn is-sitwazzjonijiet il-Client-IP u s-scheme malajr isiru „xi ħaġa“.

Żona ċara: jekk għandek bżonn regoli ta‘ fiduċja kumplessi (CIDR, netwerks IPv6, LB-IPs dinamici), trid twessa‘ l-kontroll tal-fiduċja (parsing reali tal-indirizzi IP, masks tan-netwerk) jew tiddeżinja l-infrastruttura sabiex biss proxy definit jikkessa‘ l-port tal-Delphi (Firewall/Security Groups). Fl-aħħar, din spiss tkun id-deċiżjoni operattiva l-iktar robusta.

Konklużjoni: Tlesta t-tmexxija ta‘ Reverse Proxy bil-nginx u Delphi b’mod nadif tfisser „Forwarded tagħmilha b’mod korrett“

Reverse Proxy bil-nginx huwa komponent standard tajjeb għal Delphi-REST-Server – iżda huwa l-immaniġġjar korrett ta‘ Forwarded u X-Forwarded-* li jagħmel l-setup stabbli fil-produzzjoni. Il-kern hu sempliċi: taċċetta headers biss minn proxies affidabbli, iskopri b’mod konsistenti Client-IP/Scheme/Host u applika din il-bażi f’redirects, logging u checkijiet tas-sigurtà. Bis-snippet imsemmi hawn fuq għandek fondazzjoni nadifa u kompatibbli ma‘ legacy li tista‘ tintegra ma‘ WebBroker, Horse jew servers HTTP proprji.

Jekk trid konsolidar backend Delphi eżistenti wara nginx jew modernizza lejn Delphi REST-API und REST-Server bi-linja tal-operazzjoni ċara, reviżjoni teknika tal-katina ta‘ proxy u tal-analiżi tal-headers spiss tkun l-aktar lever mgħaġġel. Ikkuntattja Net-Base għal klassifikazzjoni teknika qasira.

Fil-kuntest professjonali, il-Nginx Reverse Proxy u l-Forwarded headers għandhom rwol importanti meta integrazzjonijiet, flussi ta‘ data u evoluzzjoni żvilupp għandhom jiffunzjonaw b’mod nadif flimkien.

Diskuti proġett jew inizjattiva ta‘ modernizzazzjoni ma‘ Net-Base.

Aqsam il-post

Aqsam dan il-post direttament

LinkedIn, X, XING, Facebook, WhatsApp u E-Mail huma immedjatament disponibbli. Għal Instagram nippreparaw il-link u t-test qasir direttament.

Imejl

Instagram jiftaħ f'tab ġdid. Il-link u t-test qasir jiġu kkopjati qabel fil-clipboard.