Skip to content

Guides · Agents and automation

Use a proxy in n8n HTTP Request nodes

An n8n workflow that scrapes, checks prices or reads a localised page leaves from wherever n8n is hosted, which is usually the wrong country and always the same address. There are two places to fix that: on the node, and on the process n8n runs in. The second one is the one that survives.

Published Updated

Short answers

Where do I set a proxy for the HTTP Request node?

Try the node first: open Options on the HTTP Request node and look for a proxy field, which takes a full proxy URL including credentials. If your version does not have one, or the node ignores it, set the proxy for the whole n8n process with environment variables instead.

Which environment variables does n8n need?

HTTP_PROXY, HTTPS_PROXY and NO_PROXY, set on the n8n process itself. An https:// target follows HTTPS_PROXY, and its value still points at our HTTP port 7000: the scheme in the variable describes the proxy, the name of the variable describes the target.

Why does nothing change after I export the variables?

Because you exported them in your shell and n8n is running somewhere else: in a container, under systemd, or as a service started at boot. The process that opens the socket has to have the variable, so set it where that process is defined and restart it.

Can I do this on n8n Cloud?

The process-level route needs control of the environment, which self-hosting gives you and a managed instance generally does not. On a managed instance you are limited to whatever proxy field the node itself exposes.

How do I choose the country per workflow?

In the proxy username, not in n8n. USERNAME-peer-es-rot-ondemand is Spain on the residential pool with a new exit per connection; change the two-letter code and you have another country, on the same credential and the same balance.

My execution log shows 407. What now?

Read the gateway code: E_AUTH_INVALID points to credentials; E_CAP_EXCEEDED points to exhausted or expired traffic. Malformed username syntax is documented as 400 E_USERNAME_PARSE. Use the 407 checklist before replacing a password.

Two places to set it

A proxy field on the node is per request and easy to see, which is a real advantage in a workflow other people will edit. Its weakness is that it is one field: everything, including the password, has to fit into a single URL.

The environment variables are coarser and more reliable. They apply to the whole process, so every node that makes an outbound request goes through the gateway, and nothing depends on which node version a workflow happens to use.

Pick one and write it down. Two mechanisms configured at once is how a workflow ends up with one node proxied and the next one not, which looks like an intermittent fault rather than a configuration.

The URL n8n needs

One credential serves every country and rotation mode, because the targeting is in the username. Pool and country are positional; rot and sid come after them.

Percent-encode the password if it contains a reserved character. A colon, an at sign or a slash inside a URL ends the field early and the gateway receives a truncated string.

The proxy URL, and what each part doestext
http://USERNAME-peer-es-rot-ondemand:pak_xxx@gw.portproof.org:7000
socks5h://USERNAME-peer-es-rot-ondemand:pak_xxx@gw.portproof.org:7001

# USERNAME  the account part of your username, from the dashboard
# peer      the pool: peer residential, mbl mobile 4G/5G
# de        the country, two letters, lower case
# rot-...   ondemand, auto5 to auto60, or sticky with a sid
# pak_xxx   the password, percent-encoded if it contains : @ / ? or #
# gw.portproof.org  the gateway host: 7000 for HTTP, 7001 for SOCKS5

For a workflow that has to keep one exit across several nodes, add a session: sid-<name> with rot-sticky, one name per run. What that does and does not hold is in why a sticky session changed its IP.

Docker and compose, where most people are stuck

Self-hosted n8n usually runs in a container, and a container inherits nothing from your shell. Put the variables in the run command or the compose file, then recreate the container: a restart alone does not pick up new environment values.

Keep localhost in NO_PROXY so n8n’s own webhook calls and health checks do not leave through the gateway and cost GB.

Give the n8n process the variablessh · yaml
# docker run
docker run -d --name n8n -p 5678:5678 \
  -e HTTP_PROXY="http://USERNAME-peer-es-rot-ondemand:$PROXY_PASSWORD@gw.portproof.org:7000" \
  -e HTTPS_PROXY="http://USERNAME-peer-es-rot-ondemand:$PROXY_PASSWORD@gw.portproof.org:7000" \
  -e NO_PROXY="localhost,127.0.0.1,n8n" \
  -v n8n_data:/home/node/.n8n docker.n8n.io/n8nio/n8n

# docker-compose.yml
services:
  n8n:
    image: docker.n8n.io/n8nio/n8n
    ports: ["5678:5678"]
    environment:
      HTTP_PROXY: "http://USERNAME-peer-es-rot-ondemand:${PROXY_PASSWORD}@gw.portproof.org:7000"
      HTTPS_PROXY: "http://USERNAME-peer-es-rot-ondemand:${PROXY_PASSWORD}@gw.portproof.org:7000"
      NO_PROXY: "localhost,127.0.0.1,n8n"
    volumes: ["n8n_data:/home/node/.n8n"]
volumes: { n8n_data: {} }

# then: docker compose up -d --force-recreate

Under systemd rather than Docker, the same values go in a drop-in file with Environment=, followed by systemctl daemon-reload and a restart. The wider version of this problem, across curl, Python, Node and Go, is in proxy works in curl but not your app.

A password in a compose file is a password in your repository. Read it from an environment file that is not committed, or from a secret store, and regenerate it if it has ever been shared.

One workflow that confirms the exit first

Before the real job runs, prove where requests leave from. One HTTP Request node pointed at the echo endpoint is enough, and it costs a few hundred bytes.

  1. Add an HTTP Request node, method GET, URL set to the echo endpoint printed on the setup docs.
  2. Execute the node on its own and read the output: it reports the IP address the request arrived from, not a country, so look that address up if the job depends on the market.
  3. Run it a second time. With rot-ondemand the exit should differ; with rot-sticky and a session name it should not.
  4. Only then wire the node into the workflow that needs the country.

If curl works here and the node does not, the credential is fine and the problem is in how n8n was configured.

The same check outside n8n, for comparisonsh
curl -s -x "http://gw.portproof.org:7000" \
  --proxy-user "USERNAME-peer-es-rot-ondemand:$PROXY_PASSWORD" https://api.portproof.org/v1/echo-ip

# and from our side: the exit IP and the latency (no country), up to six times a minute
curl -X POST https://api.portproof.org/v1/traffic/test \
  -H "Authorization: Bearer $PORTPROOF_API_KEY" -H "Content-Type: application/json" \
  -d '{"pool":"residential","country":"ES","rotation":"ondemand"}'

Reading the execution log

What a failing execution is telling you
In the logWhere it came fromWhat to do
407 with E_AUTH_INVALIDThe gateway refused the credentialsCheck encoding, the username tokens and the password
407 with E_CAP_EXCEEDEDThe GB balance is used upTop up; the credentials do not change
400 with E_USERNAME_PARSEThe username is malformedLower case only, no hyphen inside a value
502 with E_NO_STOCK_COUNTRYNo device online in that country and poolRetry with backoff, or use the other pool
The request succeeded from your own addressn8n never used the proxyThe process did not get the variables; recreate the container

The whole error table is on the setup docs, and tunnel-level failures have their own page in fix CONNECT tunnel failed errors.

What a workflow costs

Every byte through the gateway is metered in both directions, so a workflow that fetches JSON costs very little and one that renders pages costs a great deal more. A scheduled workflow is worth sizing before you turn it on: runs per day, requests per run, bytes per request.

Both pools bill from one balance: mobile 4G/5G and residential, on the ladder shown on pricing.

Price per GB by order size, EUR
Order sizePer GB
1 to 4 GBEUR 4.50
5 to 24 GBEUR 3.90−13 %
25 to 49 GBEUR 3.50−22 %
50 to 99 GBEUR 3.20−29 %
100 to 249 GBEUR 2.90−36 %
250 GB and moreEUR 2.50−44 %

GB never expire. What you buy stays on your balance until you use it; a new purchase adds to the same balance. The trial is 0.5 GB for EUR 2.90, once per customer.

No fee per connection, per session or per workflow: the meter counts bytes and nothing else.

What is not allowed

What is not allowed: automation does not change the rules. Account creation and farming, credential stuffing, sneaker and ticket bots, and anything else on the declined list are refused whether a person or a workflow issues the request. See the acceptable-use policy.

Use a proxy in n8n HTTP Request nodes · Portproof