// Engineering Log
HTTP, HTTPS and TLS: Part 12 — HTTP-level attacks: smuggling, Host header spoofing, CSRF, XSS, SSRF and cache poisoning
Published on 2026-10-21
// Fast route
This article belongs to the topic Servers and infrastructure.
HTTPS protects data in transit, but it knows nothing about what’s inside the request. Most attacks against websites happen over a fully encrypted connection and exploit HTTP itself: how proxies and applications parse the same request differently, how the browser automatically attaches cookies, how the application trusts headers. This part describes the main classes of such attacks — how they work and what to configure to protect against them. Test the described checks only on your own systems or with written permission from the owner.
Request smuggling: proxy and application desynchronization
There is almost always a proxy in front of the application — Nginx, HAProxy, CDN. If the proxy and the application determine differently where one request ends and the next begins, an attacker can “smuggle” the start of another request inside their request.
In HTTP/1.1 the body length is specified by two headers: Content-Length and Transfer-Encoding: chunked. The standard requires that if both are present, Transfer-Encoding is used, but implementations make mistakes:
POST / HTTP/1.1
Host: example.ru
Content-Length: 13
Transfer-Encoding: chunked
0
SMUGGLEDA proxy that looks at Content-Length will pass the entire body to the application. An application that looks at Transfer-Encoding sees the end of the request at 0, and SMUGGLED is considered the start of the next request on the same connection. That “next” request can be concatenated with another user’s request that arrived over the same connection between proxy and application. The result — someone else’s response, cookie theft, bypassing access rules on the proxy.
Variations: Transfer-Encoding: xchunked or a space before the colon that one side ignores and the other does not; discrepancies when translating HTTP/2 to HTTP/1.1 on the proxy (H2.CL, H2.TE); Upgrade: h2c which the proxy passes to the application.
Protection:
- keep proxies and application servers up to date — these bugs are fixed in each release;
- the proxy should reject ambiguous requests with both headers or at least remove
Content-Lengthand not forward it; check how your entire stack behaves, not just the proxy; - use HTTP/2 between proxy and application where possible — in HTTP/2 length is set by frames, so ambiguities are gone;
- do not forward
Upgrade: h2cto the application.
Host header spoofing
Host is set by the client, and an application that builds links from it trusts user input. A classic example is password reset: the application sends an email with a link https://{Host}/reset?token=…. An attacker requests a victim’s password reset with Host: attacker.example, the victim gets a legitimate email with a link to the attacker’s site, goes there — and the reset token goes to the attacker.
Protection:
- in Nginx configure a
default_serverthat responds to unknown names with status 444 (close connection), and pass the application only requests with knownserver_name; - in the application set allowed hostnames (in Django —
ALLOWED_HOSTS) and build absolute links from settings, not from the header; - the same applies to
X-Forwarded-Host: do not accept it from clients.
server {
listen 80 default_server;
listen 443 ssl default_server;
ssl_reject_handshake on;
return 444;
}Cache poisoning and cache deception
Cache poisoning. A CDN or proxy caches a response by key — usually scheme, host, and path. If the response depends on a header not included in the key (for example, X-Forwarded-Host ends up in script URLs on the page), an attacker sends one request with a forged header, the cache stores the poisoned page and serves it to all visitors.
Cache deception. The reverse situation: the cache decides that /account/profile.css is a static file based on extension. The application ignores the “extra” suffix and returns the user’s personal page. An attacker sends a victim a link, the victim’s personal page gets into the shared cache, and the attacker reads it from there.
Protection:
- on personal pages use
Cache-Control: privateorno-store; the CDN should respect application headers and not cache by extension alone; - if the response depends on a header, add it to
Varyor to the cache key; - the application should return 404 for non-existent paths like
/account/profile.css, not serve the/account/profilepage.
CSRF: requests on behalf of the user
The browser automatically attaches site cookies to requests regardless of where they originate. If a user is logged into bank.example.ru and then opens an attacker page with a form that auto-submits to https://bank.example.ru/transfer, the request will be sent with the user’s session cookie. This is cross-site request forgery (CSRF).
Protection:
- SameSite on cookies. With
SameSite=Lax(the default in modern browsers) cookies are not sent with POST requests from other sites. Do not setSameSite=Noneunless necessary; - CSRF token — a random value in the form that the server verifies against the session. Present in all popular frameworks;
- check
Originon state-changing requests: it must match your site; - do not change data via GET:
SameSite=Laxallows link navigation, and a GET request that changes state will go through.
CORS does not protect from CSRF: it forbids the page from reading the response, but the form request still goes and is executed.
XSS and Content-Security-Policy
Cross-site scripting (XSS) — injecting someone else’s JavaScript into your site’s page: via a comment, username, search parameter printed without escaping. The script runs with site privileges: it reads the page, sends requests with the user’s cookie, tampers with forms.
Defense is layered:
- escape all user input on output; templating engines do this by default; dangerous spots are where escaping is disabled (
|safe,v-html,dangerouslySetInnerHTML,innerHTML); - session cookies with
HttpOnly— scripts cannot read them; - Content-Security-Policy — even if a script is injected, the browser will not execute it if the source is not allowed:
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-r4nd0m'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'Inline scripts are allowed only with a one-time nonce value that the server generates per response and places in <script nonce="r4nd0m">. 'unsafe-inline' in script-src effectively cancels XSS protection. Deploy CSP via Content-Security-Policy-Report-Only, collect violation reports, and enable blocking once reports stop arriving.
SSRF: requests on behalf of the server
If your application fetches something by a user-provided address — an avatar for preview, a webhook, import by URL — an attacker can specify an internal address: http://127.0.0.1:8080/admin, http://10.0.0.5:9200/, the cloud metadata address http://169.254.169.254/. The request will originate from inside your network where there is often no firewall and often no authorization. Cloud metadata can reveal temporary credentials for the cloud account.
Protection:
- validate the address after name resolution: forbid private, local, and link-local ranges (
127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,::1,fc00::/7); - validate every redirect: an external address can redirect to an internal one;
- defend against DNS rebinding: connect to the IP you validated;
- in clouds enable the metadata protection mode (in AWS — IMDSv2, which requires a token in a header);
- best is to delegate such requests to a separate service or proxy without access to the internal network.
CRLF injection and response splitting
HTTP/1.1 headers are separated by \r\n. If an application inserts user input into a header unchecked — for example, in Location for a redirect — an attacker can supply %0d%0a and append their own headers or even body: set a cookie, inject a script, poison the cache. Modern frameworks forbid newlines in headers, but custom code and old libraries are vulnerable.
Nginx has its own pitfall: $uri is decoded, and in return 302 https://$host$uri; %0d%0a can inject a header. Use $request_uri — it’s passed in original, encoded form.
Open redirects
An address like https://example.ru/login?next=https://attacker.example that redirects the user after login to a foreign site. This is not necessarily a break-in, but the link starts with your domain and inspires trust in phishing emails, and in OAuth such a redirect can steal a token.
Protection: allow only relative paths on your site in next or addresses from a whitelist. Checking “starts with /” is insufficient: //attacker.example is another site.
Clickjacking
An attacker embeds your site in a transparent frame on top of their page, and the user, thinking they are clicking a button on the attacker’s page, clicks “Delete account” or “Confirm payment” on yours. Protection — forbid embedding:
Content-Security-Policy: frame-ancestors 'none'
X-Frame-Options: DENYTen-minute self-check
The commands below check the most common issues. Run them only against your own sites; example.ru — your domain, 192.0.2.10 — the IP address of your server.
Unknown Host. A request with a foreign name to your server IP should not reach the application:
curl -s -o /dev/null -w '%{http_code}\n' -H 'Host: evil.example' http://192.0.2.10/
# good: 000 (Nginx closed the connection with return 444) or 4xx; bad: 200 with the site page
curl -sk -o /dev/null -w '%{http_code}\n' --resolve evil.example:443:192.0.2.10 https://evil.example/
# good: 000 — handshake rejected (ssl_reject_handshake)X-Forwarded-Host reflection. A forged name should not appear in links on the page:
curl -s -H 'X-Forwarded-Host: evil.example' https://example.ru/ | grep -c evil.example # should be 0Cookie flags. Session cookies should have Secure, HttpOnly and SameSite:
curl -sI https://example.ru/login | grep -i '^set-cookie'Security headers:
for h in strict-transport-security content-security-policy x-content-type-options \
referrer-policy x-frame-options permissions-policy; do
printf '%-28s ' $h; curl -sI https://example.ru/ | grep -qi "^$h:" && echo present || echo MISSING
doneOpen redirect. Substitute parameters your site uses for return-after-login (next, return, redirect, url):
curl -s -o /dev/null -w '%{http_code} -> %{redirect_url}\n' 'https://example.ru/login?next=//evil.example'
# bad: redirect to evil.exampleCRLF injection:
curl -sI 'https://example.ru/%0d%0aX-Injected:%20yes' | grep -ci '^x-injected' # should be 0TRACE method should be disabled:
curl -si -X TRACE https://example.ru/ | head -1 # 405 or 403Personal pages and cache. Open a page that is available only after login twice with the session cookie and once without it:
curl -sI -b 'session=YOUR_SESSION' https://example.ru/account | grep -iE '^(cache-control|x-cache|cf-cache-status|age)'The response should include Cache-Control: private or no-store, and the CDN should miss or bypass the cache (MISS, BYPASS, DYNAMIC), without an Age header.
SSRF. If the site has features to fetch by URL (avatar by address, import, webhooks), try URLs http://127.0.0.1:22/ and http://169.254.169.254/latest/meta-data/. The application should refuse, not return content or a connection error with details.
Request smuggling is hard to check manually. There is the HTTP Request Smuggler extension for Burp Suite and the smuggler.py utility; run them on a test environment with the same proxy chain as production.
What to check
- Proxies and application servers are updated; requests with simultaneous
Content-LengthandTransfer-Encodingdo not reach the application ambiguously. - There is a
default_serverthat closes requests with unknownHost. - Personal pages are not cached by the CDN.
- Session cookies:
Secure; HttpOnly; SameSite=Lax. - There is a CSP without
'unsafe-inline'for scripts and withframe-ancestors. - Server-side requests to user-provided addresses do not reach the internal network.
- Redirect parameters are validated against a whitelist.
For automated testing use OWASP ZAP and Burp Suite: they include scanners for all the attack classes listed. Run them on a test environment — active scanning creates entries, submits forms and can damage data.
// Similar task
If you are dealing with something similar
This article belongs to one of the main working topics. You can keep reading on the topic, go to the homepage to understand what I do, or open the service pages directly.
Article topic
Servers and infrastructure
VPS, Linux, web stack, migrations, hosting, databases, and core operations.
Typical tasks behind this topic
- Move a site or service to a new server
- Set up Linux, Nginx, databases, and backups
- Figure out why the system behaves unstably
// Next step
If you need help with this topic, not just another article, it is better to go straight to the service page. The homepage and topic collection stay available as secondary routes.
Open services// Reviews
Related reviews
Thanks to Mikhail for his responsiveness. We spoke by phone; he explained how I could do it myself. This is my second time reaching out — everything’s great and prompt.
Thanks to Mikhail for his responsiveness. We had a call; he explained how I could do it myself. This is my second time contacting him; everything is great and prompt.
Consultation on Nginx Proxy Manager and Portainer
2025-02-25 · ★ 5/5
I want to express my deep gratitude to the specialist who set up SEO-friendly URLs for me on OpenCart. Configuring them turned out to be easy and simple, and I'm glad I finally found a professional who did everything well and without unnecessary complications. Before this I went through four specialists, and each time there were issues with the setup, but this person handled the task perfectly.
I want to express my great gratitude to the specialist who configured SEO-friendly URLs for me on OpenCart. Setting up the URLs turned out to be easy and simple, and I'm glad that I finally found a professional who did …
Excellent work — this isn't the first time I've used them; they find solutions to complex problems. I recommend.
Excellent work, this isn't my first time contacting them; they find solutions to complex problems. I recommend.
Excellent work! He completed the assigned task on time and without errors. It was a pleasure to work with him; I recommend him.
Great job! Completed the assigned task on time and without errors. It was a pleasure to work together, I recommend.
Excellent specialist — he delved into the problem, figured it out, and fixed it. I recommend him.
Excellent specialist, delved into the problem, figured it out, and fixed it. I recommend.
I needed to resolve an SSL certificate issue on a server where the certificate had been issued through Nginx Proxy Manager. Mikhail clarified all the details of my setup and requested access to assess the feasibility of solving the issue, since he hadn't worked with that service before. He quickly figured it out and fixed my problem. Perfect cooperation)
I needed to solve an issue with an SSL certificate on the server that was issued through Ngnix Proxy manager. Mikhail clarified all the details of how everything is set up for me, asked for access to assess the …
Diagnosing Nginx Proxy Manager in a Docker container and resolving the issue
2024-03-15 · ★ 5/5
// Contact
Need help?
Get in touch with me and I'll help solve the problem
I reply within one business day (03:00-13:00 GMT)
Или оставьте заявку здесь:
// Related