Skip to content

HTTP Header Convention

This document defines all HTTP request headers recognized by the SlaunchX backend, categorized by origin layer. It serves as the single source of truth for frontend developers and internal teams.

Architecture Overview

Requests pass through four layers before reaching the backend application:

Client (Browser / API Caller)
  -> Cloudflare (CDN + Edge Security)
    -> Nginx (Gateway Reverse Proxy)
      -> Spring Boot (Backend Application)

Each layer may inject, pass through, or strip headers. The table below classifies all headers by origin and trust level.


L1: Client Headers (Set by Frontend / API Caller)

These headers are explicitly set by the caller. The backend validates but does not blindly trust them.

Web Chain (JWT Authentication)

HeaderRequiredDescription
AuthorizationYesBearer <JWT> token obtained during login flow
X-LOCALENoApplication language preference (e.g., en-US, zh-CN)
X-Client-HashConditionalClient fingerprint hash. Required when endpoint enables fingerprint validation (@GatewayValidation with FingerprintHandler)
X-Workspace-IdNoWorkspace identifier for multi-workspace users
X-Turnstile-TokenConditionalCloudflare Turnstile challenge token. Required on endpoints with enableTurnstile=true. Alternative: cf-turnstile-response query parameter

API Chain (HMAC Authentication)

HeaderRequiredDescription
X-Api-KeyYesAPI Key identifier (e.g., sk_live_abc123)
X-TimestampYesUnix timestamp (seconds, UTC). Server rejects requests with drift > 60 seconds
X-NonceYesUnique request identifier (UUID v4 recommended). Prevents replay attacks
X-SignatureYesHMAC-SHA256 signature, Base64 encoded. See authentication guide for computation method
X-LOCALENoApplication language preference

L2: Browser Automatic Headers (Transparently Forwarded)

These headers are set by the browser engine and forwarded as-is through Cloudflare and Nginx. Non-browser clients can spoof them.

HeaderDescriptionConsumer
User-AgentBrowser/client identification stringSession fingerprint, risk engine, audit logs
Accept-LanguageBrowser language preferenceRisk engine context
Sec-CH-UAClient Hints: browser brand and versionSession fingerprint
Sec-CH-UA-PlatformClient Hints: operating systemSession fingerprint
Sec-CH-UA-MobileClient Hints: mobile device flag (?0 or ?1)Session fingerprint

Security Note: These headers participate in session fingerprint computation (SessionFingerprintExtractor). A fingerprint mismatch between login and subsequent requests triggers FINGERPRINT_MISMATCH (error code 50030101).


L3: Cloudflare Injected Headers (Trusted, Client Cannot Forge)

These headers are added by Cloudflare edge nodes. The backend treats them as authoritative. Clients cannot set or override these headers -- Cloudflare strips any client-provided values.

Automatically Injected (Always Present)

HeaderDescription
CF-Connecting-IPClient real IP address. Primary source for ClientIpResolver
Cf-RayCloudflare request trace ID. Used for debugging and risk audit trails
Exposed-Credential-CheckLeaked credential detection flag. Injected when Cloudflare Leaked Credentials Detection is enabled

Managed Transforms (Requires Cloudflare Dashboard Configuration)

Requires enabling "Add visitor location headers" Managed Transform.

HeaderDescription
cf-ipcountryISO 3166-1 alpha-2 country code
cf-regionRegion or province
cf-ipcityCity
cf-iplatitudeLatitude coordinate
cf-iplongitudeLongitude coordinate
cf-postal-codePostal code
cf-timezoneClient timezone (e.g., America/Los_Angeles)

Configuration Path: Cloudflare Dashboard > Rules > Transform Rules > Managed Transforms > "Add visitor location headers".


L4: Nginx Injected Headers (Trusted, Reverse Proxy Written)

These headers are written by the Nginx reverse proxy on the gateway server. They are authoritative and cannot be influenced by clients.

HeaderDescriptionGeneration
X-Real-IPDirect connection IP (usually Cloudflare edge IP)$remote_addr
X-Forwarded-ForFull proxy chain IP list$proxy_add_x_forwarded_for
X-Forwarded-ProtoOriginal request protocolHardcoded https
X-Request-IdUnique request trace ID$msec-$connection-$connection_requests
X-PORTAL-ACCESS-CODEPortal access code resolved from subdomainNginx map block: subdomain -> access code

Portal Access Code Mapping

X-PORTAL-ACCESS-CODE is not set by the frontend. Nginx resolves it from the subdomain:

system-alpha.slaunchx.cc  -> map -> $alpha_portal_access_code = "<portal-access-code>"
<tenant>-alpha.slaunchx.cc -> map -> $alpha_portal_access_code = "<portal-access-code>"

Note: The example values above are placeholders. Actual access codes are stored in /etc/nginx/snippets/slaunchx-*-portals.conf and must not be committed to documentation.

The backend's PortalAccessCodeHandler validates this access code against the database to determine portalType and institutionBizId.


IP Resolution Priority

ClientIpResolver uses the following priority chain to determine the client's real IP:

1. CF-Connecting-IP     (L3 -- Cloudflare, authoritative)
2. X-Real-IP            (L4 -- Nginx, fallback)
3. X-Forwarded-For[0]   (L4 -- first entry, may be spoofed without CF)
4. request.getRemoteAddr() (direct connection IP, last resort)

Under the standard Cloudflare + Nginx architecture, CF-Connecting-IP is always present and used.


Code Reference

ClassModuleResponsibility
ClientHeaderExtractorslaunchx-infra-gateway-coreExtracts L1/L2/L3 headers into ClientInfo record
ClientIpResolverslaunchx-infra-gateway-coreIP resolution priority chain
SessionFingerprintExtractorslaunchx-infra-gateway-coreBuilds session fingerprint from headers
GatewayRiskContextBuilderslaunchx-infra-riskBuilds risk context from all header layers
ValidationContextslaunchx-infra-gateway-coreReads X-Request-Id and X-Forwarded-For

Security Boundaries

PrincipleImplementation
L3/L4 headers are trustedCloudflare and Nginx are the sole sources. Backend does not validate their format.
L1 headers require validationJWT is verified, HMAC is recomputed, Turnstile tokens are checked via CF API
L2 headers are informational onlyUsed for fingerprinting and risk scoring, never for access control decisions
Deny by defaultEndpoint without @ApiScope = 403. WEB chain missing X-PORTAL-ACCESS-CODE = portal resolution failure
No header forwarding to clientsL3/L4 header names and values never appear in API responses

Internal Handbook