Question
How can custom HTTP headers be added to an HTTP request made with PHP cURL? For example, how would a request include these non-standard headers?
X-Apple-Tz: 0
X-Apple-Store-Front: 143444,12
Short Answer
You will learn how HTTP request headers work and how to send custom headers in PHP with CURLOPT_HTTPHEADER. You will also see how to combine custom headers with common headers such as Accept and Authorization, inspect outgoing requests, and avoid replacing headers accidentally.
Concept
HTTP headers are name-and-value pieces of metadata sent alongside an HTTP request. They describe information about the request rather than its main body.
For example, a server may use headers to determine:
- Which response formats the client accepts (
Accept) - Whether the request is authenticated (
Authorization) - Which application or device made the request (
User-Agent) - A client-specific setting required by an API (
X-Apple-Tz)
In PHP cURL, custom request headers are set with CURLOPT_HTTPHEADER. Its value must be an array of strings, with each string formatted exactly as:
Header-Name: header value
The header name and its value are separated by a colon. cURL adds the supplied headers when it sends the request.
Custom headers are common when integrating with APIs. However, a header alone does not grant access to a private service: servers can also require valid credentials, signed requests, cookies, rate-limit compliance, and permission to use the API.
Mental Model
Think of an HTTP request as a package sent to a company.
- The URL is the delivery address.
- The request body is the item inside the package.
- Headers are labels on the outside, such as “Fragile,” “Deliver before noon,” or “Sender account: 123.”
CURLOPT_HTTPHEADER is the step where you attach those labels. Each array item is one label, written as Name: Value.
Syntax and Examples
Use curl_setopt() with CURLOPT_HTTPHEADER.
<?php
$ch = curl_init('https://example.com/api/artwork');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-Apple-Tz: 0',
'X-Apple-Store-Front: 143444,12',
]);
$response = curl_exec($ch);
if ($response === false) {
throw new RuntimeException(curl_error($ch));
}
curl_close($ch);
echo $response;
The array passed to CURLOPT_HTTPHEADER contains two strings:
'X-Apple-Tz: 0'sends theX-Apple-Tzheader with value0.- sends the header with value .
Step by Step Execution
Consider this request:
<?php
$ch = curl_init('https://httpbin.org/headers');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-App-Name: ArtworkTool',
'X-Apple-Tz: 0',
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Step by step:
-
curl_init(...)creates a cURL handle configured with the target URL. -
CURLOPT_RETURNTRANSFERtells cURL to return the response as a string instead of printing it immediately. -
CURLOPT_HTTPHEADERreceives an array of header lines. -
When
curl_exec($ch)runs, cURL makes the HTTP request and includes:X-App-Name: ArtworkTool X-Apple-Tz: 0 -
The endpoint returns a JSON response containing the headers it received.
Real World Use Cases
Custom headers are used in many kinds of PHP integrations:
- API authentication: Send
Authorization: Bearer ...or an API key header required by a service. - JSON APIs: Send
Accept: application/jsonto state that the client expects JSON. - Content submission: Send
Content-Type: application/jsonwhen posting JSON data. - Client identification: Send a
User-Agentor application-specificX-Client-Versionheader. - Tracing requests: Send a correlation ID such as
X-Request-IDso logs across multiple services can be connected. - Feature controls: Send a header such as
X-Feature-Preview: enabledwhen an internal API supports an approved preview mode. - Localization: Send a locale or time zone header when an API uses it to format dates, currencies, or translated content.
Always use a service's documented API and authentication process. Headers observed from another client may be internal, change without notice, or be insufficient for authorized access.
Real Codebase Usage
In production code, developers usually build headers from request context and add only the headers required by the endpoint.
Build a reusable request function
<?php
function getJson(string $url, string $token): array
{
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
'X-Request-ID: ' . bin2hex(random_bytes(8)),
],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($body === false) {
$error = curl_error($ch);
curl_close();
();
}
();
( < || >= ) {
();
}
(, , , JSON_THROW_ON_ERROR);
}
Common Mistakes
Passing one string instead of an array
CURLOPT_HTTPHEADER expects an array.
// Incorrect
curl_setopt($ch, CURLOPT_HTTPHEADER, 'X-Apple-Tz: 0');
// Correct
curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-Apple-Tz: 0']);
Omitting the colon
A header must have the Name: Value format.
// Incorrect
'X-Apple-Tz 0'
// Correct
'X-Apple-Tz: 0'
Including Copy from copied documentation
Some web pages show a “Copy” control beside sample headers. Copy is not part of the actual header name.
// Incorrect: sends a header literally named CopyX-Apple-Tz
'CopyX-Apple-Tz: 0'
Comparisons
| Item | Purpose | Example |
|---|---|---|
CURLOPT_HTTPHEADER | Sets request headers as an array of Name: Value strings | ['Accept: application/json'] |
CURLOPT_POSTFIELDS | Sets the request body data | json_encode(['name' => 'Album']) |
Accept header | Says what response format the client prefers | Accept: application/json |
Content-Type header | Says what format the request body uses | Content-Type: application/json |
CURLOPT_USERAGENT |
Cheat Sheet
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Header-Name: value',
'Another-Header: another value',
]);
CURLOPT_HTTPHEADERrequires an array.- Each item is one complete header line:
'Name: Value'. - Do not include UI text such as
Copyin the header name. - Set all headers in one call; a new call replaces the prior list.
- Use
Accept: application/jsonwhen expecting JSON. - Use
Content-Type: application/jsonwhen sending a JSON body. - Check cURL failures with
curl_error($ch). - Check server responses with
curl_getinfo($ch, CURLINFO_RESPONSE_CODE). - Use
CURLOPT_VERBOSEduring local debugging to inspect request details; do not expose sensitive logs.
curl_setopt($ch, CURLOPT_VERBOSE, true);
Example custom headers:
[
'X-Apple-Tz: 0',
'X-Apple-Store-Front: 143444,12',
]
FAQ
How do I add custom headers to a PHP cURL request?
Pass an array of Header-Name: value strings to CURLOPT_HTTPHEADER.
curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-App-Name: MyApp']);
Can I send multiple headers with PHP cURL?
Yes. Add each header as a separate array item.
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Accept: application/json',
'X-App-Name: MyApp',
]);
Does PHP cURL add headers or replace them?
When you set CURLOPT_HTTPHEADER again, the new array replaces the header list previously set with that option. Build one complete array before calling it.
What is the difference between Accept and Content-Type?
Accept describes the response format you want. Content-Type describes the format of the request body you are sending.
Do custom headers need an X- prefix?
No. The X- prefix is a historical convention, not a requirement. Use the exact header names defined by the API you are calling.
Can a custom header authenticate me to an API?
Mini Project
Description
Create a small PHP command-line client that sends custom headers to a test endpoint and prints the headers that the server received. This is a safe way to practice correct header formatting and verify your cURL configuration.
Goal
Send two custom headers with PHP cURL and confirm that the server receives them.
Requirements
Use PHP cURL to make a GET request to https://httpbin.org/headers.
Set X-App-Name to HeaderPractice.
Set X-Client-Timezone to UTC.
Return the response as a string instead of printing it directly.
Handle a cURL execution error.
Decode and display the JSON response.
Keep learning
Related questions
Are PDO Prepared Statements Enough to Prevent SQL Injection in PHP?
Learn how PDO prepared statements prevent SQL injection in PHP, what they protect, and the mistakes that still leave MySQL apps vulnerable.
Can You Bind an Array to an IN Clause in PHP PDO?
Learn how PDO handles placeholders in IN() clauses, why arrays cannot be bound directly, and the safe PHP pattern to build dynamic queries.
Choosing the Right MySQL Collation for PHP and UTF-8
Learn how MySQL character sets and collations work with PHP, and how to choose a practical UTF-8 setup for web applications.