diff --git a/console/tracker-client/src/console/clients/checker/app.rs b/console/tracker-client/src/console/clients/checker/app.rs
index 395f65df9..88ce5a8ac 100644
--- a/console/tracker-client/src/console/clients/checker/app.rs
+++ b/console/tracker-client/src/console/clients/checker/app.rs
@@ -114,7 +114,7 @@ fn setup_config(args: Args) -> Result<Configuration> {
 }
 
 fn load_config_from_file(path: &PathBuf) -> Result<Configuration> {
-    let file_content = std::fs::read_to_string(path).with_context(|| format!("can't read config file {path:?}"))?;
+    let file_content = std::fs::read_to_string(path).with_context(|| format!("can't read config file {}", path.display()))?;
 
     parse_from_json(&file_content).context("invalid config format")
 }
diff --git a/packages/http-tracker-core/src/services/announce.rs b/packages/http-tracker-core/src/services/announce.rs
index 5890d35c1..959dcc615 100644
--- a/packages/http-tracker-core/src/services/announce.rs
+++ b/packages/http-tracker-core/src/services/announce.rs
@@ -13,6 +13,7 @@ use std::sync::Arc;
 
 use bittorrent_http_tracker_protocol::v1::requests::announce::{peer_from_request, Announce};
 use bittorrent_http_tracker_protocol::v1::services::peer_ip_resolver::{self, ClientIpSources, PeerIpResolutionError};
+use bittorrent_primitives::info_hash::InfoHash;
 use bittorrent_tracker_core::announce_handler::{AnnounceHandler, PeersWanted};
 use bittorrent_tracker_core::authentication::service::AuthenticationService;
 use bittorrent_tracker_core::authentication::{self, Key};
@@ -23,56 +24,6 @@ use torrust_tracker_primitives::core::AnnounceData;
 
 use crate::statistics;
 
-/// Errors related to announce requests.
-#[derive(thiserror::Error, Debug, Clone)]
-pub enum HttpAnnounceError {
-    #[error("Error resolving peer IP: {source}")]
-    PeerIpResolutionError { source: PeerIpResolutionError },
-
-    #[error("Tracker core error: {source}")]
-    TrackerCoreError { source: TrackerCoreError },
-}
-
-impl From<PeerIpResolutionError> for HttpAnnounceError {
-    fn from(peer_ip_resolution_error: PeerIpResolutionError) -> Self {
-        Self::PeerIpResolutionError {
-            source: peer_ip_resolution_error,
-        }
-    }
-}
-
-impl From<TrackerCoreError> for HttpAnnounceError {
-    fn from(tracker_core_error: TrackerCoreError) -> Self {
-        Self::TrackerCoreError {
-            source: tracker_core_error,
-        }
-    }
-}
-
-impl From<AnnounceError> for HttpAnnounceError {
-    fn from(announce_error: AnnounceError) -> Self {
-        Self::TrackerCoreError {
-            source: announce_error.into(),
-        }
-    }
-}
-
-impl From<WhitelistError> for HttpAnnounceError {
-    fn from(whitelist_error: WhitelistError) -> Self {
-        Self::TrackerCoreError {
-            source: whitelist_error.into(),
-        }
-    }
-}
-
-impl From<authentication::key::Error> for HttpAnnounceError {
-    fn from(whitelist_error: authentication::key::Error) -> Self {
-        Self::TrackerCoreError {
-            source: whitelist_error.into(),
-        }
-    }
-}
-
 /// The HTTP tracker `announce` service.
 ///
 /// The service sends an statistics event that increments:
@@ -123,50 +74,61 @@ impl AnnounceService {
         client_ip_sources: &ClientIpSources,
         maybe_key: Option<Key>,
     ) -> Result<AnnounceData, HttpAnnounceError> {
-        // Authentication
-        if self.core_config.private {
-            match maybe_key {
-                Some(key) => match self.authentication_service.authenticate(&key).await {
-                    Ok(()) => (),
-                    Err(error) => return Err(error.into()),
-                },
-                None => {
-                    return Err(authentication::key::Error::MissingAuthKey {
-                        location: Location::caller(),
-                    }
-                    .into())
-                }
-            }
-        }
-
-        // Authorization
-        match self.whitelist_authorization.authorize(&announce_request.info_hash).await {
-            Ok(()) => (),
-            Err(error) => return Err(error.into()),
-        }
+        self.authenticate(maybe_key).await?;
 
-        let peer_ip = match peer_ip_resolver::invoke(self.core_config.net.on_reverse_proxy, client_ip_sources) {
-            Ok(peer_ip) => peer_ip,
-            Err(error) => return Err(error.into()),
-        };
+        self.authorize(announce_request.info_hash).await?;
 
-        let mut peer = peer_from_request(announce_request, &peer_ip);
+        let remote_client_ip = self.resolve_remote_client_ip(client_ip_sources)?;
 
-        let peers_wanted = match announce_request.numwant {
-            Some(numwant) => PeersWanted::only(numwant),
-            None => PeersWanted::AsManyAsPossible,
-        };
+        let mut peer = peer_from_request(announce_request, &remote_client_ip);
 
-        let original_peer_ip = peer.peer_addr.ip();
+        let peers_wanted = Self::peers_wanted(announce_request);
 
-        // The tracker could change the original peer ip
         let announce_data = self
             .announce_handler
-            .announce(&announce_request.info_hash, &mut peer, &original_peer_ip, &peers_wanted)
+            .announce(&announce_request.info_hash, &mut peer, &remote_client_ip, &peers_wanted)
             .await?;
 
+        self.send_stats_event(remote_client_ip).await;
+
+        Ok(announce_data)
+    }
+
+    async fn authenticate(&self, maybe_key: Option<Key>) -> Result<(), authentication::key::Error> {
+        if self.core_config.private {
+            let key = maybe_key.ok_or(authentication::key::Error::MissingAuthKey {
+                location: Location::caller(),
+            })?;
+
+            self.authentication_service.authenticate(&key).await?;
+        }
+
+        Ok(())
+    }
+
+    async fn authorize(&self, info_hash: InfoHash) -> Result<(), WhitelistError> {
+        self.whitelist_authorization.authorize(&info_hash).await
+    }
+
+    /// Resolves the client's real IP address considering proxy headers
+    fn resolve_remote_client_ip(&self, client_ip_sources: &ClientIpSources) -> Result<IpAddr, PeerIpResolutionError> {
+        match peer_ip_resolver::invoke(self.core_config.net.on_reverse_proxy, client_ip_sources) {
+            Ok(peer_ip) => Ok(peer_ip),
+            Err(error) => Err(error),
+        }
+    }
+
+    /// Determines how many peers the client wants in the response
+    fn peers_wanted(announce_request: &Announce) -> PeersWanted {
+        match announce_request.numwant {
+            Some(numwant) => PeersWanted::only(numwant),
+            None => PeersWanted::AsManyAsPossible,
+        }
+    }
+
+    async fn send_stats_event(&self, peer_ip: IpAddr) {
         if let Some(http_stats_event_sender) = self.opt_http_stats_event_sender.as_deref() {
-            match original_peer_ip {
+            match peer_ip {
                 IpAddr::V4(_) => {
                     http_stats_event_sender
                         .send_event(statistics::event::Event::Tcp4Announce)
@@ -179,8 +141,56 @@ impl AnnounceService {
                 }
             }
         }
+    }
+}
 
-        Ok(announce_data)
+/// Errors related to announce requests.
+#[derive(thiserror::Error, Debug, Clone)]
+pub enum HttpAnnounceError {
+    #[error("Error resolving peer IP: {source}")]
+    PeerIpResolutionError { source: PeerIpResolutionError },
+
+    #[error("Tracker core error: {source}")]
+    TrackerCoreError { source: TrackerCoreError },
+}
+
+impl From<PeerIpResolutionError> for HttpAnnounceError {
+    fn from(peer_ip_resolution_error: PeerIpResolutionError) -> Self {
+        Self::PeerIpResolutionError {
+            source: peer_ip_resolution_error,
+        }
+    }
+}
+
+impl From<TrackerCoreError> for HttpAnnounceError {
+    fn from(tracker_core_error: TrackerCoreError) -> Self {
+        Self::TrackerCoreError {
+            source: tracker_core_error,
+        }
+    }
+}
+
+impl From<AnnounceError> for HttpAnnounceError {
+    fn from(announce_error: AnnounceError) -> Self {
+        Self::TrackerCoreError {
+            source: announce_error.into(),
+        }
+    }
+}
+
+impl From<WhitelistError> for HttpAnnounceError {
+    fn from(whitelist_error: WhitelistError) -> Self {
+        Self::TrackerCoreError {
+            source: whitelist_error.into(),
+        }
+    }
+}
+
+impl From<authentication::key::Error> for HttpAnnounceError {
+    fn from(whitelist_error: authentication::key::Error) -> Self {
+        Self::TrackerCoreError {
+            source: whitelist_error.into(),
+        }
     }
 }
 
diff --git a/packages/http-tracker-core/src/services/scrape.rs b/packages/http-tracker-core/src/services/scrape.rs
index 48cee7c8c..dcb88508c 100644
--- a/packages/http-tracker-core/src/services/scrape.rs
+++ b/packages/http-tracker-core/src/services/scrape.rs
@@ -12,7 +12,6 @@ use std::sync::Arc;
 
 use bittorrent_http_tracker_protocol::v1::requests::scrape::Scrape;
 use bittorrent_http_tracker_protocol::v1::services::peer_ip_resolver::{self, ClientIpSources, PeerIpResolutionError};
-use bittorrent_primitives::info_hash::InfoHash;
 use bittorrent_tracker_core::authentication::service::AuthenticationService;
 use bittorrent_tracker_core::authentication::{self, Key};
 use bittorrent_tracker_core::error::{ScrapeError, TrackerCoreError, WhitelistError};
@@ -22,55 +21,6 @@ use torrust_tracker_primitives::core::ScrapeData;
 
 use crate::statistics;
 
-/// Errors related to announce requests.
-#[derive(thiserror::Error, Debug, Clone)]
-pub enum HttpScrapeError {
-    #[error("Error resolving peer IP: {source}")]
-    PeerIpResolutionError { source: PeerIpResolutionError },
-
-    #[error("Tracker core error: {source}")]
-    TrackerCoreError { source: TrackerCoreError },
-}
-
-impl From<PeerIpResolutionError> for HttpScrapeError {
-    fn from(peer_ip_resolution_error: PeerIpResolutionError) -> Self {
-        Self::PeerIpResolutionError {
-            source: peer_ip_resolution_error,
-        }
-    }
-}
-
-impl From<TrackerCoreError> for HttpScrapeError {
-    fn from(tracker_core_error: TrackerCoreError) -> Self {
-        Self::TrackerCoreError {
-            source: tracker_core_error,
-        }
-    }
-}
-
-impl From<ScrapeError> for HttpScrapeError {
-    fn from(announce_error: ScrapeError) -> Self {
-        Self::TrackerCoreError {
-            source: announce_error.into(),
-        }
-    }
-}
-
-impl From<WhitelistError> for HttpScrapeError {
-    fn from(whitelist_error: WhitelistError) -> Self {
-        Self::TrackerCoreError {
-            source: whitelist_error.into(),
-        }
-    }
-}
-
-impl From<authentication::key::Error> for HttpScrapeError {
-    fn from(whitelist_error: authentication::key::Error) -> Self {
-        Self::TrackerCoreError {
-            source: whitelist_error.into(),
-        }
-    }
-}
 /// The HTTP tracker `scrape` service.
 ///
 /// The service sends an statistics event that increments:
@@ -110,6 +60,11 @@ impl ScrapeService {
         }
     }
 
+    /// Handles a scrape request.
+    ///
+    /// When the peer is not authenticated and the tracker is running in `private`
+    /// mode, the tracker returns empty stats for all the torrents.
+    ///
     /// # Errors
     ///
     /// This function will return an error if:
@@ -121,67 +76,93 @@ impl ScrapeService {
         client_ip_sources: &ClientIpSources,
         maybe_key: Option<Key>,
     ) -> Result<ScrapeData, HttpScrapeError> {
-        // Authentication
-        let return_fake_scrape_data = if self.core_config.private {
-            match maybe_key {
-                Some(key) => match self.authentication_service.authenticate(&key).await {
-                    Ok(()) => false,
-                    Err(_error) => true,
-                },
-                None => true,
-            }
+        let scrape_data = if self.authentication_is_required() && !self.is_authenticated(maybe_key).await {
+            ScrapeData::zeroed(&scrape_request.info_hashes)
         } else {
-            false
+            self.scrape_handler.scrape(&scrape_request.info_hashes).await?
         };
 
-        // Authorization for scrape requests is handled at the `bittorrent_tracker_core`
-        // level for each torrent.
+        let remote_client_ip = self.resolve_remote_client_ip(client_ip_sources)?;
 
-        let peer_ip = match peer_ip_resolver::invoke(self.core_config.net.on_reverse_proxy, client_ip_sources) {
-            Ok(peer_ip) => peer_ip,
-            Err(error) => return Err(error.into()),
-        };
+        self.send_stats_event(&remote_client_ip).await;
+
+        Ok(scrape_data)
+    }
+
+    fn authentication_is_required(&self) -> bool {
+        self.core_config.private
+    }
 
-        if return_fake_scrape_data {
-            return Ok(fake(&self.opt_http_stats_event_sender, &scrape_request.info_hashes, &peer_ip).await);
+    async fn is_authenticated(&self, maybe_key: Option<Key>) -> bool {
+        if let Some(key) = maybe_key {
+            return self.authentication_service.authenticate(&key).await.is_ok();
         }
 
-        let scrape_data = self.scrape_handler.scrape(&scrape_request.info_hashes).await?;
+        false
+    }
 
-        send_scrape_event(&peer_ip, &self.opt_http_stats_event_sender).await;
+    /// Resolves the client's real IP address considering proxy headers.
+    fn resolve_remote_client_ip(&self, client_ip_sources: &ClientIpSources) -> Result<IpAddr, PeerIpResolutionError> {
+        peer_ip_resolver::invoke(self.core_config.net.on_reverse_proxy, client_ip_sources)
+    }
 
-        Ok(scrape_data)
+    async fn send_stats_event(&self, original_peer_ip: &IpAddr) {
+        if let Some(http_stats_event_sender) = self.opt_http_stats_event_sender.as_deref() {
+            let event = match original_peer_ip {
+                IpAddr::V4(_) => statistics::event::Event::Tcp4Scrape,
+                IpAddr::V6(_) => statistics::event::Event::Tcp6Scrape,
+            };
+            http_stats_event_sender.send_event(event).await;
+        }
     }
 }
 
-/// The HTTP tracker fake `scrape` service. It returns zeroed stats.
-///
-/// When the peer is not authenticated and the tracker is running in `private` mode,
-/// the tracker returns empty stats for all the torrents.
-///
-/// > **NOTICE**: tracker statistics are not updated in this case.
-pub async fn fake(
-    opt_http_stats_event_sender: &Arc<Option<Box<dyn statistics::event::sender::Sender>>>,
-    info_hashes: &Vec<InfoHash>,
-    original_peer_ip: &IpAddr,
-) -> ScrapeData {
-    send_scrape_event(original_peer_ip, opt_http_stats_event_sender).await;
-
-    ScrapeData::zeroed(info_hashes)
+/// Errors related to announce requests.
+#[derive(thiserror::Error, Debug, Clone)]
+pub enum HttpScrapeError {
+    #[error("Error resolving peer IP: {source}")]
+    PeerIpResolutionError { source: PeerIpResolutionError },
+
+    #[error("Tracker core error: {source}")]
+    TrackerCoreError { source: TrackerCoreError },
 }
 
-async fn send_scrape_event(
-    original_peer_ip: &IpAddr,
-    opt_http_stats_event_sender: &Arc<Option<Box<dyn statistics::event::sender::Sender>>>,
-) {
-    if let Some(http_stats_event_sender) = opt_http_stats_event_sender.as_deref() {
-        match original_peer_ip {
-            IpAddr::V4(_) => {
-                http_stats_event_sender.send_event(statistics::event::Event::Tcp4Scrape).await;
-            }
-            IpAddr::V6(_) => {
-                http_stats_event_sender.send_event(statistics::event::Event::Tcp6Scrape).await;
-            }
+impl From<PeerIpResolutionError> for HttpScrapeError {
+    fn from(peer_ip_resolution_error: PeerIpResolutionError) -> Self {
+        Self::PeerIpResolutionError {
+            source: peer_ip_resolution_error,
+        }
+    }
+}
+
+impl From<TrackerCoreError> for HttpScrapeError {
+    fn from(tracker_core_error: TrackerCoreError) -> Self {
+        Self::TrackerCoreError {
+            source: tracker_core_error,
+        }
+    }
+}
+
+impl From<ScrapeError> for HttpScrapeError {
+    fn from(announce_error: ScrapeError) -> Self {
+        Self::TrackerCoreError {
+            source: announce_error.into(),
+        }
+    }
+}
+
+impl From<WhitelistError> for HttpScrapeError {
+    fn from(whitelist_error: WhitelistError) -> Self {
+        Self::TrackerCoreError {
+            source: whitelist_error.into(),
+        }
+    }
+}
+
+impl From<authentication::key::Error> for HttpScrapeError {
+    fn from(whitelist_error: authentication::key::Error) -> Self {
+        Self::TrackerCoreError {
+            source: whitelist_error.into(),
         }
     }
 }
@@ -208,7 +189,6 @@ mod tests {
     use tokio::sync::mpsc::error::SendError;
     use torrust_tracker_configuration::Configuration;
     use torrust_tracker_primitives::{peer, DurationSinceUnixEpoch};
-    use torrust_tracker_test_helpers::configuration;
 
     use crate::statistics;
     use crate::tests::sample_info_hash;
@@ -219,10 +199,6 @@ mod tests {
         authentication_service: Arc<AuthenticationService>,
     }
 
-    fn initialize_services_for_public_tracker() -> Container {
-        initialize_services_with_configuration(&configuration::ephemeral_public())
-    }
-
     fn initialize_services_with_configuration(config: &Configuration) -> Container {
         let database = initialize_database(&config.core);
         let in_memory_whitelist = Arc::new(InMemoryWhitelist::default());
@@ -433,28 +409,34 @@ mod tests {
         use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
         use std::sync::Arc;
 
+        use bittorrent_http_tracker_protocol::v1::requests::scrape::Scrape;
+        use bittorrent_http_tracker_protocol::v1::services::peer_ip_resolver::ClientIpSources;
         use bittorrent_tracker_core::announce_handler::PeersWanted;
         use mockall::predicate::eq;
         use torrust_tracker_primitives::core::ScrapeData;
+        use torrust_tracker_test_helpers::configuration;
 
-        use crate::services::scrape::fake;
         use crate::services::scrape::tests::{
-            initialize_services_for_public_tracker, sample_info_hashes, sample_peer, MockHttpStatsEventSender,
+            initialize_services_with_configuration, sample_info_hashes, sample_peer, MockHttpStatsEventSender,
         };
+        use crate::services::scrape::ScrapeService;
         use crate::statistics;
         use crate::tests::sample_info_hash;
 
         #[tokio::test]
-        async fn it_should_always_return_the_zeroed_scrape_data_for_a_torrent() {
+        async fn it_should_return_the_zeroed_scrape_data_when_the_tracker_is_running_in_private_mode_and_the_peer_is_not_authenticated(
+        ) {
+            let config = configuration::ephemeral_private();
+
+            let container = initialize_services_with_configuration(&config);
+
             let (http_stats_event_sender, _http_stats_repository) = statistics::setup::factory(false);
             let http_stats_event_sender = Arc::new(http_stats_event_sender);
 
-            let container = initialize_services_for_public_tracker();
-
             let info_hash = sample_info_hash();
             let info_hashes = vec![info_hash];
 
-            // Announce a new peer to force scrape data to contain not zeroed data
+            // Announce a new peer to force scrape data to contain non zeroed data
             let mut peer = sample_peer();
             let original_peer_ip = peer.ip();
             container
@@ -463,7 +445,26 @@ mod tests {
                 .await
                 .unwrap();
 
-            let scrape_data = fake(&http_stats_event_sender, &info_hashes, &original_peer_ip).await;
+            let scrape_request = Scrape {
+                info_hashes: sample_info_hashes(),
+            };
+
+            let client_ip_sources = ClientIpSources {
+                right_most_x_forwarded_for: None,
+                connection_info_ip: Some(original_peer_ip),
+            };
+
+            let scrape_service = Arc::new(ScrapeService::new(
+                Arc::new(config.core),
+                container.scrape_handler.clone(),
+                container.authentication_service.clone(),
+                http_stats_event_sender.clone(),
+            ));
+
+            let scrape_data = scrape_service
+                .handle_scrape(&scrape_request, &client_ip_sources, None)
+                .await
+                .unwrap();
 
             let expected_scrape_data = ScrapeData::zeroed(&info_hashes);
 
@@ -472,6 +473,10 @@ mod tests {
 
         #[tokio::test]
         async fn it_should_send_the_tcp_4_scrape_event_when_the_peer_uses_ipv4() {
+            let config = configuration::ephemeral();
+
+            let container = initialize_services_with_configuration(&config);
+
             let mut http_stats_event_sender_mock = MockHttpStatsEventSender::new();
             http_stats_event_sender_mock
                 .expect_send_event()
@@ -483,11 +488,34 @@ mod tests {
 
             let peer_ip = IpAddr::V4(Ipv4Addr::new(126, 0, 0, 1));
 
-            fake(&http_stats_event_sender, &sample_info_hashes(), &peer_ip).await;
+            let scrape_request = Scrape {
+                info_hashes: sample_info_hashes(),
+            };
+
+            let client_ip_sources = ClientIpSources {
+                right_most_x_forwarded_for: None,
+                connection_info_ip: Some(peer_ip),
+            };
+
+            let scrape_service = Arc::new(ScrapeService::new(
+                Arc::new(config.core),
+                container.scrape_handler.clone(),
+                container.authentication_service.clone(),
+                http_stats_event_sender.clone(),
+            ));
+
+            scrape_service
+                .handle_scrape(&scrape_request, &client_ip_sources, None)
+                .await
+                .unwrap();
         }
 
         #[tokio::test]
         async fn it_should_send_the_tcp_6_scrape_event_when_the_peer_uses_ipv6() {
+            let config = configuration::ephemeral();
+
+            let container = initialize_services_with_configuration(&config);
+
             let mut http_stats_event_sender_mock = MockHttpStatsEventSender::new();
             http_stats_event_sender_mock
                 .expect_send_event()
@@ -499,7 +527,26 @@ mod tests {
 
             let peer_ip = IpAddr::V6(Ipv6Addr::new(0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969));
 
-            fake(&http_stats_event_sender, &sample_info_hashes(), &peer_ip).await;
+            let scrape_request = Scrape {
+                info_hashes: sample_info_hashes(),
+            };
+
+            let client_ip_sources = ClientIpSources {
+                right_most_x_forwarded_for: None,
+                connection_info_ip: Some(peer_ip),
+            };
+
+            let scrape_service = Arc::new(ScrapeService::new(
+                Arc::new(config.core),
+                container.scrape_handler.clone(),
+                container.authentication_service.clone(),
+                http_stats_event_sender.clone(),
+            ));
+
+            scrape_service
+                .handle_scrape(&scrape_request, &client_ip_sources, None)
+                .await
+                .unwrap();
         }
     }
 }
diff --git a/packages/udp-tracker-core/src/services/announce.rs b/packages/udp-tracker-core/src/services/announce.rs
index 051944d7e..698f5fba6 100644
--- a/packages/udp-tracker-core/src/services/announce.rs
+++ b/packages/udp-tracker-core/src/services/announce.rs
@@ -12,6 +12,7 @@ use std::ops::Range;
 use std::sync::Arc;
 
 use aquatic_udp_protocol::AnnounceRequest;
+use bittorrent_primitives::info_hash::InfoHash;
 use bittorrent_tracker_core::announce_handler::{AnnounceHandler, PeersWanted};
 use bittorrent_tracker_core::error::{AnnounceError, WhitelistError};
 use bittorrent_tracker_core::whitelist;
@@ -21,47 +22,15 @@ use torrust_tracker_primitives::core::AnnounceData;
 use crate::connection_cookie::{check, gen_remote_fingerprint, ConnectionCookieError};
 use crate::statistics;
 
-/// Errors related to announce requests.
-#[derive(thiserror::Error, Debug, Clone)]
-pub enum UdpAnnounceError {
-    /// Error returned when there was an error with the connection cookie.
-    #[error("Connection cookie error: {source}")]
-    ConnectionCookieError { source: ConnectionCookieError },
-
-    /// Error returned when there was an error with the tracker core announce handler.
-    #[error("Tracker core announce error: {source}")]
-    TrackerCoreAnnounceError { source: AnnounceError },
-
-    /// Error returned when there was an error with the tracker core whitelist.
-    #[error("Tracker core whitelist error: {source}")]
-    TrackerCoreWhitelistError { source: WhitelistError },
-}
-
-impl From<ConnectionCookieError> for UdpAnnounceError {
-    fn from(connection_cookie_error: ConnectionCookieError) -> Self {
-        Self::ConnectionCookieError {
-            source: connection_cookie_error,
-        }
-    }
-}
-
-impl From<AnnounceError> for UdpAnnounceError {
-    fn from(announce_error: AnnounceError) -> Self {
-        Self::TrackerCoreAnnounceError { source: announce_error }
-    }
-}
-
-impl From<WhitelistError> for UdpAnnounceError {
-    fn from(whitelist_error: WhitelistError) -> Self {
-        Self::TrackerCoreWhitelistError { source: whitelist_error }
-    }
-}
-
 /// The `AnnounceService` is responsible for handling the `announce` requests.
+///
+/// The service sends an statistics event that increments:
+///
+/// - The number of UDP `announce` requests handled by the UDP tracker.
 pub struct AnnounceService {
-    pub announce_handler: Arc<AnnounceHandler>,
-    pub whitelist_authorization: Arc<whitelist::authorization::WhitelistAuthorization>,
-    pub opt_udp_core_stats_event_sender: Arc<Option<Box<dyn statistics::event::sender::Sender>>>,
+    announce_handler: Arc<AnnounceHandler>,
+    whitelist_authorization: Arc<whitelist::authorization::WhitelistAuthorization>,
+    opt_udp_core_stats_event_sender: Arc<Option<Box<dyn statistics::event::sender::Sender>>>,
 }
 
 impl AnnounceService {
@@ -86,52 +55,94 @@ impl AnnounceService {
     ///
     /// - The tracker is running in listed mode and the torrent is not in the
     ///   whitelist.
-    #[allow(clippy::too_many_arguments)]
     pub async fn handle_announce(
         &self,
         remote_addr: SocketAddr,
         request: &AnnounceRequest,
         cookie_valid_range: Range<f64>,
     ) -> Result<AnnounceData, UdpAnnounceError> {
-        // Authentication
-        check(
-            &request.connection_id,
-            gen_remote_fingerprint(&remote_addr),
-            cookie_valid_range,
-        )?;
+        Self::authenticate(remote_addr, request, cookie_valid_range)?;
 
         let info_hash = request.info_hash.into();
-        let remote_client_ip = remote_addr.ip();
 
-        // Authorization
-        self.whitelist_authorization.authorize(&info_hash).await?;
+        self.authorize(&info_hash).await?;
+
+        let remote_client_ip = remote_addr.ip();
 
         let mut peer = peer_builder::from_request(request, &remote_client_ip);
-        let peers_wanted: PeersWanted = i32::from(request.peers_wanted.0).into();
 
-        let original_peer_ip = peer.peer_addr.ip();
+        let peers_wanted: PeersWanted = i32::from(request.peers_wanted.0).into();
 
-        // The tracker could change the original peer ip
         let announce_data = self
             .announce_handler
-            .announce(&info_hash, &mut peer, &original_peer_ip, &peers_wanted)
+            .announce(&info_hash, &mut peer, &remote_client_ip, &peers_wanted)
             .await?;
 
+        self.send_stats_event(remote_client_ip).await;
+
+        Ok(announce_data)
+    }
+
+    fn authenticate(
+        remote_addr: SocketAddr,
+        request: &AnnounceRequest,
+        cookie_valid_range: Range<f64>,
+    ) -> Result<f64, ConnectionCookieError> {
+        check(
+            &request.connection_id,
+            gen_remote_fingerprint(&remote_addr),
+            cookie_valid_range,
+        )
+    }
+
+    async fn authorize(&self, info_hash: &InfoHash) -> Result<(), WhitelistError> {
+        self.whitelist_authorization.authorize(info_hash).await
+    }
+
+    async fn send_stats_event(&self, peer_ip: IpAddr) {
         if let Some(udp_stats_event_sender) = self.opt_udp_core_stats_event_sender.as_deref() {
-            match original_peer_ip {
-                IpAddr::V4(_) => {
-                    udp_stats_event_sender
-                        .send_event(statistics::event::Event::Udp4Announce)
-                        .await;
-                }
-                IpAddr::V6(_) => {
-                    udp_stats_event_sender
-                        .send_event(statistics::event::Event::Udp6Announce)
-                        .await;
-                }
-            }
+            let event = match peer_ip {
+                IpAddr::V4(_) => statistics::event::Event::Udp4Announce,
+                IpAddr::V6(_) => statistics::event::Event::Udp6Announce,
+            };
+
+            udp_stats_event_sender.send_event(event).await;
         }
+    }
+}
 
-        Ok(announce_data)
+/// Errors related to announce requests.
+#[derive(thiserror::Error, Debug, Clone)]
+pub enum UdpAnnounceError {
+    /// Error returned when there was an error with the connection cookie.
+    #[error("Connection cookie error: {source}")]
+    ConnectionCookieError { source: ConnectionCookieError },
+
+    /// Error returned when there was an error with the tracker core announce handler.
+    #[error("Tracker core announce error: {source}")]
+    TrackerCoreAnnounceError { source: AnnounceError },
+
+    /// Error returned when there was an error with the tracker core whitelist.
+    #[error("Tracker core whitelist error: {source}")]
+    TrackerCoreWhitelistError { source: WhitelistError },
+}
+
+impl From<ConnectionCookieError> for UdpAnnounceError {
+    fn from(connection_cookie_error: ConnectionCookieError) -> Self {
+        Self::ConnectionCookieError {
+            source: connection_cookie_error,
+        }
+    }
+}
+
+impl From<AnnounceError> for UdpAnnounceError {
+    fn from(announce_error: AnnounceError) -> Self {
+        Self::TrackerCoreAnnounceError { source: announce_error }
+    }
+}
+
+impl From<WhitelistError> for UdpAnnounceError {
+    fn from(whitelist_error: WhitelistError) -> Self {
+        Self::TrackerCoreWhitelistError { source: whitelist_error }
     }
 }
diff --git a/packages/udp-tracker-core/src/services/scrape.rs b/packages/udp-tracker-core/src/services/scrape.rs
index fddc2ec2d..61301cd43 100644
--- a/packages/udp-tracker-core/src/services/scrape.rs
+++ b/packages/udp-tracker-core/src/services/scrape.rs
@@ -20,50 +20,17 @@ use torrust_tracker_primitives::core::ScrapeData;
 use crate::connection_cookie::{check, gen_remote_fingerprint, ConnectionCookieError};
 use crate::statistics;
 
-/// Errors related to scrape requests.
-#[derive(thiserror::Error, Debug, Clone)]
-pub enum UdpScrapeError {
-    /// Error returned when there was an error with the connection cookie.
-    #[error("Connection cookie error: {source}")]
-    ConnectionCookieError { source: ConnectionCookieError },
-
-    /// Error returned when there was an error with the tracker core scrape handler.
-    #[error("Tracker core scrape error: {source}")]
-    TrackerCoreScrapeError { source: ScrapeError },
-
-    /// Error returned when there was an error with the tracker core whitelist.
-    #[error("Tracker core whitelist error: {source}")]
-    TrackerCoreWhitelistError { source: WhitelistError },
-}
-
-impl From<ConnectionCookieError> for UdpScrapeError {
-    fn from(connection_cookie_error: ConnectionCookieError) -> Self {
-        Self::ConnectionCookieError {
-            source: connection_cookie_error,
-        }
-    }
-}
-
-impl From<ScrapeError> for UdpScrapeError {
-    fn from(scrape_error: ScrapeError) -> Self {
-        Self::TrackerCoreScrapeError { source: scrape_error }
-    }
-}
-
-impl From<WhitelistError> for UdpScrapeError {
-    fn from(whitelist_error: WhitelistError) -> Self {
-        Self::TrackerCoreWhitelistError { source: whitelist_error }
-    }
-}
-
 /// The `ScrapeService` is responsible for handling the `scrape` requests.
+///
+/// The service sends an statistics event that increments:
+///
+/// - The number of UDP `scrape` requests handled by the UDP tracker.
 pub struct ScrapeService {
     scrape_handler: Arc<ScrapeHandler>,
     opt_udp_stats_event_sender: Arc<Option<Box<dyn statistics::event::sender::Sender>>>,
 }
 
 impl ScrapeService {
-    /// Creates a new `ScrapeService`.
     #[must_use]
     pub fn new(
         scrape_handler: Arc<ScrapeHandler>,
@@ -82,32 +49,81 @@ impl ScrapeService {
     /// It will return an error if the tracker core scrape handler returns an error.
     pub async fn handle_scrape(
         &self,
-        remote_addr: SocketAddr,
+        remote_client_addr: SocketAddr,
         request: &ScrapeRequest,
         cookie_valid_range: Range<f64>,
     ) -> Result<ScrapeData, UdpScrapeError> {
+        Self::authenticate(remote_client_addr, request, cookie_valid_range)?;
+
+        let scrape_data = self
+            .scrape_handler
+            .scrape(&Self::convert_from_aquatic(&request.info_hashes))
+            .await?;
+
+        self.send_stats_event(remote_client_addr).await;
+
+        Ok(scrape_data)
+    }
+
+    fn authenticate(
+        remote_addr: SocketAddr,
+        request: &ScrapeRequest,
+        cookie_valid_range: Range<f64>,
+    ) -> Result<f64, ConnectionCookieError> {
         check(
             &request.connection_id,
             gen_remote_fingerprint(&remote_addr),
             cookie_valid_range,
-        )?;
-
-        // Convert from aquatic infohashes
-        let info_hashes: Vec<InfoHash> = request.info_hashes.iter().map(|&x| x.into()).collect();
+        )
+    }
 
-        let scrape_data = self.scrape_handler.scrape(&info_hashes).await?;
+    fn convert_from_aquatic(aquatic_infohashes: &[aquatic_udp_protocol::common::InfoHash]) -> Vec<InfoHash> {
+        aquatic_infohashes.iter().map(|&x| x.into()).collect()
+    }
 
+    async fn send_stats_event(&self, remote_addr: SocketAddr) {
         if let Some(udp_stats_event_sender) = self.opt_udp_stats_event_sender.as_deref() {
-            match remote_addr {
-                SocketAddr::V4(_) => {
-                    udp_stats_event_sender.send_event(statistics::event::Event::Udp4Scrape).await;
-                }
-                SocketAddr::V6(_) => {
-                    udp_stats_event_sender.send_event(statistics::event::Event::Udp6Scrape).await;
-                }
-            }
+            let event = match remote_addr {
+                SocketAddr::V4(_) => statistics::event::Event::Udp4Scrape,
+                SocketAddr::V6(_) => statistics::event::Event::Udp6Scrape,
+            };
+            udp_stats_event_sender.send_event(event).await;
         }
+    }
+}
 
-        Ok(scrape_data)
+/// Errors related to scrape requests.
+#[derive(thiserror::Error, Debug, Clone)]
+pub enum UdpScrapeError {
+    /// Error returned when there was an error with the connection cookie.
+    #[error("Connection cookie error: {source}")]
+    ConnectionCookieError { source: ConnectionCookieError },
+
+    /// Error returned when there was an error with the tracker core scrape handler.
+    #[error("Tracker core scrape error: {source}")]
+    TrackerCoreScrapeError { source: ScrapeError },
+
+    /// Error returned when there was an error with the tracker core whitelist.
+    #[error("Tracker core whitelist error: {source}")]
+    TrackerCoreWhitelistError { source: WhitelistError },
+}
+
+impl From<ConnectionCookieError> for UdpScrapeError {
+    fn from(connection_cookie_error: ConnectionCookieError) -> Self {
+        Self::ConnectionCookieError {
+            source: connection_cookie_error,
+        }
+    }
+}
+
+impl From<ScrapeError> for UdpScrapeError {
+    fn from(scrape_error: ScrapeError) -> Self {
+        Self::TrackerCoreScrapeError { source: scrape_error }
+    }
+}
+
+impl From<WhitelistError> for UdpScrapeError {
+    fn from(whitelist_error: WhitelistError) -> Self {
+        Self::TrackerCoreWhitelistError { source: whitelist_error }
     }
 }