Volga
Home
Agent Skill
API Docs
GitHub
  • English
  • Русский
Home
Agent Skill
API Docs
GitHub
  • English
  • Русский
  • Home
  • Getting Started

    • Quick Start
    • Handlers
    • Route Parameters
    • Query Parameters
    • Route Groups
  • Requests & Responses

    • Headers
    • Handling JSON
    • Handling Form Data
    • Working with Files
    • Multipart Responses
    • Raw Request Body
    • Cookies
    • Validating Requests
  • Middleware & Infrastructure

    • Basic Middleware
    • Custom Middleware
    • Parameterized Middleware
    • Response Compression
    • Request Decompression
    • CORS (Cross-Origin Resource Sharing)
    • Static Files
    • Rate Limiting
    • Configuration Files
    • OpenAPI
  • Security & Access

    • Authentication and Authorization
    • OAuth 2.1 & OpenID Connect
    • OAuth 2.1 Client
    • Machine-to-Machine Grants
    • DPoP — Sender-Constrained Tokens
  • Reliability & Observability

    • Global Error Handling
    • Tracing & Logging
    • Request cancellation
    • Graceful Shutdown
  • Protocols & Realtime

    • HTTP Versions and Methods
    • HTTPS
    • WebSockets
    • Server-Sent Events (SSE)
  • Advanced Patterns

    • Dependency Injection
    • Custom Handling of HEAD, OPTIONS, and TRACE Methods
  • Agent Skill

HTTPS

Volga supports HTTPS/TLS protocols implemented on top of rustls.

If you're not using the full feature set, ensure you enable the tls feature in your Cargo.toml:

[dependencies]
volga = { version = "...", features = ["tls"] }

Simple HTTPS server

Use Development Certificates

For local development and testing purposes you may leverage automatic self-signed certificate generation. First, enable the dev-cert feature in Cargo.toml:

[dependencies]
volga = { version = "...", features = ["tls", "dev-cert"] }

Next, in main.rs you can enable development certificates using the with_dev_cert() method. There are two modes available via the DevCertMode enum:

  • DevCertMode::Ask — checks for existing certificates and asks you whether to generate them if they are missing.
  • DevCertMode::Auto — automatically generates certificates without asking.

Info

In release builds, this method is a no-op.

use volga::{App, tls::DevCertMode};

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let mut app = App::new()
        .with_tls(|tls| tls
            .with_dev_cert(DevCertMode::Ask));

    app.map_get("/hello", || async {
        "Hello, World!"
    });

    app.run().await
}

When you run your web server, it will look for the cert folder with certificate files: dev-cert.pem and dev-key.pem. If they are missing and you use DevCertMode::Ask, the server will prompt you to generate them. If you agree, the certificates will be created and automatically used.

If you want to avoid the prompt and always generate missing certificates, use DevCertMode::Auto:

use volga::{App, tls::DevCertMode};

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let mut app = App::new()
        .with_tls(|tls| tls
            .with_dev_cert(DevCertMode::Auto));

    app.map_get("/hello", || async {
        "Hello, World!"
    });

    app.run().await
}

Manually Generate Self-Signed Certificates

If you want to create self-signed certificates manually, you can use the following command:

openssl req -x509 -newkey rsa:4096 -nodes -keyout key.pem -out cert.pem -days 365 -subj '/CN=localhost'`

Adjusting code to use certificate and private key

If you generated a certificate and private key in the folder where your Cargo.toml is located, you can simply do the following:

use volga::{App, tls::TlsConfig};

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let mut app = App::new()
        .set_tls(TlsConfig::new());

    app.map_get("/hello", || async {
        "Hello, World!"
    });

    app.run().await
}

By default, TlsConfig reads these files from this folder and expects the names: cert.pem and key.pem. If you have these files in another folder you can configure the TLS like this:

let config = TlsConfig::from_pem("path/to/certs");

In the case, if you have different file names, you can handle it like this:

let config = TlsConfig::new()
    .with_cert_path("path/to/certs/server.pem")
    .with_key_path("path/to/certs/server.key");

You can test the code above by using curl command:

> curl -v "https://localhost:7878/hello"

Client Authentication

For the code above the client authentication is disabled. You may enable it as optional or required. The difference is that in the first case, it still allows anonymous requests.

Generate CA Certificate and Private Key

First, let's run the following commands to generate the CA (Client Authority) certificate and private key:

openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.pem -subj "/C=US/ST=State/L=City/O=Organization/OU=OrgUnit/CN=CA"

Optional Client Authentication

This configuration will configure trust anchor for optional authentication:

let config = TlsConfig::new()
    .with_cert_path("path/to/certs/server.pem")
    .with_key_path("path/to/certs/server.key")
    .with_optional_client_auth("path/to/certs/ca.pem");

Required Client Authentication

This configuration will configure trust anchor for required authentication:

let config = TlsConfig::new()
    .with_cert_path("tests/tls/server.pem")
    .with_key_path("tests/tls/server.key")
    .with_required_client_auth("path/to/certs/ca.pem");

Then you need to generate client certificate and private key:

openssl req -x509 -newkey rsa:4096 -nodes -keyout client.key -out client.pem -days 365 -subj '/CN=localhost'`

And then you can test it by using curl:

> curl --cert client.pem --key client.key --cacert ca.pem https://localhost:7878/hello

HTTPS Redirection

Volga also supports an HTTPS redirection, that allows you to configure a redirect from an HTTP request to HTTPS. You can configure it by leveraging with_https_redirection() method:

let config = TlsConfig::new()
    .with_cert_path("path/to/certs/server.pem")
    .with_key_path("path/to/certs/server.key")
    .with_https_redirection();

The default HTTP port is 7879 but you can change it to any other like this:

let config = TlsConfig::new()
    .with_cert_path("path/to/certs/server.pem")
    .with_key_path("path/to/certs/server.key")
    .with_https_redirection()
    .with_http_port(7979);

Now, if you run this curl command, your request will be redirected to https://localhost:7878/hello:

> curl -v "http://localhost:7979/hello"

Internally, when you run this code in debug mode it uses a Temporary Redirect (307), since link caching can cause unstable behavior in development environments. However, in release mode, it responds with 308 - Permanent Redirect.

Which host the redirect points at

The Location is built from the host the request was addressed to, with the HTTPS port substituted and the path and query kept as they were. That host is read from the request target first and from the Host header second, which is the order RFC 9112 §3.2.2 gives them — and the only order that finds anything over HTTP/2, where the host arrives as the :authority pseudo-header and never becomes a Host header at all (RFC 9113 §8.3.1).

A request with no usable host — none at all, more than one Host, or one that is not a valid authority — is answered 400, as RFC 9112 §3.2 requires. There is nowhere to send it.

The listener itself serves HTTP/1.1 and HTTP/2, so a browser — which speaks HTTP/1.1 to a plaintext port — is redirected whatever the http2 feature is set to.

HTTP Strict Transport Security Protocol (HSTS)

HTTP Strict Transport Security (HSTS) is an opt-in security enhancement that is specified by the web server through the use of a response header. When a browser that supports HSTS receives this header:

  • The browser stores configuration for the domain that prevents sending any communication over HTTP.
  • The browser forces all communication over HTTPS.
  • The browser prevents the user from using untrusted or invalid certificates.
  • The browser disables prompts that allow a user to temporarily trust such a certificate.

Because HSTS is enforced by the client, it has some limitations:

  • The client should support HSTS.
  • HSTS requires at least one successful HTTPS request to establish the HSTS policy.
  • The application must check every HTTP request and redirect or reject the HTTP request.

HSTS in Volga is enabled by default, however you may configure it by leveraging with_hsts() method:

use volga::{App, tls::TlsConfig};

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let mut app = App::new()
        .with_tls(|tls| tls
            .with_https_redirection()
            .with_hsts(|hsts| hsts.with_preload())
        );

    app.map_get("/hello", || async {
        "Hello, World!"
    });

    app.run().await
}

Then if you run this code you will receive the Strict-Transport-Security HTTP header along with the successful response.

Info

with_preload() and with_sub_domains() take no arguments — they enable the corresponding flags. Use without_preload() / without_sub_domains() to disable them. All HSTS settings are configured through the with_hsts(|h| ...) closure on TlsConfig.

Excluding hosts

Some hosts should not be told to enforce HTTPS forever — a localhost a developer reaches over plain HTTP, or an internal name whose certificate is not public. with_exclude_hosts() names them:

use volga::{App, tls::TlsConfig};

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let mut app = App::new()
        .with_tls(|tls| tls
            .with_https_redirection()
            .with_hsts(|hsts| hsts
                .with_exclude_hosts(["localhost", "internal.example.com"])));

    app.map_get("/hello", || async { "Hello, World!" });

    app.run().await
}

A host is matched by its name alone, on any port: a browser keeps an HSTS policy per host name and applies it whatever port that host is reached on, so there is no port for the list to distinguish. A port written in an entry is ignored, as are case, surrounding whitespace, a user@ prefix and a trailing dot — localhost covers LOCALHOST:8443 and localhost. alike.

The same list can come from a configuration file, as exclude_hosts under [tls.hsts_config], and is read the same way. The request's own host is taken from its target before the Host header, so the list applies over HTTP/2 as well.

You can find more examples here.

Last Updated: 9/14/26, 6:45 AM
Prev
HTTP Versions and Methods
Next
WebSockets