Proxy inverso transparente (Express + http/https nativo). Reenvia peticiones HTTP a cualquier destino y devuelve la respuesta sin transformarla: status, headers, cookies y body.
Este deployment expone el proxy bajo el prefijo /proxy.
https://ESTE-HOST/proxy/<ruta-del-destino>?<query>
El destino se elige por peticion con el header x-target-url.
Si el servidor tiene TARGET_URL configurada y no envias el header, se usa esa.
Si no hay ninguna de las dos: 400 TARGET_REQUIRED.
# El upstream recibe: https://www.deezer.com/users/1?x=1 curl "https://ESTE-HOST/proxy/users/1?x=1" \ -H "x-target-url: https://www.deezer.com"
La ruta y la query que siguen al prefijo se concatenan tal cual al destino.
El header x-target-url nunca llega al upstream.
GETPOSTPUT PATCHDELETEOPTIONS HEAD
OPTIONS con Access-Control-Request-Method (preflight CORS) se responde
localmente con 204. Un OPTIONS normal se reenvia al upstream.
El body se captura como Buffer crudo y se reenvia byte a byte, sin parsear ni re-serializar:
| Tipo | Ejemplo |
|---|---|
| JSON | Content-Type: application/json |
| Formulario | application/x-www-form-urlencoded |
| Archivos | multipart/form-data (no fijes el Content-Type a mano: deja que FormData genere el boundary) |
| Texto / Binario | cualquier text/* u application/octet-stream |
Limite de tamano del body: configurable en el servidor (por defecto 50mb).
Al superarlo responde 413.
Se reenvian todos (Authorization, Cookie, Origin, Referer, Accept, User-Agent, etc.)
excepto los hop-by-hop (host, connection, content-length,
transfer-encoding, keep-alive, upgrade, te,
trailer, expect, proxy-*) y el propio
x-target-url. Se anaden X-Forwarded-For/Proto/Host.
Status, headers y multiples Set-Cookie se devuelven tal cual.
El body se transmite en streaming (vale para JSON y para descargas grandes).
El header Cookie del cliente se reenvia integro, y las
Set-Cookie del upstream vuelven sin fusionarse (una por cookie).
| Status | code | Cuando |
|---|---|---|
| 400 | TARGET_REQUIRED | Sin header x-target-url y sin TARGET_URL configurada |
| 400 | TARGET_INVALID | El destino no es una URL http/https valida |
| 403 | TARGET_BLOCKED | Destino a red privada con BLOCK_PRIVATE_TARGETS=true |
| 413 | entity.too.large | Body mayor que BODY_LIMIT |
| 502 | UPSTREAM_UNREACHABLE | No se pudo conectar con el destino |
| 502 | UPSTREAM_TIMEOUT | El destino no respondio dentro del timeout |
Formato de error: {"error":"...","code":"...","message":"..."}
curl -X POST "https://ESTE-HOST/proxy/ajax/gw-light.php?method=deezer_userAutolog&input=3&api_version=1.0&api_token=&cid=123" \
-H "x-target-url: https://www.deezer.com" \
-H "Content-Type: application/json" \
-H "Origin: https://www.deezer.com" \
-H "Referer: https://www.deezer.com/" \
-d '{"APP_NAME":"deezer-web","AUTHORIZE_UNLOGGED":true,"CHECKFORM":true,"DEVICE_TOKEN":"abc"}'
curl -X POST "https://ESTE-HOST/proxy/api" \
-H "x-target-url: https://pipe.deezer.com" \
-H "Content-Type: application/json" \
-H "Origin: https://www.deezer.com" \
-H "Referer: https://www.deezer.com/" \
-d '{"query":"{ __typename }"}'
// fetch
await fetch("https://ESTE-HOST/proxy/users/1", {
headers: { "x-target-url": "https://www.deezer.com" },
});
// axios
const api = axios.create({
baseURL: "https://ESTE-HOST/proxy",
headers: { "x-target-url": "https://www.deezer.com" },
});
const form = new FormData();
form.append("file", file);
form.append("name", "hola");
await fetch("https://ESTE-HOST/proxy/upload", {
method: "POST",
headers: { "x-target-url": "https://tu-api.com" },
body: form, // no fijes Content-Type manualmente
});
const res = await fetch("https://ESTE-HOST/proxy/files/123", {
headers: { "x-target-url": "https://tu-api.com" },
});
const blob = await res.blob();
Prueba peticiones desde el navegador contra este mismo proxy.
- No soporta WebSockets (los headers Upgrade/Connection se eliminan).
- No sigue redirects: los devuelve tal cual al cliente.
- El timeout al upstream es configurable en el servidor (por defecto 30s).
- Logs del servidor: solo metodo, ruta sin query y status. Nunca headers ni tokens.
- Si el proxy esta abierto a Internet, protegelo con PROXY_API_KEY (ver bloque comentado en index.js) y activa BLOCK_PRIVATE_TARGETS=true.