Skip to main content

Command Palette

Search for a command to run...

How to Use Proxies in Scrapy (Middleware Tutorial for Web Scraping)

Learn how to configure proxy middleware in Scrapy, rotate proxies, and manage request behavior for more reliable scraping.

Updated
•3 min read•View as Markdown
How to Use Proxies in Scrapy (Middleware Tutorial for Web Scraping)

Introduction

If you're using Scrapy for web scraping, adding proxies isn’t optional once you scale.

Without proxies:

  • Requests come from a single IP

  • Detection increases

  • Your crawler gets blocked

Scrapy provides a built-in way to handle this through middleware.

In this tutorial, you’ll learn how to:

  • Configure proxies in Scrapy

  • Use middleware for request handling

  • Rotate proxies

  • Improve scraping reliability

How Scrapy Handles Requests

Before adding proxies, understand how Scrapy works.

Basic flow:

  1. Spider generates requests

  2. Requests pass through middleware

  3. Downloader sends the request

  4. Response comes back

👉 Middleware is where proxies are applied.

Basic Proxy Setup in Scrapy

You can assign a proxy directly to a request.

Example:

def start_requests(self):
    yield scrapy.Request(
        url="https://httpbin.org/ip",
        meta={"proxy": "http://user:pass@ip:port"}
    )

👉 This routes the request through a proxy.

Using Downloader Middleware

For scalable setups, use middleware instead of hardcoding proxies.

Step 1: Create middleware


class ProxyMiddleware:
    def process_request(self, request, spider):
        request.meta['proxy'] = "http://user:pass@ip:port"

Step 2: Enable middleware


DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.ProxyMiddleware': 543,
}

👉 Now all requests go through your proxy.

Rotating Proxies in Scrapy

Using one proxy is not enough.

Example rotation:


import random

class RotatingProxyMiddleware:
    def __init__(self):
        self.proxies = [
            "http://user:pass@ip1:port",
            "http://user:pass@ip2:port",
            "http://user:pass@ip3:port"
        ]

    def process_request(self, request, spider):
        request.meta['proxy'] = random.choice(self.proxies)

👉 Each request uses a different proxy.

Handling Failed Proxies

Some proxies will fail. Handle it properly.


def process_exception(self, request, exception, spider):
    print("Proxy failed:", exception)

👉 You can remove bad proxies or retry requests.

Choosing a Proxy Provider

When working with Scrapy at scale, proxy quality affects request success rates.

Developers often test providers such as Bright Data, Oxylabs, NetNut, Decodo, and Squid Proxies to evaluate how proxies behave under load.

Different providers offer combinations of datacenter and residential proxies, which can impact request consistency depending on the target.

Integrating Proxies into Scrapy Workflows

In Scrapy-based systems, proxy integration should be handled centrally through middleware.

For example, Squid Proxies can be used within middleware configurations to assign proxies dynamically across requests.

This approach keeps proxy logic separate from spider logic, making it easier to maintain and scale scraping pipelines.

Best Practices

✅ Use middleware for proxy management

✅ Rotate proxies across requests

✅ Monitor failed requests

✅ Avoid hardcoding proxies in spiders

Common Mistakes

❌ Using a single proxy

❌ Not handling failed proxies

❌ Mixing proxy logic inside spiders

❌ Ignoring request errors

O

Middleware priority is everything - your custom proxy middleware has gotta jump ahead of HttpProxyMiddleware which runs at priority 750, so throw it at like 400 or 600, not 750 itself, or your proxy assignment just gets ghosted, and authentication headers get nuked on retry - you need to manually inject that proxy-authorization header in your middleware, otherwise you're gonna get hammered with 407 errors every time it retries. Also the default retry logic is basically a dumpster fire that loops infinitely - scrapy just keeps hammering the same failing proxy over and over, so you gotta write custom retry middleware that actually rotates to a fresh proxy each time. Plus, the article completely glosses over stuff like soft-ban detection , those capctha pages that sneakily return a 200, sticky sessions for authenticated flows, and proxy health monitoring to catch dead IPs before they tank your job. So teams shipping this thing to production without really understanding what's under the hood are gonna get wrecked with phantom failures, auth breakdowns, and hemorrhaged bandwidth

O

Middleware priority is everything - your custom proxy middleware has gotta jump ahead of HttpProxyMiddleware which runs at priority 750, so throw it at like 400 or 600, not 750 itself, or your proxy assignment just gets ghosted, and authentication headers get nuked on retry - you need to manually inject that proxy-authorization header in your middleware, otherwise you're gonna get hammered with 407 errors every time it retries. Also the default retry logic is basically a dumpster fire that loops infinitely - scrapy just keeps hammering the same failing proxy over and over, so you gotta write custom retry middleware that actually rotates to a fresh proxy each time. Plus, the article completely glosses over stuff like soft-ban detection , those capctha pages that sneakily return a 200, sticky sessions for authenticated flows, and proxy health monitoring to catch dead IPs before they tank your job. So teams shipping this thing to production without really understanding what's under the hood are gonna get wrecked with phantom failures, auth breakdowns, and hemorrhaged bandwidth