---
name: fetch_http_fingerprint
kind: function
lang: py
domain: cybersecurity
version: "1.0.0"
purity: impure
signature: "def fetch_http_fingerprint(url: str, timeout_s: float = 15.0, verify_tls: bool = True, max_html_bytes: int = 500_000, user_agent: str | None = None) -> dict"
description: "Hace un GET HTTP(S) a una URL con User-Agent de navegador, sigue redirects y recoge TODAS las senales crudas para fingerprint de la tecnologia web (estilo Wappalyzer): cabeceras HTTP de respuesta normalizadas (lowercase), nombres de cookies, el HTML, el titulo y la cadena del servidor. Es la capa IMPURA (toca la red) del fingerprinting web / deteccion de stack tecnologico; la capa de matching de firmas es la funcion pura aparte detect_web_tech_py_cybersecurity que consume exactamente lo que esta devuelve. Descomprime gzip/deflate y decodifica el HTML best-effort. Nunca lanza: devuelve dict {status: ok|error}; un 403/500 sigue siendo senal util y se devuelve con su status_code real. SEGURIDAD: en cookies solo guarda los NOMBRES, nunca los valores. Solo stdlib (urllib, ssl, re, gzip, zlib)."
tags: [recon, cybersecurity, web-recon]
params:
  - name: url
    desc: "URL objetivo. Si no trae esquema se asume https:// y, si la conexion HTTPS falla, reintenta con http://. Vacia devuelve status error."
  - name: timeout_s
    desc: "Timeout de la peticion en segundos (default 15.0)."
  - name: verify_tls
    desc: "Si False crea un ssl context sin verificacion de certificado (inseguro, vulnerable a MITM; solo para recon de hosts propios con cert self-signed). Default True."
  - name: max_html_bytes
    desc: "Corta el HTML leido a este tamano en bytes para no descargar megas (default 500_000 = 500 KB). Las SPAs grandes pueden quedar truncadas."
  - name: user_agent
    desc: "User-Agent a enviar. Default un UA realista de Chrome desktop."
output: "dict. En exito: {status: 'ok', url, final_url (tras redirects), status_code (int), headers (dict claves lowercase, valores str, ultimo si repetido), cookies (lista de SOLO nombres de cookie de Set-Cookie, nunca valores), title (str|None), server (str|None, atajo a headers['server']), html (str cortado a max_html_bytes), html_len (int), raw (bloque legible status+headers SIN el html, para evidencia OSINT)}. Un error HTTP (403/404/500...) devuelve status ok con su status_code real. En error de red total (host no resuelve / conexion rechazada / timeout): {status: 'error', error: str, url}."
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_py_core"
imports: []
tested: true
tests: ["test_status_ok_y_status_code_200", "test_headers_normalizados_lowercase", "test_cookies_solo_nombres_no_valores", "test_title_extraido", "test_url_vacia_devuelve_error", "test_host_inexistente_devuelve_error_sin_lanzar"]
test_file_path: "python/functions/cybersecurity/fetch_http_fingerprint_test.py"
file_path: "python/functions/cybersecurity/fetch_http_fingerprint.py"
---

## Ejemplo

```python
import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from cybersecurity import fetch_http_fingerprint

fp = fetch_http_fingerprint("https://example.com")
if fp["status"] == "ok":
    print(fp["status_code"])          # 200
    print(fp["final_url"])            # https://www.example.com/ (tras redirects)
    print(fp["server"])               # 'nginx' (o None)
    print(fp["headers"].get("x-powered-by"))  # 'PHP/8.1' (o None)
    print(fp["title"])                # titulo de la pagina
    print(fp["cookies"])              # ['PHPSESSID', 'cf_clearance']  (SOLO nombres)
    # fp["html"] / fp["headers"] alimentan detect_web_tech para el matching de firmas.
    # fp["raw"] tiene status + headers (sin html) para guardar como evidencia OSINT.
else:
    print("fallo:", fp["error"])
```

## Cuando usarla

Usala como **primer paso del fingerprint de la tecnologia web** de un sitio:
recoge headers + html + cookies crudos para que
`detect_web_tech_py_cybersecurity` los matchee contra firmas (estilo
Wappalyzer) e identifique CMS, frameworks, servidores, CDNs, lenguajes, etc.
Tambien es util **sola** para inspeccionar las cabeceras HTTP, el titulo y el
servidor de una URL durante recon, o para conservar la respuesta (`raw`) como
evidencia. Sigue redirects, asi que tambien revela el destino final de una URL.

## Gotchas

- IMPURA: hace red. Nunca lanza — fallos de red total devuelven
  `{"status": "error", ...}`. Un error HTTP (403/500...) se devuelve como
  `status: ok` con su `status_code` real porque sigue siendo senal de
  fingerprint.
- **`cookies` guarda SOLO los nombres**, nunca los valores. Un Set-Cookie lleva
  tokens de sesion; capturar el valor seria filtrar un secreto. El bloque `raw`
  tampoco incluye valores de cookie.
- **`verify_tls=False` es inseguro** (vulnerable a MITM): desactiva la
  verificacion del certificado TLS. Usalo solo en recon de hosts propios con
  cert self-signed, nunca contra objetivos en internet.
- **Sigue redirects** (urllib por defecto): el `final_url` puede saltar a otro
  dominio/host distinto del solicitado. Comprueba `final_url` si el scope
  importa.
- **`max_html_bytes` corta el HTML** (default 500 KB): SPAs grandes o paginas
  con mucho inline pueden quedar truncadas, y el matching de firmas que dependa
  del final del documento puede fallar. Sube el limite si lo necesitas.
- Un **WAF / anti-bot** (Cloudflare, etc.) puede devolver una pagina challenge
  en vez del sitio real; en ese caso el fingerprint reflejara el WAF, no el
  stack subyacente.
- **Legal**: respeta robots, el scope autorizado y la autorizacion legal del
  objetivo antes de escanear. Es trafico activo contra el host (un GET real).
- Fallback de esquema: una `url` sin `://` se intenta primero como `https://` y,
  si falla la conexion, como `http://`.
