haskellscottyhaskell-warphaskell-wai

How to log the real IP address when behind a proxy using Scotty / wai


This is my scotty app, notice how I am logging requests to the console:

{-# LANGUAGE OverloadedStrings #-}
import Web.Scotty
import Network.Wai.Middleware.RequestLogger

import Data.Monoid (mconcat)

main = scotty 3000 $ do
    --log requests to console
    middleware logStdoutDev

    get "/:word" $ do
        beam <- param "word"
        html $ mconcat ["<h1>Scotty, ", beam, " me up!</h1>"]

My scotty app runs behind nginx using the proxy mechanism. This causes the scotty app to log like this:

127.0.0.1 - - [27/Aug/2014:15:12:00 +0000] "GET / HTTP/1.0" 200 - ...

I want the REAL IP ADDRESS to be logged.

I had the same issue in my Node.js/Express apps, where I solved it like this:

Express.js: how to get remote client address

How do I solve this problem in Scotty?


Solution

  • There's an IPAddrSource data type in wai-extra which originates in the wai-logger package. So, if you want the IP address to come from a header, it looks like you can do something like:

    {-# LANGUAGE OverloadedStrings #-}
    import Web.Scotty
    import Network.Wai.Middleware.RequestLogger
    
    import Control.Monad.IO.Class
    import Data.Monoid (mconcat)
    import Data.Default
    
    main = scotty 3000 $ do
        --log requests to console
        logger <- liftIO $ mkRequestLogger def { outputFormat = Apache FromHeader }
        middleware logger
    
        get "/:word" $ do
            beam <- param "word"
            html $ mconcat ["<h1>Scotty, ", beam, " me up!</h1>"]
    

    From the description, it also looks like Apache FromFallback will check the headers first, and use the socket IP address if no header is found.

    Update

    You can also just create the logger outside the scotty function:

    main = do
        logger <- mkRequestLogger def { outputFormat = Apache FromHeader }
        scotty 3000 $ do
            middleware logger
    
            get "/:word" $ do
                beam <- param "word"
                html $ mconcat ["<h1>Scotty, ", beam, " me up!</h1>"]