How to get the IP Address of a Request in F# / Giraffe

Date: 2024-05-20 | create | tech | fsharp | giraffe |

FullstackProjects.xyz is a website that generates random project ideas. It utilizes an ~anonymous voting feature to help rank these ideas and further utilizes IP addresses to naively dedupe votes from the same source.

It's built with F# / Giraffe so needed the IP address of the current request in order to identify the source of the vote. This was surprisingly hard so here I'll show how I was able to get the IP address of a request in F# / Giraffe.

F# / Giraffe HttpHandler

To get the IPAddress of a request, we need a handle to the request itself. F# / Giraffe uses HttpHandlers as the default interface for dealing with incoming requests. It passes along two parameter types - HttpFunc and HttpContext. We'll be using HttpContext to access information about the request and get access to IP information.

Example F#/Giraffe Route declaration:

POST [
    route wordCardVoteEndpointRoute (
        wordCardVoteHttpHandler 
    )
]

Example HttpHandler:

let wordCardVoteHttpHandler 
    = 
    fun (next : HttpFunc) (ctx : HttpContext) -> 
        ...

A deep dive into F# / Giraffe is beyond the scope of this post but I have a few guides if you want to learn more:

Getting IPAddress from F# / Giraffe Request

So now let's say we get a request, it hits the route we've created, and we call into the httpHandler - how do we get the IPAddress?

The function I used to get IPAddress checks 3 places recommended by the internets for retrieving IPAddresses in ASP.NET. If it finds one it returns otherwise it goes onto the next ones.

  • ServerVariable HTTP_X_FORWARDED_FOR
  • ServerVariable REMOTE_ADDR
  • Connection.RemoteIpAddress

I'll be honest - I do not have context on why these are where IPAddresses are stored nor which are better than others in different situations. All I know is this seems to be where IPAddresses are stored and generally the recommended places to look and this seems to work in my project.

My code takes in an HttpContext from the request and tries to find an IPAddress on it:

Update: Here's a more concise version of this code using option pipelines.

let getRequestIpAddress 
    (ctx: HttpContext)
    : string option 
    =
    let httpForwardedForRaw = ctx.Request.HttpContext.GetServerVariable("HTTP_X_FORWARDED_FOR")
    let httpForwardedFor =
        match httpForwardedForRaw with 
        | v when isNull v -> None 
        | v -> Some v
    
    if Option.isSome httpForwardedFor
    then httpForwardedFor
    else 

    let remoteAddressRaw = ctx.Request.HttpContext.GetServerVariable("REMOTE_ADDR")
    let remoteAddress = 
        match remoteAddressRaw with 
        | v when isNull v -> None 
        | v -> Some v

    if Option.isSome remoteAddress
    then remoteAddress
    else 

    let remoteIpRaw = ctx.Connection.RemoteIpAddress
    let remoteIp = 
        match remoteIpRaw with 
        | v when isNull v -> None 
        | v -> Some (v.ToString())

    remoteIp

In my testing, it seems like Connection.RemoteIpAddress is the only one that's ever populated but I think this is highly dependent on the way your own app is setup cause most people seem to prefer the other ones /shrug.

Next

I'm still enjoying building webapps with F# / Giraffe over SPAs like SvelteKit but it can be frustrating when simple things like this aren't easy to get working. Hopefully this makes your dev journey a little easier.

Q: How are you getting IPAddresses in your F# projects?

If you liked this you might also like:

Want more like this?

The best / easiest way to support my work is by subscribing for future updates and sharing with your network.