Setting up Rust/Actix to work with subdomains

Hey

I'll have a website which will distribute a specific service to multiple businesses. I have those businesses registered in a database-table.

database-table:

+----+-----------+---------------+-----+
| ID | subdomain | business_name | ... |
+----+-----------+---------------+-----+
|  1 | a         | Business A    |     |
|  2 | b         | Business B    |     |
|  3 | c         | Business C    |     |
+----+-----------+---------------+-----+

Each business will have a own subdomain on my site.
So the structure will be:
www.mysite.com -> My own site explaining the service
a.mysite.com -> Unique page for Business A
b.mysite.com -> Unique page for Business B
c.mysite.com -> Unique page for Business C
...

File structure:

mysite.com                 #Folder containing my own site
---- www/
-------- index.html
-------- about.html
-------- register_your_business.html
---- template/              #Folder containing the base-templates for the businesses
-------- index.html
-------- service.html

templates/index.html will be something like this:

<!DOCTYPE html>
<html>
    ...
    <body>

        <h1> Welcome to %company_name%</h1>
        <p>
            ...
        </p>
    </body>
</html>

%company_name% will be replaced by the name of the business depending on the visited subdomain. So if someone visits a.mysite.com the site will show Welcome to Business A. If b.mysite.com is visited Welcome to Business B will be displayed.

The name of the company - among other things which are required to fill in the template - will be fetched from the database-table. That database-table will have an unique column containing the name of the sub-domain.

I will register two domains:

template.mysite.com

Using CNAME's I can register the sub-domains of my clients.
CNAME's:

CNAME a.mysite.com -> template.mysite.com
CNAME b.mysite.com -> template.mysite.com
CNAME c.mysite.com -> template.mysite.com

The URL will still look like a.mysite.com but the request will be sent to template.mysite.com. The back-end on template.mysite.com will fetch the sub-domain part from my URL and because that serves as an unique identifier in my database-table I can fetch all the required data from my database-table to fill my template.

My two questions now are:

  • How can I get the sub-domain part from the visited URL in Rust/Actix? This is needed because it serves as row-identifier to get the data of the visited business from the database-table.
  • How can I set this up to test on my localhost?

Now I do this: bind("127.0.0.1:8080") and my site is only accessible on 127.0.0.1.
I can register hosts and virtual hosts using Caddy. But .bind("mysite.local") does not work.

I think I could emulate the CNAME's by doing this in Caddy:

{
    local_certs
}

mysite.test {
    root * /Users/Name/Sites/mysite.com/www
    try_files {path} /index.html
    file_server browse
}

a.mysite.test {
    root * /Users/Name/Sites/mysite.com/template
    try_files {path} /index.html
    file_server browse
}
b.mysite.test {
    root * /Users/Name/Sites/mysite.com/template
    try_files {path} /index.html
    file_server browse
}

c.mysite.test {
    root * /Users/Name/Sites/mysite.com/template
    try_files {path} /index.html
    file_server browse
}

Now I only need to know on how to bind my Rust/Actix website to a local domain instead of 127.0.0.1.

Thanks!

Actually I just need that when I visit a.mysite.test on my localhost I'll get what I now have on 127.0.0.1:8080.

The back-end on 127.0.0.1 will fill in the template correctly depending on the subdomain.

The domain will be available as the Host header.

Or the :authority pseudo-header when using HTTP/2, which might be available via a Request object (I'm not familiar with those frameworks). This one should actually have higher priority than Host.

A different option is to dispatch based on server name indication (SNI) if you are using TLS. If you are doing this, you should check that the SNI matches the authority field, to avoid domain fronting security issues.

I have set the host Header like this:

// start http server
    HttpServer::new(move || {
        App::new()
        .service(web::resource("/").route(web::get().to(index)))
        .service(web::resource("/home").route(web::get().to(index)))
        .service(web::resource("/bestel").route(web::get().to(bestel)))
        .service(web::resource("/faq").route(web::get().to(faq)))
        .service(Files::new("/assets", "./templates/assets").show_files_listing())
        .wrap(middleware::DefaultHeaders::new().header("Host", "mylocalurl.test"))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await

I still don't understand how I can visit my site now using my own url.

It seems like you are setting the header, but actually the Host header is set by the browser, not set by the server.

Yeah but I can't just alter my HTTP request in Safari each time?

Safari should automatically set the Host header correctly.

1 Like

I think we are misunderstanding each other. :stuck_out_tongue:

Safari can't resolve the IP after the domain myownurl.test because there is no DNS-system or anything to resolve what the domain name is pointing to.

I need to setup a kind of local DNS system/virtual host to point myownurl.test to 127.0.0.1:8080.

I don't know how it works on Mac, but on Linux you can edit /etc/hosts to set your own hostname->IP resolution. I assume that Mac shouldn't be much different.
You could just add records that point to 127.0.0.1 and browse to your custom domains as if they really existed.

3 Likes

Yes that's exactly what I want! But the problem is that for this project I am not using Apache but Caddy.

However, I could still switch back to Apache for a while to use the /etc/hosts file. Thanks.

/etc/hosts is not Apache-specific; it should affect all programs that use your system's name resolution APIs.

You can also use .localhost hostnames (like "foo.localhost" and "myownurl.localhost") that point to the loopback address automatically. Then you don't need to configure anything.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.