Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use the headers argument on scrapy.Request when one request needs custom values. Use DEFAULT_REQUEST_HEADERS in settings.py for project-wide defaults. Scrapy’s default-header middleware fills only headers that are missing, so a value supplied on an individual request wins. Cookies, Referer, and request fingerprints have separate behavior that you must account for.

The examples below follow the current Scrapy 2.19.0 documentation. Check the linked API pages if you maintain a project across Scrapy releases.

Choose the right place to set a header

Need Use What happens
A different value on one request scrapy.Request(..., headers={...}) The value is attached at the call site and takes precedence over a project default.
The same fallback values throughout a project DEFAULT_REQUEST_HEADERS DefaultHeadersMiddleware adds configured values only when the request does not already contain that header.
Cookie state managed by Scrapy The request’s cookies argument Cookie middleware understands these cookies; a raw Cookie header is not treated as managed cookie state.
Control or limit Referer Referer middleware, REFERER_POLICY, and request metadata Middleware can derive a Referer from the response that created a request, replacing a configured default in many cases.

Scrapy’s request and response API is documented at the Requests and Responses reference. Its settings reference lists the built-in default values and middleware settings at the Scrapy settings documentation.

Add headers to one request

Pass a dictionary-like mapping to headers when creating the request. This is the most precise option when only one URL, endpoint, or API call needs a special value.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import scrapy

class ExampleSpider(scrapy.Spider):
    name = "example"
    start_urls = ["https://example.com"]

    def start_requests(self):
        yield scrapy.Request(
            "https://example.com",
            headers={
                "Accept-Language": "fr",
                "X-Client": "my-spider",
            },
        )

If your spider already yields requests elsewhere, the essential form is:

yield scrapy.Request(
    url,
    headers={"X-Client": "my-spider"},
)

Header values can be strings for single-valued headers or lists for multi-valued headers. The request exposes them through a dictionary-like scrapy.http.headers.Headers object. Passing None as a value means that header is not sent. These details are defined in the Request API reference.

Use a list for a multi-valued header

yield scrapy.Request(
    "https://example.com/feed",
    headers={
        "Accept": ["application/xml", "text/xml"],
        "X-Trace": "crawl-2026-09-29",
    },
)

Only use a list when the target service and the HTTP header semantics allow multiple values. For ordinary headers such as Accept-Language or an authorization token, a string is usually clearer.

Omit a value explicitly

yield scrapy.Request(
    "https://example.com",
    headers={"X-Optional": None},
)

A None value tells Scrapy not to send that header. If a project default exists for the same name, verify the resulting request in your own downloader logs or instrumentation rather than assuming a default has been removed at every middleware stage.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Set project-wide default headers

Put common fallback values in the project’s settings.py:

DEFAULT_REQUEST_HEADERS = {
    "Accept": "application/json",
    "Accept-Language": "en",
    "X-Client": "my-spider",
}

Scrapy documents built-in defaults of Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 and Accept-Language: en. The DefaultHeadersMiddleware component applies values from DEFAULT_REQUEST_HEADERS. See the Downloader Middleware documentation and the Settings reference.

Per-request values override defaults

The middleware uses request.headers.setdefault(k, v). In practical terms, a configured default fills an absent key; it does not overwrite a value already placed on the request.

# settings.py
DEFAULT_REQUEST_HEADERS = {
    "Accept": "application/json",
    "X-Client": "default-client",
}

# spider
yield scrapy.Request(
    "https://example.com/special",
    headers={
        "Accept": "text/csv",
        "X-Client": "csv-exporter",
    },
)

The special request receives text/csv and csv-exporter; other requests without those keys receive the defaults. This precedence lets you keep authentication or content-negotiation policy centralized while making deliberate exceptions locally.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Cookies are not ordinary custom headers

When Scrapy’s cookie middleware should maintain session state, pass cookies through the request’s cookies argument:

yield scrapy.Request(
    "https://example.com/account",
    cookies={
        "session_id": "abc123",
        "language": "en",
    },
)

The settings documentation cautions that cookies supplied as a raw Cookie header are not considered by that middleware. A manually composed header may reach the server, but it will not provide the same cookie-jar behavior for subsequent requests. Use the Cookie header directly only when you intentionally need to control the wire value and do not expect cookie middleware to manage it.

Understand Referer middleware

RefererMiddleware derives a Referer header from the response that generated a new request. Consequently, a Referer placed in DEFAULT_REQUEST_HEADERS generally reaches requests for which the middleware does not set one, such as start requests; it is not a guaranteed override for follow-up requests.

Set the policy globally

Use the REFERER_POLICY setting to select the policy documented by Scrapy. The available policy controls how much of the originating URL may be sent and whether a referrer is sent at all. The behavior and setting names are described in the Spider Middleware documentation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Set a policy for one request

Scrapy also supports a per-request referrer_policy metadata key:

yield scrapy.Request(
    "https://example.com/next",
    meta={"referrer_policy": "no-referrer"},
)

Choose a policy that matches the target site’s documented requirements and your privacy obligations. Do not assume that adding a hand-written Referer header defeats middleware policy; inspect the final request behavior when the distinction matters.

Headers and request fingerprints

Adding a header does not automatically make two otherwise identical requests distinct to Scrapy’s default request fingerprinter. The request-fingerprinting reference says headers are ignored by default. If a selected header must participate in deduplication or HTTP cache identity, include it through the fingerprinter’s include_headers argument as described in Scrapy’s request utility reference.

This matters for language, authorization, tenant, or API-version headers: two requests with different logical identities can otherwise share a fingerprint. Decide deliberately whether that header should change duplicate filtering and cache keys; including volatile headers can reduce cache reuse.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A complete spider pattern

The following example combines project defaults, a per-request override, managed cookies, and a request-specific referrer policy:

# settings.py
DEFAULT_REQUEST_HEADERS = {
    "Accept": "application/json",
    "Accept-Language": "en",
    "X-Client": "catalog-spider",
}

# spiders/catalog.py
import scrapy

class CatalogSpider(scrapy.Spider):
    name = "catalog"
    start_urls = ["https://example.com/catalog"]

    def parse(self, response):
        yield scrapy.Request(
            "https://example.com/catalog/export",
            headers={
                "Accept": "text/csv",
                "X-Export-Format": "csv",
            },
            cookies={"region": "us"},
            meta={"referrer_policy": "same-origin"},
            callback=self.parse_export,
        )

    def parse_export(self, response):
        yield {"status": response.status, "length": len(response.body)}

Here, Accept-Language and X-Client come from settings, while the export request replaces only Accept and adds X-Export-Format. The region cookie is handed to cookie middleware, and the referrer behavior is selected through metadata rather than by assuming a static default header.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Common failures and fixes

The custom value never appears

  • Check that the header mapping is passed to the Request you actually yield. Setting a variable on the spider does nothing unless it is attached to the request.
  • Confirm the spelling and capitalization of the key. HTTP field names are treated case-insensitively, but a typo creates a different key.
  • Look for another downloader middleware or request callback that creates a new request without copying the headers.

The default overwrites my request value

Scrapy’s default-header middleware uses setdefault, so the documented behavior is the opposite: an existing per-request key is preserved. If you see a different result, another middleware, a redirect, or a newly generated request is likely involved. Trace the request object at the point where it is yielded and compare it with the final downloader instrumentation.

My manually set Referer changes on follow-up requests

That is expected when Referer middleware derives a value from the parent response. Configure REFERER_POLICY or the request’s referrer_policy metadata instead of relying on a project-wide static header. Refer to the official middleware rules.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Cookies do not persist

Move cookie data from a raw Cookie header to the request’s cookies argument when cookie middleware should maintain the session. Ensure you are not disabling or bypassing the cookie middleware for that request.

Requests with different headers are deduplicated

Default fingerprints ignore headers. Configure fingerprinting to include only the headers that define request identity; including every dynamic header can create unnecessary duplicates and reduce cache effectiveness. The implementation details are in the request utility source documentation.

The server rejects my User-Agent or Accept

There is no universal browser-style header set. Values depend on the target service, API contract, authentication method, and access rules. Read that service’s published guidance, send only values you need, and do not treat custom headers as a way to bypass robots rules, bot checks, authentication, or other access controls.

Reliability, performance, and maintenance

  • Keep stable defaults small. A short set of project defaults is easier to audit than copying a browser dump into every request.
  • Make exceptions local. Put endpoint-specific content negotiation, tenant IDs, or API versions beside the request that needs them.
  • Protect secrets. Do not hard-code tokens in a spider committed to source control; load them from Scrapy settings populated by your deployment environment.
  • Consider cache identity. If a header changes the representation or authorization context, decide whether it belongs in the request fingerprint and HTTP cache key.
  • Expect middleware to transform requests. Cookies, redirects, retries, authentication middleware, and Referer handling can all make the final wire request differ from the object created in a callback.
  • Use the target’s contract. Header names and values are not interchangeable across services. Follow the API documentation for required media types, authorization syntax, language tags, and rate-limit expectations.

Or skip the browser setup

If your goal is a rendered page image rather than extracting responses with Scrapy, ScreenshotNeo provides a single HTTP call. Before capture it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response identifies the result with X-Page-Verdict and X-Billed headers. It also offers an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For API details, see the ScreenshotNeo documentation. The following calls use the supplied API endpoint and save the returned WebP image:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan, and yearly billing gives two months free. Create a free ScreenshotNeo account to start without a card.

Frequently Asked Questions

Should I copy a browser’s entire header set into Scrapy?

No. Send the headers required by the target service and your application. A universal browser header set is neither necessary nor guaranteed to work.

Does a header value affect Scrapy’s duplicate filter automatically?

No. Scrapy’s default request fingerprinter ignores headers unless you configure selected headers through its include_headers option.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What is the correct spelling of the HTTP referrer field?

The HTTP header is spelled Referer. Scrapy’s RefererMiddleware and its referrer_policy metadata use that spelling.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.