|
| 1 | +# Generic HTTP Forward Proxy (`/proxy/forward`) |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +`/proxy/forward` is a transparent HTTP relay endpoint. It forwards any HTTP request — any method, any body, any headers — to a destination URL using **MediaFlow's outbound IP**, then returns the upstream response verbatim. |
| 6 | + |
| 7 | +**Primary use cases:** |
| 8 | + |
| 9 | +- **Debrid IP binding** — Route debrid API calls (RealDebrid, AllDebrid, TorBox, etc.) through MediaFlow so the debrid service records MediaFlow's IP as the TCP source, not the addon server's IP. Combine with the `{mediaflow_ip}` placeholder to keep the `ip=` parameter consistent. |
| 10 | +- **Extractor POST requests** — Send JSON or form POST to a video host's extraction endpoint from MediaFlow's IP (resolves [issue #271](https://github.com/mhdzumair/mediaflow-proxy/issues/271)). |
| 11 | +- **Any IP-bound API call** — Any HTTP API that must appear to come from the same IP that later fetches the stream. |
| 12 | + |
| 13 | +--- |
| 14 | + |
| 15 | +## Endpoint |
| 16 | + |
| 17 | +``` |
| 18 | +ANY /proxy/forward |
| 19 | + ?d=<destination_url> |
| 20 | + &api_password=<password> # required (or encrypted token) |
| 21 | + [&h_<header-name>=<value>] # set outbound request header (repeatable) |
| 22 | + [&r_<header-name>=<value>] # override response header (repeatable) |
| 23 | +``` |
| 24 | + |
| 25 | +### Methods |
| 26 | + |
| 27 | +`GET`, `HEAD`, `POST`, `PUT`, `PATCH`, `DELETE`, `OPTIONS` |
| 28 | + |
| 29 | +### Parameters |
| 30 | + |
| 31 | +| Parameter | Required | Description | |
| 32 | +|---|---|---| |
| 33 | +| `d` | Yes | Destination URL. Must be `http://` or `https://`. | |
| 34 | +| `api_password` | Yes* | API password (*if `API_PASSWORD` is configured). | |
| 35 | +| `h_<name>` | No | Set a request header sent to the destination. E.g. `h_Authorization=Bearer tok`. | |
| 36 | +| `r_<name>` | No | Override a response header returned to the caller. | |
| 37 | + |
| 38 | +### Request body |
| 39 | + |
| 40 | +The incoming request body is forwarded to the destination as-is. No base64, no envelope. |
| 41 | + |
| 42 | +### Response |
| 43 | + |
| 44 | +The upstream HTTP status, headers (minus hop-by-hop), and body are returned verbatim. |
| 45 | + |
| 46 | +--- |
| 47 | + |
| 48 | +## `{mediaflow_ip}` placeholder |
| 49 | + |
| 50 | +Embed the literal string `{mediaflow_ip}` anywhere in the destination URL or request body. MediaFlow substitutes it with its own public IP before forwarding. |
| 51 | + |
| 52 | +This keeps the `ip=` parameter that debrid services use for CDN URL binding consistent with the TCP source of the API call. |
| 53 | + |
| 54 | +**Example — RealDebrid `ip=` binding:** |
| 55 | + |
| 56 | +``` |
| 57 | +GET /proxy/forward |
| 58 | + ?d=https://api.real-debrid.com/rest/1.0/torrents/instantAvailability/{hash}%3Fip%3D{mediaflow_ip} |
| 59 | + &api_password=secret |
| 60 | + &h_Authorization=Bearer <rd_token> |
| 61 | +``` |
| 62 | + |
| 63 | +MediaFlow replaces `{mediaflow_ip}` with e.g. `1.2.3.4` before calling RealDebrid. |
| 64 | + |
| 65 | +--- |
| 66 | + |
| 67 | +## Public IP endpoint |
| 68 | + |
| 69 | +``` |
| 70 | +GET /proxy/ip?api_password=<password> |
| 71 | +``` |
| 72 | + |
| 73 | +Returns `{"ip": "<public-ip>"}` — MediaFlow's outbound IP. Useful when you need to retrieve MediaFlow's IP before making a separate request (e.g. appending `ip=` to a CDN URL). |
| 74 | + |
| 75 | +Configure a static value with the `PUBLIC_IP` environment variable to skip the auto-detection request. |
| 76 | + |
| 77 | +--- |
| 78 | + |
| 79 | +## Safety controls |
| 80 | + |
| 81 | +| Control | Default | Description | |
| 82 | +|---|---|---| |
| 83 | +| SSRF guard | Always on | Requests to loopback (`127.x`, `::1`) and RFC-1918 private addresses are blocked with `403`. | |
| 84 | +| Allowlist | Empty (allow all) | Set `FORWARD_ALLOWED_HOSTS` to restrict forwarding to specific hostnames. | |
| 85 | +| Denylist | Empty | Set `FORWARD_DENIED_HOSTS` to block additional hostnames. | |
| 86 | +| Request body limit | 50 MB | Set `FORWARD_MAX_REQUEST_BODY_BYTES`. Blocks at the limit with `413`. | |
| 87 | +| Response body limit | 10 MB | Set `FORWARD_MAX_RESPONSE_BODY_BYTES`. Returns `502` if exceeded. | |
| 88 | +| Auth required | Always | `api_password` or a valid encrypted token is mandatory. | |
| 89 | + |
| 90 | +### IP-disclosure header stripping |
| 91 | + |
| 92 | +The following headers are stripped from the outbound request before forwarding so the caller's IP is never leaked: |
| 93 | + |
| 94 | +`X-Forwarded-For`, `X-Real-IP`, `X-Client-IP`, `True-Client-IP`, `Forwarded`, `CF-Connecting-IP`, `X-Original-Forwarded-For`, `X-Cluster-Client-IP` |
| 95 | + |
| 96 | +--- |
| 97 | + |
| 98 | +## Examples |
| 99 | + |
| 100 | +### GET — API call through MediaFlow's IP |
| 101 | + |
| 102 | +```bash |
| 103 | +curl "http://localhost:8888/proxy/forward\ |
| 104 | +?d=https://api.real-debrid.com/rest/1.0/user\ |
| 105 | +&api_password=secret\ |
| 106 | +&h_Authorization=Bearer%20YOUR_RD_TOKEN" |
| 107 | +``` |
| 108 | + |
| 109 | +### POST JSON — extractor endpoint |
| 110 | + |
| 111 | +```bash |
| 112 | +curl -X POST \ |
| 113 | + "http://localhost:8888/proxy/forward\ |
| 114 | +?d=https://example.com/api/videos/abc123/playback\ |
| 115 | +&api_password=secret\ |
| 116 | +&h_Referer=https%3A%2F%2Fexample.com%2F\ |
| 117 | +&h_Content-Type=application%2Fjson" \ |
| 118 | + -d '{"fingerprint":{"userAgent":"Mozilla/5.0"}}' |
| 119 | +``` |
| 120 | + |
| 121 | +### POST form — debrid API |
| 122 | + |
| 123 | +```bash |
| 124 | +curl -X POST \ |
| 125 | + "http://localhost:8888/proxy/forward\ |
| 126 | +?d=https://api.real-debrid.com/rest/1.0/torrents/addMagnet\ |
| 127 | +&api_password=secret\ |
| 128 | +&h_Authorization=Bearer%20YOUR_RD_TOKEN\ |
| 129 | +&h_Content-Type=application%2Fx-www-form-urlencoded" \ |
| 130 | + --data-urlencode "magnet=magnet:?xt=urn:btih:..." |
| 131 | +``` |
| 132 | + |
| 133 | +### DELETE — remove a torrent |
| 134 | + |
| 135 | +```bash |
| 136 | +curl -X DELETE \ |
| 137 | + "http://localhost:8888/proxy/forward\ |
| 138 | +?d=https://api.real-debrid.com/rest/1.0/torrents/delete/TORRENT_ID\ |
| 139 | +&api_password=secret\ |
| 140 | +&h_Authorization=Bearer%20YOUR_RD_TOKEN" |
| 141 | +``` |
| 142 | + |
| 143 | +### Get MediaFlow's public IP |
| 144 | + |
| 145 | +```bash |
| 146 | +curl "http://localhost:8888/proxy/ip?api_password=secret" |
| 147 | +# {"ip":"1.2.3.4"} |
| 148 | +``` |
| 149 | + |
| 150 | +--- |
| 151 | + |
| 152 | +## Configuration |
| 153 | + |
| 154 | +New settings added to support `/proxy/forward`. All are optional. |
| 155 | + |
| 156 | +| Environment variable | Default | Description | |
| 157 | +|---|---|---| |
| 158 | +| `PUBLIC_IP` | *(auto-detected)* | Static public IP returned by `/proxy/ip` and substituted for `{mediaflow_ip}`. Skip auto-detection on startup. | |
| 159 | +| `FORWARD_ALLOWED_HOSTS` | `[]` | Comma-separated allowlist of hostnames. Empty = allow any host. | |
| 160 | +| `FORWARD_DENIED_HOSTS` | `[]` | Comma-separated denylist of hostnames (in addition to the built-in private-IP guard). | |
| 161 | +| `FORWARD_MAX_REQUEST_BODY_BYTES` | `52428800` (50 MB) | Maximum incoming request body size. Allows NZB/torrent file uploads. | |
| 162 | +| `FORWARD_MAX_RESPONSE_BODY_BYTES` | `10485760` (10 MB) | Maximum upstream response body size. | |
| 163 | + |
| 164 | +--- |
| 165 | + |
| 166 | +## Comparison with `/proxy/stream` |
| 167 | + |
| 168 | +| Feature | `/proxy/stream` | `/proxy/forward` | |
| 169 | +|---|---|---| |
| 170 | +| Methods | `GET`, `HEAD` | Any (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`, …) | |
| 171 | +| Request body | No | Yes (forwarded verbatim) | |
| 172 | +| Range / partial content | Yes | No | |
| 173 | +| HLS / DASH rewriting | Yes | No | |
| 174 | +| IP binding (`{mediaflow_ip}`) | No | Yes | |
| 175 | +| Response size limit | No | Yes (10 MB default) | |
| 176 | +| Intended use | Video streaming | API calls, extractor POSTs | |
0 commit comments