diff --git a/Cargo.lock b/Cargo.lock
index e41e40161fde..dcdfb2747c6e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3320,6 +3320,7 @@ dependencies = [
  "tonic",
  "tonic-build",
  "tonic-health",
+ "tonic-reflection",
  "tonic-web-wasm-client",
  "tower",
  "tracing",
@@ -3444,6 +3445,7 @@ dependencies = [
  "toml 0.8.10",
  "tonic",
  "tonic-health",
+ "tonic-reflection",
  "tonic-web",
  "tower",
  "tower-http 0.5.2",
@@ -6130,6 +6132,19 @@ dependencies = [
  "tonic",
 ]
 
+[[package]]
+name = "tonic-reflection"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "548c227bd5c0fae5925812c4ec6c66ffcfced23ea370cb823f4d18f0fc1cb6a7"
+dependencies = [
+ "prost",
+ "prost-types",
+ "tokio",
+ "tokio-stream",
+ "tonic",
+]
+
 [[package]]
 name = "tonic-web"
 version = "0.11.0"
diff --git a/Cargo.toml b/Cargo.toml
index f36cd997b855..4f59d5328ae6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -114,6 +114,7 @@ thiserror = "1.0.57"
 tonic = { version = "0.11", default-features = false }
 tonic-build = { version = "0.11", default-features = false }
 tonic-health = "0.11"
+tonic-reflection = "0.11"
 tonic-web = "0.11"
 tonic-web-wasm-client = "0.5.1"
 tokio = "1.36.0"
diff --git a/flake.nix b/flake.nix
index 484ca9e1d283..24a62f65e479 100644
--- a/flake.nix
+++ b/flake.nix
@@ -35,6 +35,7 @@
           kubectl
 
           # for Wasm testing
+          chromium
           chromedriver
           wasm-pack
         ];
diff --git a/linera-rpc/Cargo.toml b/linera-rpc/Cargo.toml
index ab9a47ae007a..019dbf9567c9 100644
--- a/linera-rpc/Cargo.toml
+++ b/linera-rpc/Cargo.toml
@@ -29,7 +29,7 @@ metrics = [
     "linera-views/metrics",
 ]
 
-server = ["tonic-health"]
+server = ["tonic-health", "tonic-reflection"]
 simple-network = ["tokio-util/net"]
 
 web = [
@@ -65,6 +65,7 @@ thiserror.workspace = true
 tokio.workspace = true
 tokio-util = { workspace = true, optional = true, features = ["codec"] }
 tonic-health = { workspace = true, optional = true }
+tonic-reflection = { workspace = true, optional = true }
 tower.workspace = true
 tracing.workspace = true
 
diff --git a/linera-rpc/build.rs b/linera-rpc/build.rs
index 3205cd37c4f4..68126283d006 100644
--- a/linera-rpc/build.rs
+++ b/linera-rpc/build.rs
@@ -2,8 +2,11 @@
 // SPDX-License-Identifier: Apache-2.0
 
 fn main() -> Result<(), Box<dyn std::error::Error>> {
+    let out_dir: std::path::PathBuf = std::env::var("OUT_DIR")?.into();
+
     let no_includes: &[&str] = &[];
     tonic_build::configure()
+        .file_descriptor_set_path(out_dir.join("file_descriptor_set.bin"))
         .protoc_arg("--experimental_allow_proto3_optional")
         .compile(&["proto/rpc.proto"], no_includes)?;
 
diff --git a/linera-rpc/src/grpc/mod.rs b/linera-rpc/src/grpc/mod.rs
index 9f231e3fedb2..6b7a3bcaf5de 100644
--- a/linera-rpc/src/grpc/mod.rs
+++ b/linera-rpc/src/grpc/mod.rs
@@ -37,6 +37,10 @@ pub enum GrpcError {
 
     #[error(transparent)]
     InvalidUri(#[from] tonic::codegen::http::uri::InvalidUri),
+
+    #[cfg(with_server)]
+    #[error(transparent)]
+    Reflection(#[from] tonic_reflection::server::Error),
 }
 
 const MEBIBYTE: usize = 1024 * 1024;
diff --git a/linera-rpc/src/grpc/server.rs b/linera-rpc/src/grpc/server.rs
index 8d78e606aa52..0f91e329c337 100644
--- a/linera-rpc/src/grpc/server.rs
+++ b/linera-rpc/src/grpc/server.rs
@@ -252,6 +252,10 @@ where
             .max_encoding_message_size(GRPC_MAX_MESSAGE_SIZE)
             .max_decoding_message_size(GRPC_MAX_MESSAGE_SIZE);
 
+        let reflection_service = tonic_reflection::server::Builder::configure()
+            .register_encoded_file_descriptor_set(crate::FILE_DESCRIPTOR_SET)
+            .build()?;
+
         let handle = tokio::spawn(
             tonic::transport::Server::builder()
                 .layer(
@@ -260,6 +264,7 @@ where
                         .into_inner(),
                 )
                 .add_service(health_service)
+                .add_service(reflection_service)
                 .add_service(worker_node)
                 .serve_with_shutdown(server_address, receiver.map(|_| ())),
         );
diff --git a/linera-rpc/src/lib.rs b/linera-rpc/src/lib.rs
index 61bd3f441a70..69a338d54f16 100644
--- a/linera-rpc/src/lib.rs
+++ b/linera-rpc/src/lib.rs
@@ -37,3 +37,5 @@ pub struct HandleCertificateRequest {
     pub wait_for_outgoing_messages: bool,
     pub blobs: Vec<linera_chain::data_types::HashedValue>,
 }
+
+pub const FILE_DESCRIPTOR_SET: &[u8] = tonic::include_file_descriptor_set!("file_descriptor_set");
diff --git a/linera-service/Cargo.toml b/linera-service/Cargo.toml
index 02ef6095b498..a2e7a97baeff 100644
--- a/linera-service/Cargo.toml
+++ b/linera-service/Cargo.toml
@@ -79,6 +79,7 @@ tokio-stream.workspace = true
 toml.workspace = true
 tonic = { workspace = true, features = ["transport", "tls", "tls-roots"] }
 tonic-health.workspace = true
+tonic-reflection.workspace = true
 tonic-web.workspace = true
 tower.workspace = true
 tower-http = { workspace = true, features = ["cors"] }
diff --git a/linera-service/src/grpc_proxy.rs b/linera-service/src/grpc_proxy.rs
index 6076180c85e7..40c37ad3c7f5 100644
--- a/linera-service/src/grpc_proxy.rs
+++ b/linera-service/src/grpc_proxy.rs
@@ -214,6 +214,9 @@ impl GrpcProxy {
         let internal_server = Server::builder()
             .add_service(tonic_web::enable(self.as_notifier_service()))
             .serve(self.internal_address());
+        let reflection_service = tonic_reflection::server::Builder::configure()
+            .register_encoded_file_descriptor_set(linera_rpc::FILE_DESCRIPTOR_SET)
+            .build()?;
         let public_server = self
             .public_server()?
             .layer(
@@ -221,7 +224,9 @@ impl GrpcProxy {
                     .layer(PrometheusMetricsMiddlewareLayer)
                     .into_inner(),
             )
+            .accept_http1(true)
             .add_service(health_service)
+            .add_service(reflection_service)
             .add_service(self.as_validator_node())
             .serve(self.public_address());