This is a simple Nginx module that demonstrates how to create custom functionality in Nginx. The module adds a custom header and returns a simple HTML response.
The goal of this module is to demonstrate the fundamental concepts of Nginx module development by creating a simple "Hello World" module that:
- Intercepts HTTP requests
- Adds a custom header to the response
- Returns a simple HTML page
- Shows how to properly handle the Nginx request/response cycle
This serves as a foundation for understanding how to:
- Create custom Nginx modules
- Handle HTTP requests
- Modify response headers
- Generate response content
- Integrate with Nginx's configuration system
Here's a detailed breakdown of what happens when a request is processed:
-
Request Arrival
- A client sends an HTTP request to Nginx
- Nginx matches the request to our location block (defined in nginx.conf)
- The
hellodirective is encountered in the configuration
-
Configuration Phase
- Nginx calls our
ngx_http_hellofunction (the command handler) - This function sets up our
ngx_http_hello_handleras the content handler - The configuration is now ready to handle requests
- Nginx calls our
-
Request Processing
- When a request matches our location, Nginx calls our handler
- Our handler (
ngx_http_hello_handler) is executed - The handler:
a. Sets the content type to HTML
b. Overrides the default Server header (
server: nginx) c. Adds our custom "X-Hello-Module" header d. Creates a buffer with our HTML response e. Sets up the response chain f. Sends the headers and body
-
Response Generation
- Headers are sent first via
ngx_http_send_header - The response body is sent through Nginx's output filter chain
- The client receives the complete response
- Headers are sent first via
ngx_module_t ngx_http_hello_module = {
NGX_MODULE_V1, // Module version
&ngx_http_hello_module_ctx, // Module context
ngx_http_hello_commands, // Module directives
NGX_HTTP_MODULE, // Module type
NULL, // init master
NULL, // init module
NULL, // init process
NULL, // init thread
NULL, // exit thread
NULL, // exit process
NULL, // exit master
NGX_MODULE_V1_PADDING
};This is the main module definition. It tells Nginx:
- What type of module it is (HTTP module)
- What commands/directives it provides
- What context it needs
- Various initialization and cleanup functions (all NULL in our case)
static ngx_command_t ngx_http_hello_commands[] = {
{ ngx_string("hello"), // Command name
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS, // Where it can be used and arguments (in location and without arguments)
ngx_http_hello, // Handler function
0, // Offset in configuration
0, // Offset in location configuration
NULL }, // Post handler
ngx_null_command // End of commands array
};This defines what commands our module provides. In our case, we have one command called hello that:
- Can be used in location blocks (
NGX_HTTP_LOC_CONF) - Takes no arguments (
NGX_CONF_NOARGS) - Is handled by the
ngx_http_hellofunction
static ngx_http_module_t ngx_http_hello_module_ctx = {
NULL, // preconfiguration
NULL, // postconfiguration
NULL, // create main configuration
NULL, // init main configuration
NULL, // create server configuration
NULL, // merge server configuration
NULL, // create location configuration
NULL // merge location configuration
};The module context defines how our module interacts with Nginx's configuration system. Since our module is simple and doesn't need to store any configuration, all these are NULL.
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;
}This function is called when Nginx encounters our hello directive in the configuration. It:
- Gets the location configuration
- Sets our handler function (
ngx_http_hello_handler) as the content handler for this location
static ngx_int_t
ngx_http_hello_handler(ngx_http_request_t *r)
{
// Set content type
r->headers_out.content_type.len = sizeof("text/html") - 1;
r->headers_out.content_type.data = (u_char *) "text/html";
// Add custom header
ngx_table_elt_t *h = ngx_list_push(&r->headers_out.headers);
h->hash = 1;
ngx_str_set(&h->key, "X-Hello-Module");
ngx_str_set(&h->value, "Hello from Nginx Module!!!!");
// Create response buffer
ngx_buf_t *b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
b->pos = (u_char *) "<html><body><h1>Hello from Nginx Module!</h1></body></html>";
b->last = b->pos + sizeof("<html><body><h1>Hello from Nginx Module!</h1></body></html>") - 1;
b->memory = 1;
b->last_buf = 1;
// Create output chain
ngx_chain_t *out = ngx_pcalloc(r->pool, sizeof(ngx_chain_t));
out->buf = b;
out->next = NULL;
// Set response status and length
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = b->last - b->pos;
// Send headers and body
ngx_http_send_header(r);
return ngx_http_output_filter(r, out);
}This is the most important part of our code: The handler! here are the things that our handler does:
- Sets the content type to HTML
- Adds our custom "X-Hello-Module" header and override the Server header.
- Creates a buffer with our HTML response
- Sets up the response chain
- Sends the headers and body
- Build the Docker container:
docker compose up --build- Test the module:
curl -v http://localhost:8080You should see:
- A 200 OK response
- The custom "X-Hello-Module" header
- The HTML content we defined
When a request comes to Nginx:
- Nginx matches the URL to our location block
- Sees the
hellodirective - Calls our handler function
- Our handler creates and sends the response
This is a basic example, but it shows the core concepts of Nginx module development:
- Module definition
- Command registration
- Request handling
- Response generation