Networking / Intermediate: lifecycle, trust, and application protocols / Lesson 12
Follow HTTP semantics through intermediaries
Separate methods, response status, framing, connection reuse, version negotiation, proxies, and caches in an application request path.
On this page
Lesson goals
- Explain HTTP semantics independently of wire version.
- Distinguish application status from DNS, TCP, and TLS failures.
- Trace proxy and cache behavior with explicit trust boundaries.
Methods and status describe application meaning
HTTP defines request methods, target resources, fields, and response status independently of one particular wire encoding. GET retrieval and POST processing have different semantics, and idempotency concerns the intended effect of repeating a request rather than whether every reply is byte-identical. A 4xx or 5xx response establishes that some HTTP-speaking component answered, but the component might be a proxy rather than the origin. Keep method, authority, path, status, and request identity together. Do not call every non-200 response a network outage or retry a non-idempotent operation blindly after a transport interruption.
HTTP versions share semantics but differ in transport behavior
HTTP/1.1 uses its message syntax and connection rules, while HTTP/2 multiplexes streams using a binary framing layer over its connection context. HTTP/3 maps HTTP semantics over QUIC, which uses UDP and provides its own transport mechanisms. Therefore TCP-only assumptions do not explain every modern web exchange. Version negotiation and fallback depend on the application and deployment. Multiplexing can reduce some connection overhead while introducing shared-resource interactions; it does not guarantee lower latency for every workload. Preserve which version and connection were actually used before comparing request timing.
Parse the declared format rather than guessing boundaries
HTTP message framing rules prevent a receiver from confusing payload bytes with a later message. A small offline parser can demonstrate a fixed status line or URL, but it is not a production HTTP implementation. Real parsing must handle valid field syntax, body-length rules, version behavior, and malformed input consistently across intermediaries. Disagreement between parsers can be a security issue, so do not build a proxy by splitting arbitrary bytes on a convenient delimiter. Use supported protocol libraries for real applications. The examples here inspect fixed synthetic text only and never send HTTP requests.
from urllib.parse import urlsplit
url = urlsplit('https://report.example:8443/reports/7?format=json')
print('scheme=' + url.scheme)
print('host=' + str(url.hostname))
print('port=' + str(url.port))
print('path=' + url.path)
scheme=https
host=report.example
port=8443
path=/reports/7
urlsplit parses components only. It performs no DNS, TCP, TLS, or HTTP operation, and parsing does not authenticate the hostname.
A proxy creates another observation and trust boundary
A forward or reverse proxy can terminate connections, rewrite routing context, authenticate clients, and generate its own responses. The origin may therefore see the proxy’s address rather than the original client’s. Forwarded metadata is trustworthy only when the application accepts it from a controlled trusted boundary and handles untrusted client-supplied copies correctly. A load balancer’s successful health check is not proof that every user path or backend dependency works. Record frontend and backend protocols, termination points, and request correlation. Avoid publishing private endpoint inventories or credentials while sharing diagnosis.
A cached response has freshness and validation semantics
HTTP caches use directives, validators, age, and other semantics to decide reuse. A fresh cached response may avoid reaching the origin at all; a conditional request can validate an existing representation rather than transfer the full body. Cache-Control no-cache and no-store are not interchangeable concepts. Private and shared caches also have different boundaries, especially for authenticated or personalized responses. Do not infer backend availability solely from one cached success. The following fixture calculates declared freshness only; it deliberately omits the many rules needed for a real cache.
max_age = 120
current_age = 90
print('synthetic_fresh=' + str(current_age < max_age))
print('remaining=' + str(max(0, max_age - current_age)))
synthetic_fresh=True
remaining=30
The response has 30 seconds of modeled freshness under these inputs. The calculation does not implement authorization, validators, Vary, or full HTTP cache-age rules.
Common errors and fixes
An HTTP 403 is called a TCP failure
- Why it happens
- An application response was confused with missing transport.
- How to fix it
- Identify the responding component and authorization policy.
- Prevent it next time
- Keep protocol milestones separate.
All web traffic is assumed to use TCP
- Why it happens
- HTTP/3 and QUIC were omitted.
- How to fix it
- Record the actual protocol version and transport.
- Prevent it next time
- Include version negotiation in diagnostics.
A cached success proves origin health
- Why it happens
- The request may never have reached the origin.
- How to fix it
- Distinguish cache reuse, validation, and origin response.
- Prevent it next time
- Record cache context with availability evidence.
Check your understanding
1. A reverse proxy returns 502. What should be established before blaming the client network?
Show worked solution
The client reached an HTTP-speaking component. Identify that proxy’s backend connection and error context, then distinguish backend resolution, transport, TLS, and application failures. The frontend status alone does not name the backend cause.
2. Why is no-cache not simply a promise to store nothing?
Show worked solution
It concerns reuse requiring the applicable validation rather than the same storage prohibition expressed by no-store. Review the full cache rules and response context before designing privacy or freshness behavior from a directive’s everyday wording.
Key takeaways
- Follow HTTP semantics through actual transport and intermediaries.
- Treat cache reuse and origin execution as different events.