> ## Content Index
> Fetch the complete content index at: https://blog.valentinvie.fr/llms.txt
> Use this file to discover other available public pages before exploring further.

# How to expose a WebFinger web server with Unraid ?
- URL: https://blog.valentinvie.fr/how-to-expose-a-webfinger-web-server-with-unraid-2/
- Published: 2025-08-05T21:36:17.000Z
- Updated: 2026-02-25T11:08:55.000Z
- Description: WebFinger is a protocol used to discover information about people or other entities on the Internet, given an identifier like an email address or a URL. Let's see how to set it up using Unraid !
- Author: Valentin Vie
- Tags: Self-hosting, Unraid

### What is WebFinger ?

WebFinger is a protocol used to discover information about people or other entities on the Internet, given an identifier like an email address or a URL. 

For example, if you wanted to know more about an identifier like an email address `user@example.com` you can host a WebFinger endpoint to allow anyone to access more information. The endpoint would give you the name and surname of the user associated to the email address. Much more than names and surnames can be provided with WebFinger : public keys, blog addresses, openID endpoint... 

### Why do we need WebFinger ? 

Let's say you have created an awesome website which requires users to log in. You don't want each user to create an account on your website because that would mean managing a database of accounts (with associated passwords), the logic behind authentication, password resets... It would also create some resistence from users You want your users to login in a user-friendly way. Making your website compatible with an SSO protocol seems like a good option but we need more information about the user to make it all work. That is where WebFinger comes in !

The process is the following : 

- The user provides its identifier, e.g. `user@example.com` .
- Your website backend calls the WebFinger endpoint for the domain `example.com`. This endpoint is standardized and is available at : `https://example.com/.well-known/webfinger?resource=acct:user@example.com`. Basically we request all the information publicly available about the user associated with `user@example.com`.
- The WebFinger endpoint returns the following information :

```JSON
{
  "subject": "acct:john.doe@example.com",
  "aliases": [
    "https://example.com/users/john.doe"
  ],
  "properties": {
    "http://schema.org/givenName": "John",
    "http://schema.org/familyName": "Doe",
    "http://schema.org/image": "https://example.com/media/profile/john_doe.jpg"
  },
  "links": [
    {
      "rel": "http://openid.net/specs/connect/1.0/issuer",
      "href": "https://auth.example.com/"
    }
  ]
}

```

- Your website is now able to display a login page stating "*Welcome John Doe, please login*" with a personalized profile picture available at the link given by the properties : `https://example.com/media/profile/john_doe.jpg`.
- Your website is also able to redirect a user to its authentication page using its OpenID connector available at `auth.example.com`.

### Any practical use ?

Let's say I want to integrate Tailscale into my home-lab but without relying on Tailscale home-made authentication. I want to manage my own user-base and associated authentication mechanisms using my OpenID connector. 

💡

I personnally use Authentic as my authentication & OIDC server but it's not really within the scope of this blog post. Additionnally, if you really wanted to break free from Tailscale while still preserving the same point-to-point VPN capabilities I would recommend [Headscale](https://headscale.net/stable/). Headscale allows you to host your own Tailscale control server. Integrating a custom OpenID connector only delegates the authentication to your OpenID server, everything else is up to the service integrated.

[Tailscale blog](https://tailscale.com/kb/1240/sso-custom-oidc#webfinger-setup) is pretty clear on what we need to do : 

> The WebFinger endpoint must be served at `https://${domain}/.well-known/webfinger` and must include the issuer URL within the JRD in the response :

```JSON
{
  "subject": "acct:${email}",
  "links": [
    {
      "rel": "http://openid.net/specs/connect/1.0/issuer",
      "href": "${issuer URL}"
    }
  ]
}
```

### Let's host our own WebFinger endpoint using Unraid ! 

I will be using the WebFinger server implementation in Go provided by Maronato on GitHub : <https://github.com/Maronato/go-finger>. This implementation is recommended by the WebFinger official website : <https://webfinger.net/code/>. 

#### Step 1 : Clone the repository to your PC

I first created a folder `/WebFinger` on my PC to hold the entire project.

Clone the repo inside the folder : `/WebFinger/go-finger/...`

#### Step 2 : Create a Docker Image

Use the Dockerfile provided and build the image. In my case, I'm using VSCode.

![](https://blog.valentinvie.fr/content/images/2025/08/image.png)

Click on Build Image

I tagged my image as `webfinger:latest` but you can name it however you like.

#### Step 3 : Publish your Docker Image to Docker Hub

I use Docker:Desktop to push the image to DockerHub (you need an account for this).

![](https://blog.valentinvie.fr/content/images/2025/08/image-4.png)

#### Step 4 : Configure the .yaml file

Create a file called `fingers.yml`. This is required by the project documentation : <https://github.com/Maronato/go-finger/tree/v1.2.1#usage>. For my purpose :

```YAML
tailscale@example.com:
  # If the value is a URI, it'll be exposed as a webfinger link
  openid: "https://auth.example.com/"

```

#### Step 5 : Start the Docker Container on your Unraid Server

Launch the docker container on your Unraid Server : 

Click on `Add Container` at the very bottom of the Docker tab. Create the following template manually :

![](https://blog.valentinvie.fr/content/images/2025/08/image-5.png)

Within the folder `/appdata/webfinger/`, upload the .yaml file created at step 4\. Now the docker container should start and we should be able to resolve a webfinger request locally using the URL :

`http://localhost:8082/.well-known/webfinger?resource=acct:tailscale@example.com`

You should receive something like this : 

```JSON
{
"subject":"acct:tailscale@example.com",
"links":
  [{
    "rel":"http://openid.net/specs/connect/1.0/issuer",
    "href":"https://auth.example.com/"
  }]
}
```

#### Step 6 : Expose the container to the internet

I won't go into the details of exposing ports and configuring your DNS as you can find other blog post. Here is a vague list of things you should do : 

- Create a DNS record for your base domain `example.com`.
- Configure a dynamic DNS if your ISP has dynamic IPs setup.
- Push a hole though your firewall for HTTPS port 443 (hope you know what you are doing).
- Reverse proxy your docker container endpoint, in my case to port 8082\. Your reverse proxy configuration should be something like this if you use Caddy, I personnaly added a few things to improve logging, HTTPS redirection, security headers while also hosting other things on my main domain :

```Caddyfile
(logging_policy) { # Can be used with "import logging_policy karakeep" or "import logging_policy jellyfin"
    log {
        output file /logs/{args[0]}.log {
            roll_size 50MiB
            roll_keep 5
            roll_keep_for 100d
        }
        format json
        level INFO
    }
}

(security-headers) {
	header {
		# Enable HSTS
		Strict-Transport-Security "max-age=31536000; ;includeSubDomains; preload"

		# Disable clients from sniffing the media type
		X-Content-Type-Options nosniff

		# Clickjacking protection
		X-Frame-Options SAMEORIGIN
	}
}

(redir-https) {
	# Force HTTPS
	@is_http {
		protocol http
	}
	redir @is_http https://{host}{uri}
}

example.com {
	import logging_policy examplecom
	import security-headers
	import redir-https

	#If it matches it does not evaluate the second handle https://caddyserver.com/docs/caddyfile/directives/handle
	@is_webfinger path /.well-known/webfinger

    handle @is_webfinger {
        #WebFinger
        reverse_proxy localhost:8082
    }

    handle {
        #Else, redirect to subdomain or reverse proxy to another other container running on lets say 7777
        #redir https://blog.example.com{uri} permanent
        #reverse_proxy localhost:7777 
    }

}
```

You can now check it's working properly by testing it directly from the webfinger website : <https://webfinger.net>. 

![](https://blog.valentinvie.fr/content/images/2025/08/image-6.png)

Yay ! It works 😄