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)
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer <JWT> token obtained during login flow |
X-LOCALE | No | Application language preference (e.g., en-US, zh-CN) |
X-Client-Hash | Conditional | Client fingerprint hash. Required when endpoint enables fingerprint validation (@GatewayValidation with FingerprintHandler) |
X-Workspace-Id | No | Workspace identifier for multi-workspace users |
X-Turnstile-Token | Conditional | Cloudflare Turnstile challenge token. Required on endpoints with enableTurnstile=true. Alternative: cf-turnstile-response query parameter |
API Chain (HMAC Authentication)
| Header | Required | Description |
|---|---|---|
X-Api-Key | Yes | API Key identifier (e.g., sk_live_abc123) |
X-Timestamp | Yes | Unix timestamp (seconds, UTC). Server rejects requests with drift > 60 seconds |
X-Nonce | Yes | Unique request identifier (UUID v4 recommended). Prevents replay attacks |
X-Signature | Yes | HMAC-SHA256 signature, Base64 encoded. See authentication guide for computation method |
X-LOCALE | No | Application 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.
| Header | Description | Consumer |
|---|---|---|
User-Agent | Browser/client identification string | Session fingerprint, risk engine, audit logs |
Accept-Language | Browser language preference | Risk engine context |
Sec-CH-UA | Client Hints: browser brand and version | Session fingerprint |
Sec-CH-UA-Platform | Client Hints: operating system | Session fingerprint |
Sec-CH-UA-Mobile | Client 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)
| Header | Description |
|---|---|
CF-Connecting-IP | Client real IP address. Primary source for ClientIpResolver |
Cf-Ray | Cloudflare request trace ID. Used for debugging and risk audit trails |
Exposed-Credential-Check | Leaked 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.
| Header | Description |
|---|---|
cf-ipcountry | ISO 3166-1 alpha-2 country code |
cf-region | Region or province |
cf-ipcity | City |
cf-iplatitude | Latitude coordinate |
cf-iplongitude | Longitude coordinate |
cf-postal-code | Postal code |
cf-timezone | Client 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.
| Header | Description | Generation |
|---|---|---|
X-Real-IP | Direct connection IP (usually Cloudflare edge IP) | $remote_addr |
X-Forwarded-For | Full proxy chain IP list | $proxy_add_x_forwarded_for |
X-Forwarded-Proto | Original request protocol | Hardcoded https |
X-Request-Id | Unique request trace ID | $msec-$connection-$connection_requests |
X-PORTAL-ACCESS-CODE | Portal access code resolved from subdomain | Nginx 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.confand 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
| Class | Module | Responsibility |
|---|---|---|
ClientHeaderExtractor | slaunchx-infra-gateway-core | Extracts L1/L2/L3 headers into ClientInfo record |
ClientIpResolver | slaunchx-infra-gateway-core | IP resolution priority chain |
SessionFingerprintExtractor | slaunchx-infra-gateway-core | Builds session fingerprint from headers |
GatewayRiskContextBuilder | slaunchx-infra-risk | Builds risk context from all header layers |
ValidationContext | slaunchx-infra-gateway-core | Reads X-Request-Id and X-Forwarded-For |
Security Boundaries
| Principle | Implementation |
|---|---|
| L3/L4 headers are trusted | Cloudflare and Nginx are the sole sources. Backend does not validate their format. |
| L1 headers require validation | JWT is verified, HMAC is recomputed, Turnstile tokens are checked via CF API |
| L2 headers are informational only | Used for fingerprinting and risk scoring, never for access control decisions |
| Deny by default | Endpoint without @ApiScope = 403. WEB chain missing X-PORTAL-ACCESS-CODE = portal resolution failure |
| No header forwarding to clients | L3/L4 header names and values never appear in API responses |