Skip to content

Managing Request Headers

This guide covers the internal structures and functions for handling HTTP headers in NGINX module development.


Header Storage Architecture

NGINX stores headers in two main structures:

Structure Purpose
headers_in Input request headers from client
headers_out Output headers for response

Both structures live in ngx_http_request_t. There is no separate "response" entity—all data is stored in a single request structure.


HTTP Header Flexibility

HTTP headers are flexible but complex:

  • Single header with one value
  • Multiple headers with the same name
  • One header split across multiple lines

NGINX handles this complexity with optimized storage:

  1. Known headers → Direct pointer in headers_in/headers_out
  2. Multi-value headers → Arrays (e.g., Cookies, Cache-Control)
  3. Numeric headers → Pre-parsed numeric field (e.g., content_length_n)
  4. Unknown headers → Plain list (headers.headers)

Getting Header Values

Search through all headers by name:

static ngx_table_elt_t *
search_headers_in(ngx_http_request_t *r, u_char *name, size_t len) {
    ngx_list_part_t  *part;
    ngx_table_elt_t  *h;
    ngx_uint_t        i;

    part = &r->headers_in.headers.part;
    h = part->elts;

    for (i = 0; /* void */ ; i++) {
        if (i >= part->nelts) {
            if (part->next == NULL) {
                break;
            }
            part = part->next;
            h = part->elts;
            i = 0;
        }

        if (len != h[i].key.len || 
            ngx_strcasecmp(name, h[i].key.data) != 0) {
            continue;
        }

        return &h[i];
    }

    return NULL;
}

Method 2: Hash Lookup (Known Headers)

Fast lookup for headers NGINX knows about:

ngx_table_elt_t *
search_hashed_headers_in(ngx_http_request_t *r, u_char *name, size_t len) {
    ngx_http_core_main_conf_t  *cmcf;
    ngx_http_header_t          *hh;
    u_char                     *lowcase_key;
    ngx_uint_t                  i, hash;

    lowcase_key = ngx_palloc(r->pool, len);
    if (lowcase_key == NULL) {
        return NULL;
    }

    hash = 0;
    for (i = 0; i < len; i++) {
        lowcase_key[i] = ngx_tolower(name[i]);
        hash = ngx_hash(hash, lowcase_key[i]);
    }

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
    hh = ngx_hash_find(&cmcf->headers_in_hash, hash, lowcase_key, len);

    if (hh == NULL || hh->offset == 0) {
        return NULL;
    }

    return *((ngx_table_elt_t **) ((char *) &r->headers_in + hh->offset));
}

Method 3: Direct Field Access (Fastest)

For known headers, access the field directly:

ngx_table_elt_t *
get_host_from_headers_in(ngx_http_request_t *r) {
    return r->headers_in.host;  // NULL if not present
}

Method 4: Pre-Parsed Numeric Values (Fastest)

For numeric headers:

off_t
get_content_length_n_from_headers_in(ngx_http_request_t *r) {
    return r->headers_in.content_length_n;  // -1 if not set
}

Setting Header Values

Setting Known Headers (e.g., Content-Length)

ngx_int_t
set_content_length_n_in_headers_out(ngx_http_request_t *r, 
                                     ngx_str_t *length, 
                                     off_t length_n) {
    ngx_table_elt_t *h;

    h = r->headers_out.content_length;
    if (h == NULL) {
        h = ngx_list_push(&r->headers_out.headers);
        if (h == NULL) {
            return NGX_ERROR;
        }

        h->key.data = (u_char *) "Content-Length";
        h->key.len = sizeof("Content-Length") - 1;
        r->headers_out.content_length = h;
    }

    h->value = *length;
    h->hash = 1;  // Mark as active (required!)

    r->headers_out.content_length_n = length_n;

    return NGX_OK;
}

Setting Custom (Unknown) Headers

ngx_int_t
set_custom_header_in_headers_out(ngx_http_request_t *r, 
                                  ngx_str_t *key, 
                                  ngx_str_t *value) {
    ngx_table_elt_t *h;

    h = ngx_list_push(&r->headers_out.headers);
    if (h == NULL) {
        return NGX_ERROR;
    }

    h->key = *key;
    h->value = *value;
    h->hash = 1;  // Mark as active

    return NGX_OK;
}

h->hash = 1

This tells ngx_http_header_module to include the header in the response. Without it, the header is ignored.


Proxy Pass Considerations

When using proxy_pass with custom headers, you must set the lowercased key:

header->key = (u_char *) "X-Custom-Header";
header->lowcase_key = (u_char *) "x-custom-header";

Crash Warning

The HTTP proxy module expects lowercased keys. Without lowcase_key, the module will crash on subrequests.


Common Known Headers

headers_in

Field Header
host Host
connection Connection
user_agent User-Agent
referer Referer
content_length Content-Length
content_type Content-Type
authorization Authorization
cookies Cookie (array)

headers_out

Field Header
status HTTP status code
content_length Content-Length
content_type Content-Type
location Location
last_modified Last-Modified
etag ETag

References