Comprehensive development guide for nginx C modules, derived from the official nginx development documentation and community expertise. Contains 49 rules across 8 categories, prioritized by impact to guide correct module implementation and prevent common crashes, memory leaks, and undefined behavior.
When to Apply
Reference these guidelines when:
Writing new nginx C modules (handlers, filters, upstream, load-balancers)
Implementing configuration directives and merge logic
Managing memory with nginx pools and shared memory zones
Handling the HTTP request lifecycle (body reading, subrequests, finalization)
Working with nginx's event loop, timers, and thread pools
Rule Categories by Priority
Priority
Category
Impact
Prefix
1
Memory Management
CRITICAL
mem-
2
Request Lifecycle
CRITICAL
req-
3
Configuration System
HIGH
conf-
4
Handler Development
HIGH
handler-
5
Filter Chain
MEDIUM-HIGH
filter-
6
Upstream & Proxy
MEDIUM
upstream-
7
Event Loop & Concurrency
MEDIUM
event-
8
Data Structures & Strings
LOW-MEDIUM
ds-
Quick Reference
1. Memory Management (CRITICAL)
mem-pool-allocation - Use Pool Allocation Instead of Heap malloc
mem-check-allocation - Check Every Allocation Return for NULL
mem-pcalloc-structs - Use ngx_pcalloc for Struct Initialization
mem-cleanup-handlers - Register Pool Cleanup Handlers for External Resources
mem-pnalloc-strings - Use ngx_pnalloc for String Data Allocation
mem-pfree-limitations - Avoid Relying on ngx_pfree for Pool Allocations
mem-shared-slab - Use Slab Allocator for Shared Memory Zones
2. Request Lifecycle (CRITICAL)
req-finalize-once - Finalize Requests Exactly Once
req-no-access-after-finalize - Never Access Request After Finalization
req-body-async - Handle Request Body Reading Asynchronously
req-discard-body - Discard Request Body When Not Reading It
req-subrequest-completion - Use Post-Subrequest Handlers for Completion
req-count-reference - Increment Request Count Before Async Operations
req-internal-redirect - Return After Internal Redirect
3. Configuration System (HIGH)
conf-unset-init - Initialize Config Fields with UNSET Constants
conf-merge-all-fields - Merge All Config Fields in merge_loc_conf
conf-context-flags - Use Correct Context Flags for Directives
conf-null-command - Terminate Commands Array with ngx_null_command
conf-custom-handler - Use Custom Handlers for Complex Directive Parsing
conf-module-ctx-null - Set Unused Module Context Callbacks to NULL
conf-build-config - Write Correct config Build Script for Module Compilation
4. Handler Development (HIGH)
handler-send-header-first - Send Header Before Body Output
handler-last-buf - Set last_buf Flag on Final Buffer
handler-phase-registration - Register Phase Handlers in postconfiguration
handler-content-handler - Use content_handler for Location-Specific Response Generation
handler-error-page - Return HTTP Status Codes for Error Responses
handler-empty-response - Use header_only for Empty Body Responses
handler-module-ctx - Use Module Context for Per-Request State
handler-add-variable - Register Custom Variables in preconfiguration
5. Filter Chain (MEDIUM-HIGH)
filter-registration-order - Save and Replace Top Filter in postconfiguration
filter-call-next - Always Call Next Filter in the Chain
filter-check-subrequest - Distinguish Main Request from Subrequest in Filters
filter-buffer-chain-iteration - Iterate Buffer Chains Using cl->next Pattern
filter-buffering-flag - Set Buffering Flag When Accumulating Response Data
6. Upstream & Proxy (MEDIUM)
upstream-create-request - Build Complete Request Buffer in create_request
upstream-process-header - Parse Upstream Response Incrementally in process_header
upstream-peer-free - Track Failures in Peer free Callback
upstream-finalize - Clean Up Resources in finalize_request Callback
upstream-connection-reuse - Enable Keepalive for Upstream Connections
7. Event Loop & Concurrency (MEDIUM)
event-no-blocking - Never Use Blocking Calls in Event Handlers
event-timer-management - Delete Timers Before Freeing Associated Data
event-handle-read-write - Call ngx_handle_read/write_event After I/O Operations
event-thread-pool - Offload Blocking Operations to Thread Pool
event-posted-events - Use Posted Events for Deferred Processing
8. Data Structures & Strings (LOW-MEDIUM)
ds-ngx-str-not-null-terminated - Never Assume ngx_str_t Is Null-Terminated
ds-ngx-str-set-literals - Use ngx_string Macro Only with String Literals
ds-cpymem-pattern - Use ngx_cpymem for Sequential Buffer Writes
ds-list-iteration - Iterate ngx_list_t Using Part-Based Pattern
ds-hash-readonly - Build Hash Tables During Configuration Only
How to Use
Read individual reference files for detailed explanations and code examples:
Section definitions - Category structure and impact levels
Rule template - Template for adding new rules
Reference Files
File
Description
references/_sections.md
Category definitions and ordering
assets/templates/_template.md
Template for new rules
metadata.json
Version and reference information
1---2name: nginx-c-modules3description: nginx.org C Module Development Best Practices4---5# nginx.org C Module Development Best Practices67Comprehensive development guide for nginx C modules, derived from the official nginx development documentation and community expertise. Contains 49 rules across 8 categories, prioritized by impact to guide correct module implementation and prevent common crashes, memory leaks, and undefined behavior.89## When to Apply1011Reference these guidelines when:12- Writing new nginx C modules (handlers, filters, upstream, load-balancers)13- Implementing configuration directives and merge logic14- Managing memory with nginx pools and shared memory zones15- Handling the HTTP request lifecycle (body reading, subrequests, finalization)16- Working with nginx's event loop, timers, and thread pools1718## Rule Categories by Priority1920| Priority | Category | Impact | Prefix |21|----------|----------|--------|--------|22| 1 | Memory Management | CRITICAL | `mem-` |23| 2 | Request Lifecycle | CRITICAL | `req-` |24| 3 | Configuration System | HIGH | `conf-` |25| 4 | Handler Development | HIGH | `handler-` |26| 5 | Filter Chain | MEDIUM-HIGH | `filter-` |27| 6 | Upstream & Proxy | MEDIUM | `upstream-` |28| 7 | Event Loop & Concurrency | MEDIUM | `event-` |29| 8 | Data Structures & Strings | LOW-MEDIUM | `ds-` |3031## Quick Reference3233### 1. Memory Management (CRITICAL)3435- [`mem-pool-allocation`](references/mem-pool-allocation.md) - Use Pool Allocation Instead of Heap malloc36- [`mem-check-allocation`](references/mem-check-allocation.md) - Check Every Allocation Return for NULL37- [`mem-pcalloc-structs`](references/mem-pcalloc-structs.md) - Use ngx_pcalloc for Struct Initialization38- [`mem-cleanup-handlers`](references/mem-cleanup-handlers.md) - Register Pool Cleanup Handlers for External Resources39- [`mem-pnalloc-strings`](references/mem-pnalloc-strings.md) - Use ngx_pnalloc for String Data Allocation40- [`mem-pfree-limitations`](references/mem-pfree-limitations.md) - Avoid Relying on ngx_pfree for Pool Allocations41- [`mem-shared-slab`](references/mem-shared-slab.md) - Use Slab Allocator for Shared Memory Zones4243### 2. Request Lifecycle (CRITICAL)4445- [`req-finalize-once`](references/req-finalize-once.md) - Finalize Requests Exactly Once46- [`req-no-access-after-finalize`](references/req-no-access-after-finalize.md) - Never Access Request After Finalization47- [`req-body-async`](references/req-body-async.md) - Handle Request Body Reading Asynchronously48- [`req-discard-body`](references/req-discard-body.md) - Discard Request Body When Not Reading It49- [`req-subrequest-completion`](references/req-subrequest-completion.md) - Use Post-Subrequest Handlers for Completion50- [`req-count-reference`](references/req-count-reference.md) - Increment Request Count Before Async Operations51- [`req-internal-redirect`](references/req-internal-redirect.md) - Return After Internal Redirect5253### 3. Configuration System (HIGH)5455- [`conf-unset-init`](references/conf-unset-init.md) - Initialize Config Fields with UNSET Constants56- [`conf-merge-all-fields`](references/conf-merge-all-fields.md) - Merge All Config Fields in merge_loc_conf57- [`conf-context-flags`](references/conf-context-flags.md) - Use Correct Context Flags for Directives58- [`conf-null-command`](references/conf-null-command.md) - Terminate Commands Array with ngx_null_command59- [`conf-custom-handler`](references/conf-custom-handler.md) - Use Custom Handlers for Complex Directive Parsing60- [`conf-module-ctx-null`](references/conf-module-ctx-null.md) - Set Unused Module Context Callbacks to NULL61- [`conf-build-config`](references/conf-build-config.md) - Write Correct config Build Script for Module Compilation6263### 4. Handler Development (HIGH)6465- [`handler-send-header-first`](references/handler-send-header-first.md) - Send Header Before Body Output66- [`handler-last-buf`](references/handler-last-buf.md) - Set last_buf Flag on Final Buffer67- [`handler-phase-registration`](references/handler-phase-registration.md) - Register Phase Handlers in postconfiguration68- [`handler-content-handler`](references/handler-content-handler.md) - Use content_handler for Location-Specific Response Generation69- [`handler-error-page`](references/handler-error-page.md) - Return HTTP Status Codes for Error Responses70- [`handler-empty-response`](references/handler-empty-response.md) - Use header_only for Empty Body Responses71- [`handler-module-ctx`](references/handler-module-ctx.md) - Use Module Context for Per-Request State72- [`handler-add-variable`](references/handler-add-variable.md) - Register Custom Variables in preconfiguration7374### 5. Filter Chain (MEDIUM-HIGH)7576- [`filter-registration-order`](references/filter-registration-order.md) - Save and Replace Top Filter in postconfiguration77- [`filter-call-next`](references/filter-call-next.md) - Always Call Next Filter in the Chain78- [`filter-check-subrequest`](references/filter-check-subrequest.md) - Distinguish Main Request from Subrequest in Filters79- [`filter-buffer-chain-iteration`](references/filter-buffer-chain-iteration.md) - Iterate Buffer Chains Using cl->next Pattern80- [`filter-buffering-flag`](references/filter-buffering-flag.md) - Set Buffering Flag When Accumulating Response Data8182### 6. Upstream & Proxy (MEDIUM)8384- [`upstream-create-request`](references/upstream-create-request.md) - Build Complete Request Buffer in create_request85- [`upstream-process-header`](references/upstream-process-header.md) - Parse Upstream Response Incrementally in process_header86- [`upstream-peer-free`](references/upstream-peer-free.md) - Track Failures in Peer free Callback87- [`upstream-finalize`](references/upstream-finalize.md) - Clean Up Resources in finalize_request Callback88- [`upstream-connection-reuse`](references/upstream-connection-reuse.md) - Enable Keepalive for Upstream Connections8990### 7. Event Loop & Concurrency (MEDIUM)9192- [`event-no-blocking`](references/event-no-blocking.md) - Never Use Blocking Calls in Event Handlers93- [`event-timer-management`](references/event-timer-management.md) - Delete Timers Before Freeing Associated Data94- [`event-handle-read-write`](references/event-handle-read-write.md) - Call ngx_handle_read/write_event After I/O Operations95- [`event-thread-pool`](references/event-thread-pool.md) - Offload Blocking Operations to Thread Pool96- [`event-posted-events`](references/event-posted-events.md) - Use Posted Events for Deferred Processing9798### 8. Data Structures & Strings (LOW-MEDIUM)99100- [`ds-ngx-str-not-null-terminated`](references/ds-ngx-str-not-null-terminated.md) - Never Assume ngx_str_t Is Null-Terminated101- [`ds-ngx-str-set-literals`](references/ds-ngx-str-set-literals.md) - Use ngx_string Macro Only with String Literals102- [`ds-cpymem-pattern`](references/ds-cpymem-pattern.md) - Use ngx_cpymem for Sequential Buffer Writes103- [`ds-list-iteration`](references/ds-list-iteration.md) - Iterate ngx_list_t Using Part-Based Pattern104- [`ds-hash-readonly`](references/ds-hash-readonly.md) - Build Hash Tables During Configuration Only105106## How to Use107108Read individual reference files for detailed explanations and code examples:109110- [Section definitions](references/_sections.md) - Category structure and impact levels111- [Rule template](assets/templates/_template.md) - Template for adding new rules112113## Reference Files114115| File | Description |116|------|-------------|117| [references/_sections.md](references/_sections.md) | Category definitions and ordering |118| [assets/templates/_template.md](assets/templates/_template.md) | Template for new rules |119| [metadata.json](metadata.json) | Version and reference information |
Run npx skillmds@latest add comeonoliver/nginx-c-modules in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
nginx.org C Module Development Best Practices It is listed under Coding & Dev Tools on SkillMD.
This skill has not completed SkillMD's automated safety review yet. Independent scanners report: SkillSpector: PASS, Skill Scanner: PASS. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
ComeOnOliver (@comeonoliver) published this skill. Their other Agent Skills are listed on their SkillMD profile.