Перейти к содержанию

Module Examples

Complete, working NGINX module examples you can study and build upon.


Example 1: Hello World Handler

The simplest possible HTTP module - returns "Hello, World!".

Source Code

/* ngx_http_hello_module.c */
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>

static char *ngx_http_hello(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_int_t ngx_http_hello_handler(ngx_http_request_t *r);

static ngx_command_t ngx_http_hello_commands[] = {
    {
        ngx_string("hello"),
        NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,
        ngx_http_hello,
        0,
        0,
        NULL
    },
    ngx_null_command
};

static ngx_http_module_t ngx_http_hello_module_ctx = {
    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};

ngx_module_t ngx_http_hello_module = {
    NGX_MODULE_V1,
    &ngx_http_hello_module_ctx,
    ngx_http_hello_commands,
    NGX_HTTP_MODULE,
    NULL, NULL, NULL, NULL, NULL, NULL, NULL,
    NGX_MODULE_V1_PADDING
};

static char *
ngx_http_hello(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_core_loc_conf_t *clcf;

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
    clcf->handler = ngx_http_hello_handler;

    return NGX_CONF_OK;
}

static ngx_int_t
ngx_http_hello_handler(ngx_http_request_t *r)
{
    ngx_int_t    rc;
    ngx_buf_t   *b;
    ngx_chain_t  out;

    static u_char hello[] = "Hello, World!\n";

    /* Only handle GET and HEAD */
    if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))) {
        return NGX_HTTP_NOT_ALLOWED;
    }

    /* Discard request body */
    rc = ngx_http_discard_request_body(r);
    if (rc != NGX_OK) {
        return rc;
    }

    /* Set response headers */
    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = sizeof(hello) - 1;
    r->headers_out.content_type_len = sizeof("text/plain") - 1;
    ngx_str_set(&r->headers_out.content_type, "text/plain");

    /* Send headers */
    rc = ngx_http_send_header(r);
    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
        return rc;
    }

    /* Create response buffer */
    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
    if (b == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    b->pos = hello;
    b->last = hello + sizeof(hello) - 1;
    b->memory = 1;
    b->last_buf = 1;

    out.buf = b;
    out.next = NULL;

    return ngx_http_output_filter(r, &out);
}

Config File

# config
ngx_addon_name=ngx_http_hello_module
ngx_module_type=HTTP
ngx_module_name=ngx_http_hello_module
ngx_module_srcs="$ngx_addon_dir/ngx_http_hello_module.c"
. auto/module

Usage

location /hello {
    hello;
}

Example 2: JSON Echo Handler

Returns request information as JSON.

Source Code

/* ngx_http_echo_json_module.c */
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>

static char *ngx_http_echo_json(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_int_t ngx_http_echo_json_handler(ngx_http_request_t *r);

static ngx_command_t ngx_http_echo_json_commands[] = {
    {
        ngx_string("echo_json"),
        NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,
        ngx_http_echo_json,
        0,
        0,
        NULL
    },
    ngx_null_command
};

static ngx_http_module_t ngx_http_echo_json_module_ctx = {
    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};

ngx_module_t ngx_http_echo_json_module = {
    NGX_MODULE_V1,
    &ngx_http_echo_json_module_ctx,
    ngx_http_echo_json_commands,
    NGX_HTTP_MODULE,
    NULL, NULL, NULL, NULL, NULL, NULL, NULL,
    NGX_MODULE_V1_PADDING
};

static char *
ngx_http_echo_json(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_core_loc_conf_t *clcf;
    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
    clcf->handler = ngx_http_echo_json_handler;
    return NGX_CONF_OK;
}

static ngx_int_t
ngx_http_echo_json_handler(ngx_http_request_t *r)
{
    ngx_int_t    rc;
    ngx_buf_t   *b;
    ngx_chain_t  out;
    size_t       len;
    u_char      *json, *p;

    if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))) {
        return NGX_HTTP_NOT_ALLOWED;
    }

    rc = ngx_http_discard_request_body(r);
    if (rc != NGX_OK) {
        return rc;
    }

    /* Calculate JSON length */
    len = sizeof("{\"method\":\"\",\"uri\":\"\",\"args\":\"\",\"host\":\"\"}") - 1
          + r->method_name.len
          + r->uri.len
          + r->args.len
          + (r->headers_in.host ? r->headers_in.host->value.len : 0);

    /* Allocate buffer */
    json = ngx_pnalloc(r->pool, len + 100);  /* Extra space for escaping */
    if (json == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    /* Build JSON */
    p = ngx_sprintf(json,
        "{\"method\":\"%V\",\"uri\":\"%V\",\"args\":\"%V\",\"host\":\"%V\"}",
        &r->method_name,
        &r->uri,
        &r->args,
        r->headers_in.host ? &r->headers_in.host->value : &(ngx_str_t)ngx_string(""));

    len = p - json;

    /* Set headers */
    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = len;
    ngx_str_set(&r->headers_out.content_type, "application/json");

    rc = ngx_http_send_header(r);
    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
        return rc;
    }

    /* Create buffer */
    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
    if (b == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    b->pos = json;
    b->last = json + len;
    b->memory = 1;
    b->last_buf = 1;

    out.buf = b;
    out.next = NULL;

    return ngx_http_output_filter(r, &out);
}

Output

curl http://localhost/echo
# {"method":"GET","uri":"/echo","args":"","host":"localhost"}

curl "http://localhost/echo?foo=bar"
# {"method":"GET","uri":"/echo","args":"foo=bar","host":"localhost"}

Example 3: Access Phase Module

IP-based access control with custom header.

Source Code

/* ngx_http_ip_check_module.c */
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>

typedef struct {
    ngx_array_t  *allowed_ips;  /* Array of ngx_cidr_t */
} ngx_http_ip_check_loc_conf_t;

static ngx_int_t ngx_http_ip_check_init(ngx_conf_t *cf);
static void *ngx_http_ip_check_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_ip_check_merge_loc_conf(ngx_conf_t *cf,
    void *parent, void *child);
static char *ngx_http_ip_check_allow(ngx_conf_t *cf, ngx_command_t *cmd,
    void *conf);
static ngx_int_t ngx_http_ip_check_handler(ngx_http_request_t *r);

static ngx_command_t ngx_http_ip_check_commands[] = {
    {
        ngx_string("ip_check_allow"),
        NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,
        ngx_http_ip_check_allow,
        NGX_HTTP_LOC_CONF_OFFSET,
        0,
        NULL
    },
    ngx_null_command
};

static ngx_http_module_t ngx_http_ip_check_module_ctx = {
    NULL,
    ngx_http_ip_check_init,
    NULL,
    NULL,
    NULL,
    NULL,
    ngx_http_ip_check_create_loc_conf,
    ngx_http_ip_check_merge_loc_conf
};

ngx_module_t ngx_http_ip_check_module = {
    NGX_MODULE_V1,
    &ngx_http_ip_check_module_ctx,
    ngx_http_ip_check_commands,
    NGX_HTTP_MODULE,
    NULL, NULL, NULL, NULL, NULL, NULL, NULL,
    NGX_MODULE_V1_PADDING
};

static void *
ngx_http_ip_check_create_loc_conf(ngx_conf_t *cf)
{
    ngx_http_ip_check_loc_conf_t *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ip_check_loc_conf_t));
    if (conf == NULL) {
        return NULL;
    }

    return conf;
}

static char *
ngx_http_ip_check_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
    ngx_http_ip_check_loc_conf_t *prev = parent;
    ngx_http_ip_check_loc_conf_t *conf = child;

    if (conf->allowed_ips == NULL) {
        conf->allowed_ips = prev->allowed_ips;
    }

    return NGX_CONF_OK;
}

static char *
ngx_http_ip_check_allow(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_ip_check_loc_conf_t *iccf = conf;
    ngx_str_t *value;
    ngx_cidr_t *cidr;

    value = cf->args->elts;

    if (iccf->allowed_ips == NULL) {
        iccf->allowed_ips = ngx_array_create(cf->pool, 4, sizeof(ngx_cidr_t));
        if (iccf->allowed_ips == NULL) {
            return NGX_CONF_ERROR;
        }
    }

    cidr = ngx_array_push(iccf->allowed_ips);
    if (cidr == NULL) {
        return NGX_CONF_ERROR;
    }

    if (ngx_ptocidr(&value[1], cidr) != NGX_OK) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "invalid IP address or CIDR: \"%V\"", &value[1]);
        return NGX_CONF_ERROR;
    }

    return NGX_CONF_OK;
}

static ngx_int_t
ngx_http_ip_check_handler(ngx_http_request_t *r)
{
    ngx_http_ip_check_loc_conf_t *iccf;
    ngx_cidr_t *cidr;
    ngx_uint_t i;
    struct sockaddr_in *sin;

    iccf = ngx_http_get_module_loc_conf(r, ngx_http_ip_check_module);

    if (iccf->allowed_ips == NULL) {
        return NGX_DECLINED;  /* No restrictions */
    }

    /* Get client IP */
    if (r->connection->sockaddr->sa_family != AF_INET) {
        return NGX_DECLINED;  /* Only IPv4 for simplicity */
    }

    sin = (struct sockaddr_in *) r->connection->sockaddr;

    /* Check against allowed IPs */
    cidr = iccf->allowed_ips->elts;
    for (i = 0; i < iccf->allowed_ips->nelts; i++) {
        if ((sin->sin_addr.s_addr & cidr[i].u.in.mask)
            == cidr[i].u.in.addr)
        {
            return NGX_DECLINED;  /* Allowed, continue */
        }
    }

    ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
                  "ip_check: access denied for %V", &r->connection->addr_text);

    return NGX_HTTP_FORBIDDEN;
}

static ngx_int_t
ngx_http_ip_check_init(ngx_conf_t *cf)
{
    ngx_http_handler_pt *h;
    ngx_http_core_main_conf_t *cmcf;

    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

    h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
    if (h == NULL) {
        return NGX_ERROR;
    }

    *h = ngx_http_ip_check_handler;

    return NGX_OK;
}

Usage

location /admin {
    ip_check_allow 192.168.1.0/24;
    ip_check_allow 10.0.0.0/8;
    # ... other directives
}

Example 4: Response Filter

Adds a custom header to all responses.

Source Code

/* ngx_http_add_header_filter_module.c */
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>

static ngx_int_t ngx_http_add_header_filter_init(ngx_conf_t *cf);
static ngx_int_t ngx_http_add_header_filter(ngx_http_request_t *r);

static ngx_http_output_header_filter_pt ngx_http_next_header_filter;

static ngx_http_module_t ngx_http_add_header_filter_module_ctx = {
    NULL,
    ngx_http_add_header_filter_init,
    NULL, NULL, NULL, NULL, NULL, NULL
};

ngx_module_t ngx_http_add_header_filter_module = {
    NGX_MODULE_V1,
    &ngx_http_add_header_filter_module_ctx,
    NULL,
    NGX_HTTP_MODULE,
    NULL, NULL, NULL, NULL, NULL, NULL, NULL,
    NGX_MODULE_V1_PADDING
};

static ngx_int_t
ngx_http_add_header_filter(ngx_http_request_t *r)
{
    ngx_table_elt_t *h;

    /* Only add to main request */
    if (r != r->main) {
        return ngx_http_next_header_filter(r);
    }

    /* Add custom header */
    h = ngx_list_push(&r->headers_out.headers);
    if (h == NULL) {
        return NGX_ERROR;
    }

    h->hash = 1;
    ngx_str_set(&h->key, "X-Powered-By");
    ngx_str_set(&h->value, "NGINX Custom Module");

    return ngx_http_next_header_filter(r);
}

static ngx_int_t
ngx_http_add_header_filter_init(ngx_conf_t *cf)
{
    ngx_http_next_header_filter = ngx_http_top_header_filter;
    ngx_http_top_header_filter = ngx_http_add_header_filter;

    return NGX_OK;
}

Output

curl -I http://localhost/
# ...
# X-Powered-By: NGINX Custom Module
# ...

Building Examples

# Clone example
git clone https://github.com/example/nginx-module.git

# Build with NGINX
cd nginx-1.24.0
./configure --add-module=../nginx-module
make
sudo make install

# Or as dynamic module
./configure --add-dynamic-module=../nginx-module
make modules
sudo cp objs/ngx_http_*.so /etc/nginx/modules/

Next Steps

  • Study real modules in NGINX source (src/http/modules/)
  • Read HTTP API for more patterns
  • Check 3rd-party modules for inspiration