Viewed   124 times

I have a custom implementation of a RESTful API for a PHP application that returns json data, and in order to communicate the status of the operation, ie whether there were failures in the request, I'm setting a custom HTTP Header with a (very tiny) json object as a string. This works well since I can send responses and easily retrieve them client side without messing with the actual data sent.

The question is, are there any drawbacks I may not have realized of using this method? It doesn't seem to be very common for applications to set custom http headers, so I'm wondering whether it is a bad practice or bad "taste" somehow.



This is an interesting question. There shouldn't be any reason for it to be a problem, but you should take a few things into account:

  1. The headers need to be unique to your application. Not just now, but forever. You should ensure you're prefixing them, e.g. X-MyApplication-Foo: Bar. Not doing this might cause conflicts in future.

  2. Firewalls are sometimes (rarely) a little overzealous with filtering unknown HTTP headers. This shouldn't be a problem, but it's something to keep in mind.

  3. Older browsers have smaller limitations on header field sizes than modern browsers, so you need to test across as many as you can get hold of.

Is there are reason you can't use the standard HTTP error codes? I understand that you might want to provide stack traces or other useful debugging information, but in the case of an error, wouldn't you just return a JSON blob that contains the error information, rather than the normal "result" JSON data? You could easily detect the difference based on the HTTP error code and handle the two cases differently.

The reason I'm concerned by your suggested approach is that headers are used to alter or cause browser behaviour - they're not intended to be a data storage mechanism.

Pseudo-code example:

    case 200:
    case 403:
    case 500:
        // bad response code, handle appropriately
Tuesday, August 30, 2022

I think its only because of the HTTP Spec says to do this in every case possible.

Applications SHOULD use this field to indicate the transfer-length of the message-body, unless this is prohibited by the rules in section 4.4.

You can also look at Pauls Answer on the Question of Deaomon.

I think this will answer yours too.

Also you should use Content-Length if you want someone download a file with another header: e.g.

$file = "original.pdf"
$size = filesize($file);
header('Content-type: application/pdf');
header("Content-length: $size");
header('Content-Disposition: attachment; filename="downloaded.pdf"');
Wednesday, September 21, 2022

It has been religiously included by carg-cult copy-paste programmers since it was mentioned in a comment to the readfile() manpage on

It indeed has little to do with HTTP and you can safely remove it.

Wednesday, November 23, 2022

The recommendation is was to start their name with "X-". E.g. X-Forwarded-For, X-Requested-With. This is also mentioned in a.o. section 5 of RFC 2047.

Update 1: On June 2011, the first IETF draft was posted to deprecate the recommendation of using the "X-" prefix for non-standard headers. The reason is that when non-standard headers prefixed with "X-" become standard, removing the "X-" prefix breaks backwards compatibility, forcing application protocols to support both names (E.g, x-gzip & gzip are now equivalent). So, the official recommendation is to just name them sensibly without the "X-" prefix.

Update 2: On June 2012, the deprecation of recommendation to use the "X-" prefix has become official as RFC 6648. Below are cites of relevance:

3. Recommendations for Creators of New Parameters


  1. SHOULD NOT prefix their parameter names with "X-" or similar constructs.

4. Recommendations for Protocol Designers


  1. SHOULD NOT prohibit parameters with an "X-" prefix or similar constructs from being registered.

  2. MUST NOT stipulate that a parameter with an "X-" prefix or similar constructs needs to be understood as unstandardized.

  3. MUST NOT stipulate that a parameter without an "X-" prefix or similar constructs needs to be understood as standardized.

Note that "SHOULD NOT" ("discouraged") is not the same as "MUST NOT" ("forbidden"), see also RFC 2119 for another spec on those keywords. In other words, you can keep using "X-" prefixed headers, but it's not officially recommended anymore and you may definitely not document them as if they are public standard.


  • the official recommendation is to just name them sensibly without the "X-" prefix
  • you can keep using "X-" prefixed headers, but it's not officially recommended anymore and you may definitely not document them as if they are public standard
Monday, December 12, 2022
 * get_redirect_url()
 * Gets the address that the provided URL redirects to,
 * or FALSE if there's no redirect. 
 * @param string $url
 * @return string
function get_redirect_url($url){
    $redirect_url = null; 

    $url_parts = @parse_url($url);
    if (!$url_parts) return false;
    if (!isset($url_parts['host'])) return false; //can't process relative URLs
    if (!isset($url_parts['path'])) $url_parts['path'] = '/';

    $sock = fsockopen($url_parts['host'], (isset($url_parts['port']) ? (int)$url_parts['port'] : 80), $errno, $errstr, 30);
    if (!$sock) return false;

    $request = "HEAD " . $url_parts['path'] . (isset($url_parts['query']) ? '?'.$url_parts['query'] : '') . " HTTP/1.1rn"; 
    $request .= 'Host: ' . $url_parts['host'] . "rn"; 
    $request .= "Connection: Closernrn"; 
    fwrite($sock, $request);
    $response = '';
    while(!feof($sock)) $response .= fread($sock, 8192);

    if (preg_match('/^Location: (.+?)$/m', $response, $matches)){
        if ( substr($matches[1], 0, 1) == "/" )
            return $url_parts['scheme'] . "://" . $url_parts['host'] . trim($matches[1]);
            return trim($matches[1]);

    } else {
        return false;


 * get_all_redirects()
 * Follows and collects all redirects, in order, for the given URL. 
 * @param string $url
 * @return array
function get_all_redirects($url){
    $redirects = array();
    while ($newurl = get_redirect_url($url)){
        if (in_array($newurl, $redirects)){
        $redirects[] = $newurl;
        $url = $newurl;
    return $redirects;

 * get_final_url()
 * Gets the address that the URL ultimately leads to. 
 * Returns $url itself if it isn't a redirect.
 * @param string $url
 * @return string
function get_final_url($url){
    $redirects = get_all_redirects($url);
    if (count($redirects)>0){
        return array_pop($redirects);
    } else {
        return $url;

And, as always, give credit:

Wednesday, October 12, 2022
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :