diff --git a/Cargo.lock b/Cargo.lock
index 40161e1..6013633 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -576,6 +576,7 @@ dependencies = [
  "errno",
  "nginx-src",
  "regex",
+ "shlex",
 ]
 
 [[package]]
diff --git a/nginx-sys/Cargo.toml b/nginx-sys/Cargo.toml
index 8780749..b04c496 100644
--- a/nginx-sys/Cargo.toml
+++ b/nginx-sys/Cargo.toml
@@ -30,6 +30,7 @@ cc = "1.2.0"
 dunce = "1.0.5"
 regex = "1.11.1"
 nginx-src = { version = "~1.28.0", optional = true, path = "../nginx-src" }
+shlex = "1.3"
 
 [features]
 vendored = ["dep:nginx-src"]
diff --git a/nginx-sys/build/main.rs b/nginx-sys/build/main.rs
index a206dee..8f85bd0 100644
--- a/nginx-sys/build/main.rs
+++ b/nginx-sys/build/main.rs
@@ -192,7 +192,8 @@ impl NginxSource {
 /// Generates Rust bindings for NGINX
 fn generate_binding(nginx: &NginxSource) {
     let autoconf_makefile_path = nginx.build_dir.join("Makefile");
-    let includes: Vec<_> = parse_includes_from_makefile(&autoconf_makefile_path)
+    let (includes, defines) = parse_makefile(&autoconf_makefile_path);
+    let includes: Vec<_> = includes
         .into_iter()
         .map(|path| {
             if path.is_absolute() {
@@ -202,12 +203,20 @@ fn generate_binding(nginx: &NginxSource) {
             }
         })
         .collect();
-    let clang_args: Vec<String> = includes
+    let mut clang_args: Vec<String> = includes
         .iter()
         .map(|path| format!("-I{}", path.to_string_lossy()))
         .collect();
 
-    print_cargo_metadata(nginx, &includes).expect("cargo dependency metadata");
+    clang_args.extend(defines.iter().map(|(n, ov)| {
+        if let Some(v) = ov {
+            format!("-D{n}={v}")
+        } else {
+            format!("-D{n}")
+        }
+    }));
+
+    print_cargo_metadata(nginx, &includes, &defines).expect("cargo dependency metadata");
 
     // bindgen targets the latest known stable by default
     let rust_target: bindgen::RustTarget = env::var("CARGO_PKG_RUST_VERSION")
@@ -241,22 +250,47 @@ fn generate_binding(nginx: &NginxSource) {
 }
 
 /// Reads through the makefile generated by autoconf and finds all of the includes
-/// used to compile nginx. This is used to generate the correct bindings for the
-/// nginx source code.
-fn parse_includes_from_makefile(nginx_autoconf_makefile_path: &PathBuf) -> Vec<PathBuf> {
-    fn extract_include_part(line: &str) -> &str {
-        line.strip_suffix('\\').map_or(line, |s| s.trim())
-    }
-    /// Extracts the include path from a line of the autoconf generated makefile.
-    fn extract_after_i_flag(line: &str) -> Option<&str> {
-        let mut parts = line.split("-I ");
-        match parts.next() {
-            Some(_) => parts.next().map(extract_include_part),
-            None => None,
+/// and definitions used to compile nginx. This is used to generate the correct bindings
+/// for the nginx source code.
+pub fn parse_makefile(
+    nginx_autoconf_makefile_path: &PathBuf,
+) -> (Vec<PathBuf>, Vec<(String, Option<String>)>) {
+    fn parse_line(
+        includes: &mut Vec<String>,
+        defines: &mut Vec<(String, Option<String>)>,
+        line: &str,
+    ) {
+        let mut words = shlex::Shlex::new(line);
+
+        while let Some(word) = words.next() {
+            if let Some(inc) = word.strip_prefix("-I") {
+                let value = if inc.is_empty() {
+                    words.next().expect("-I argument")
+                } else {
+                    inc.to_string()
+                };
+                includes.push(value);
+            } else if let Some(def) = word.strip_prefix("-D") {
+                let def = if def.is_empty() {
+                    words.next().expect("-D argument")
+                } else {
+                    def.to_string()
+                };
+
+                if let Some((name, value)) = def.split_once("=") {
+                    defines.push((name.to_string(), Some(value.to_string())));
+                } else {
+                    defines.push((def.to_string(), None));
+                }
+            }
         }
     }
 
-    let mut includes = vec![];
+    let mut all_incs = vec![];
+    let mut cflags_includes = vec![];
+
+    let mut defines = vec![];
+
     let makefile_contents = match read_to_string(nginx_autoconf_makefile_path) {
         Ok(path) => path,
         Err(e) => {
@@ -268,28 +302,31 @@ fn parse_includes_from_makefile(nginx_autoconf_makefile_path: &PathBuf) -> Vec<P
         }
     };
 
-    let mut includes_lines = false;
-    for line in makefile_contents.lines() {
-        if !includes_lines {
-            if let Some(stripped) = line.strip_prefix("ALL_INCS") {
-                includes_lines = true;
-                if let Some(part) = extract_after_i_flag(stripped) {
-                    includes.push(part);
-                }
-                continue;
-            }
+    let lines = makefile_contents.lines();
+    let mut line: String = "".to_string();
+    for l in lines {
+        if let Some(part) = l.strip_suffix("\\") {
+            line += part;
+            continue;
         }
 
-        if includes_lines {
-            if let Some(part) = extract_after_i_flag(line) {
-                includes.push(part);
-            } else {
-                break;
-            }
+        line += l;
+
+        if let Some(tail) = line.strip_prefix("ALL_INCS") {
+            parse_line(&mut all_incs, &mut defines, tail);
+        } else if let Some(tail) = line.strip_prefix("CFLAGS") {
+            parse_line(&mut cflags_includes, &mut defines, tail);
         }
+
+        line.clear();
     }
 
-    includes.into_iter().map(PathBuf::from).collect()
+    cflags_includes.extend(all_incs);
+
+    (
+        cflags_includes.into_iter().map(PathBuf::from).collect(),
+        defines,
+    )
 }
 
 /// Collect info about the nginx configuration and expose it to the dependents via
@@ -297,6 +334,7 @@ fn parse_includes_from_makefile(nginx_autoconf_makefile_path: &PathBuf) -> Vec<P
 pub fn print_cargo_metadata<T: AsRef<Path>>(
     nginx: &NginxSource,
     includes: &[T],
+    defines: &[(String, Option<String>)],
 ) -> Result<(), Box<dyn StdError>> {
     // Unquote and merge C string constants
     let unquote_re = regex::Regex::new(r#""(.*?[^\\])"\s*"#).unwrap();
@@ -311,7 +349,7 @@ pub fn print_cargo_metadata<T: AsRef<Path>>(
     let mut ngx_features: Vec<String> = vec![];
     let mut ngx_os = String::new();
 
-    let expanded = expand_definitions(includes)?;
+    let expanded = expand_definitions(includes, defines)?;
     for line in String::from_utf8(expanded)?.lines() {
         let Some((name, value)) = line
             .trim()
@@ -350,6 +388,19 @@ pub fn print_cargo_metadata<T: AsRef<Path>>(
             .expect("Unicode include paths")
     );
 
+    println!(
+        "cargo:metadata=cflags={}",
+        defines
+            .iter()
+            .map(|(n, ov)| if let Some(v) = ov {
+                format!("-D{n}={v}")
+            } else {
+                format!("-D{n}")
+            })
+            .collect::<Vec<_>>()
+            .join(" ")
+    );
+
     // A quoted list of all recognized features to be passed to rustc-check-cfg.
     let values = NGX_CONF_FEATURES.join("\",\"");
     println!("cargo::metadata=features_check=\"{values}\"");
@@ -372,7 +423,10 @@ pub fn print_cargo_metadata<T: AsRef<Path>>(
     Ok(())
 }
 
-fn expand_definitions<T: AsRef<Path>>(includes: &[T]) -> Result<Vec<u8>, Box<dyn StdError>> {
+fn expand_definitions<T: AsRef<Path>>(
+    includes: &[T],
+    defines: &[(String, Option<String>)],
+) -> Result<Vec<u8>, Box<dyn StdError>> {
     let path = PathBuf::from(env::var("OUT_DIR")?).join("expand.c");
     let mut writer = std::io::BufWriter::new(File::create(&path)?);
 
@@ -418,8 +472,13 @@ RUST_CONF_{flag}=NGX_{flag}
     writer.flush()?;
     drop(writer);
 
-    Ok(cc::Build::new()
-        .includes(includes)
-        .file(path)
-        .try_expand()?)
+    let mut builder = cc::Build::new();
+
+    builder.includes(includes).file(path);
+
+    for def in defines {
+        builder.define(&def.0, def.1.as_deref());
+    }
+
+    Ok(builder.try_expand()?)
 }
