diff --git a/CHANGELOG.md b/CHANGELOG.md
index c2d7794..6516447 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,12 @@
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [Unreleased]
+
+### New Functions 🚀
+
+- `header` – Standard implementations for reading a header on the server.
+
 ## [0.11.0] - 2024-07-27
 
 ### New Functions 🚀
@@ -31,7 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
   the DOM controlled by a value from storage. This leads to hydration errors which can be fixed by setting this new
   option to `true`.
 - `cookie::SameSite` is now re-exported
-- Changing the signal returned by `use_cookie` now tries and changes the headers during SSR. 
+- Changing the signal returned by `use_cookie` now tries and changes the headers during SSR.
 - New book chapter about codecs
 - The macro `use_derive_signal!` is now exported (thanks to @mscofield0).
 
@@ -66,7 +72,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
         - The new `UseWebSocketOptions::on_message` takes a `&T`.
         - `UseWebSocketOptions::on_error` now takes a `UseWebSocketError` instead of a `web_sys::Event`.
 - `use_storage` now always saves the default value to storage if the key doesn't exist yet.
-- Renamed `BreakpointsSematic` to `BreakpointsSemantic` and `breakpoints_sematic` to `breakpoints_semantic` 
+- Renamed `BreakpointsSematic` to `BreakpointsSemantic` and `breakpoints_sematic` to `breakpoints_semantic`
   (note the `n`) (thanks to @mondeja).
 
 ### Fixes 🍕
diff --git a/docs/book/src/SUMMARY.md b/docs/book/src/SUMMARY.md
index 502a1a6..c70b480 100644
--- a/docs/book/src/SUMMARY.md
+++ b/docs/book/src/SUMMARY.md
@@ -99,6 +99,7 @@
 
 # Utilities
 
+- [header](utilities/header.md)
 - [is_err](utilities/is_err.md)
 - [is_none](utilities/is_none.md)
 - [is_ok](utilities/is_ok.md)
diff --git a/docs/book/src/utilities/header.md b/docs/book/src/utilities/header.md
new file mode 100644
index 0000000..2bd2d1f
--- /dev/null
+++ b/docs/book/src/utilities/header.md
@@ -0,0 +1,3 @@
+# header
+
+<!-- cmdrun python3 ../extract_doc_comment.py utils/header -->
diff --git a/src/use_cookie.rs b/src/use_cookie.rs
index b7773a8..2e6c894 100644
--- a/src/use_cookie.rs
+++ b/src/use_cookie.rs
@@ -1,6 +1,7 @@
 #![allow(clippy::too_many_arguments)]
 
 use crate::core::now;
+use crate::utils::get_header;
 use codee::{CodecError, Decoder, Encoder};
 use cookie::time::{Duration, OffsetDateTime};
 pub use cookie::SameSite;
@@ -496,78 +497,7 @@ impl<T, E, D> Default for UseCookieOptions<T, E, D> {
             path: None,
             same_site: None,
             ssr_cookies_header_getter: Rc::new(move || {
-                #[cfg(feature = "ssr")]
-                {
-                    #[cfg(all(feature = "actix", feature = "axum"))]
-                    compile_error!("You can only enable one of features \"actix\" and \"axum\" at the same time");
-
-                    #[cfg(all(feature = "actix", feature = "spin"))]
-                    compile_error!("You can only enable one of features \"actix\" and \"spin\" at the same time");
-
-                    #[cfg(all(feature = "axum", feature = "spin"))]
-                    compile_error!("You can only enable one of features \"axum\" and \"spin\" at the same time");
-
-                    #[cfg(feature = "actix")]
-                    const COOKIE: http0_2::HeaderName = http0_2::header::COOKIE;
-                    #[cfg(any(feature = "axum", feature = "spin"))]
-                    const COOKIE: http1::HeaderName = http1::header::COOKIE;
-
-                    #[cfg(feature = "actix")]
-                    type HeaderValue = http0_2::HeaderValue;
-                    #[cfg(feature = "axum")]
-                    type HeaderValue = http1::HeaderValue;
-
-                    #[cfg(any(feature = "axum", feature = "actix", feature = "spin"))]
-                    let headers;
-                    #[cfg(feature = "actix")]
-                    {
-                        headers = use_context::<actix_web::HttpRequest>()
-                            .map(|req| req.headers().clone());
-                    }
-                    #[cfg(feature = "axum")]
-                    {
-                        headers = use_context::<http1::request::Parts>().map(|parts| parts.headers);
-                    }
-                    #[cfg(feature = "spin")]
-                    {
-                        headers = use_context::<leptos_spin::RequestParts>()
-                            .map(|parts| parts.headers().clone());
-                    }
-
-                    #[cfg(all(
-                        not(feature = "axum"),
-                        not(feature = "actix"),
-                        not(feature = "spin")
-                    ))]
-                    {
-                        leptos::logging::warn!("If you're using use_cookie without the feature `axum`, `actix` or `spin` enabled, you should provide the option `ssr_cookies_header_getter`");
-                        None
-                    }
-
-                    #[cfg(any(feature = "axum", feature = "actix"))]
-                    {
-                        headers.map(|headers| {
-                            headers
-                                .get(COOKIE)
-                                .cloned()
-                                .unwrap_or_else(|| HeaderValue::from_static(""))
-                                .to_str()
-                                .unwrap_or_default()
-                                .to_owned()
-                        })
-                    }
-                    #[cfg(feature = "spin")]
-                    {
-                        headers.and_then(|headers| {
-                            headers
-                                .iter()
-                                .find(|(key, _)| **key == COOKIE)
-                                .and_then(|(_, value)| String::from_utf8(value.to_vec()).ok())
-                        })
-                    }
-                }
-                #[cfg(not(feature = "ssr"))]
-                None
+                get_header!(COOKIE, use_cookie, ssr_cookies_header_getter)
             }),
             ssr_set_cookie: Rc::new(|cookie: &Cookie| {
                 #[cfg(feature = "ssr")]
diff --git a/src/utils/header.rs b/src/utils/header.rs
new file mode 100644
index 0000000..0e9d6cd
--- /dev/null
+++ b/src/utils/header.rs
@@ -0,0 +1,76 @@
+#[cfg(feature = "actix")]
+use http0_2::HeaderName;
+#[cfg(any(feature = "axum", feature = "spin"))]
+use http1::HeaderName;
+use leptos::*;
+
+/// Get the value of the header with the given name.
+///
+/// This function is only meant to be used on the server.
+/// So it is only defined when the feature `"ssr"` is enabled together with one of the
+/// features `"axum"`, `"actix"` or `"spin"`.
+///
+/// ## Example
+///
+/// ```ignore
+/// # use leptos_use::utils::header;
+/// #
+/// let content_len = header(http::header::CONTENT_LENGTH);
+/// ```
+pub fn header<N>(name: N) -> Option<String>
+where
+    N: Into<HeaderName>,
+{
+    let name = name.into();
+
+    #[cfg(all(feature = "actix", feature = "axum"))]
+    compile_error!("You can only enable one of features \"actix\" and \"axum\" at the same time");
+
+    #[cfg(all(feature = "actix", feature = "spin"))]
+    compile_error!("You can only enable one of features \"actix\" and \"spin\" at the same time");
+
+    #[cfg(all(feature = "axum", feature = "spin"))]
+    compile_error!("You can only enable one of features \"axum\" and \"spin\" at the same time");
+
+    #[cfg(feature = "actix")]
+    type HeaderValue = http0_2::HeaderValue;
+    #[cfg(feature = "axum")]
+    type HeaderValue = http1::HeaderValue;
+
+    #[cfg(any(feature = "axum", feature = "actix", feature = "spin"))]
+    let headers;
+    #[cfg(feature = "actix")]
+    {
+        headers = use_context::<actix_web::HttpRequest>().map(|req| req.headers().clone());
+    }
+    #[cfg(feature = "axum")]
+    {
+        headers = use_context::<http1::request::Parts>().map(|parts| parts.headers);
+    }
+    #[cfg(feature = "spin")]
+    {
+        headers = use_context::<leptos_spin::RequestParts>().map(|parts| parts.headers().clone());
+    }
+
+    #[cfg(any(feature = "axum", feature = "actix"))]
+    {
+        headers.map(|headers| {
+            headers
+                .get(name)
+                .cloned()
+                .unwrap_or_else(|| HeaderValue::from_static(""))
+                .to_str()
+                .unwrap_or_default()
+                .to_owned()
+        })
+    }
+    #[cfg(feature = "spin")]
+    {
+        headers.and_then(|headers| {
+            headers
+                .iter()
+                .find(|(key, _)| **key == name)
+                .and_then(|(_, value)| String::from_utf8(value.to_vec()).ok())
+        })
+    }
+}
diff --git a/src/utils/header_macro.rs b/src/utils/header_macro.rs
new file mode 100644
index 0000000..2cb0b52
--- /dev/null
+++ b/src/utils/header_macro.rs
@@ -0,0 +1,36 @@
+macro_rules! get_header {
+    (
+        $header_name:ident,
+        $function_name:ident,
+        $option_name:ident
+        $(,)?
+    ) => {
+        if cfg!(feature = "ssr") {
+            #[cfg(all(
+                not(feature = "axum"),
+                not(feature = "actix"),
+                not(feature = "spin")
+            ))]
+            {
+                leptos::logging::warn!(
+                    "If you're using `{}` with SSR but without any of the features `axum`, `actix` or `spin` enabled, you have to provide the option `{}`",
+                    stringify!($function_name),
+                    stringify!($option_name)
+                );
+                return None;
+            }
+
+            #[cfg(feature = "actix")]
+            const $header_name: http0_2::HeaderName = http0_2::header::$header_name;
+            #[cfg(any(feature = "axum", feature = "spin"))]
+            const $header_name: http1::HeaderName = http1::header::$header_name;
+
+            #[cfg(any(feature = "axum", feature = "actix", feature = "spin"))]
+            crate::utils::header($header_name)
+        } else {
+            None
+        }
+    };
+}
+
+pub(crate) use get_header;
diff --git a/src/utils/mod.rs b/src/utils/mod.rs
index 906f4e1..32d43b0 100644
--- a/src/utils/mod.rs
+++ b/src/utils/mod.rs
@@ -1,4 +1,10 @@
 mod filters;
+#[cfg(all(
+    feature = "ssr",
+    any(feature = "axum", feature = "actix", feature = "spin")
+))]
+mod header;
+mod header_macro;
 mod is;
 mod js;
 mod js_value_from_to_string;
@@ -7,6 +13,12 @@ mod signal_filtered;
 mod use_derive_signal;
 
 pub use filters::*;
+#[cfg(all(
+    feature = "ssr",
+    any(feature = "axum", feature = "actix", feature = "spin")
+))]
+pub use header::*;
+pub(crate) use header_macro::*;
 pub use is::*;
 pub(crate) use js_value_from_to_string::*;
 pub use pausable::*;