You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nginx反向代理Yesod时客户端真实IP无法正常转发问题

Fixing Yesod Logs Showing 127.0.0.1 Instead of Real Client IP Behind Nginx

Your Nginx configuration looks perfectly valid—you're correctly passing the real client IP via X-Real-IP and X-Forwarded-For headers. The issue is that Yesod (and its underlying WAI framework) doesn't automatically read these headers by default; it will log the IP of the direct connection source (which is Nginx's 127.0.0.1) unless you explicitly tell it to trust your reverse proxy and use the forwarded headers.

Here's how to fix this:

The cleanest way is to use the forwardedHeadersMiddleware from the wai-extra package, which handles parsing forwarded IPs (and other headers like protocol) safely and correctly.

Steps:

  • First, add wai-extra as a dependency in your project's .cabal file:
    build-depends:
        base >= 4.14 && < 5
      , yesod >= 1.6
      , wai-extra >= 3.1  -- Add this line
      -- ... other dependencies
    
  • Open your Application.hs file, import the middleware, and add it to your application stack:
    import Network.Wai.Middleware.ForwardedHeaders (forwardedHeadersMiddleware)
    
    makeApplication :: AppConfig DefaultEnv Extra -> IO Application
    makeApplication conf = do
        foundation <- makeFoundation conf
        app <- toWaiAppPlain foundation
        -- Wrap the app with the forwarded headers middleware
        return $ forwardedHeadersMiddleware app
    
  • This middleware automatically trusts requests coming from 127.0.0.1 (your Nginx server) and replaces the logged remote IP with the value from X-Forwarded-For or X-Real-IP.

2. Manually Extract Real IP in Custom Logging (Alternative)

If you prefer more control, you can modify Yesod's logging logic to directly pull the real IP from the request headers.

Example Custom Logger:

Open your Foundation.hs file and define a custom logger function:

import qualified Network.Wai as W
import Data.ByteString.Char8 (unpack)
import Data.Text (pack)

-- Custom logger that prioritizes X-Real-IP over the direct connection IP
myCustomLogger :: Yesod App => Logger -> LogSource -> LogLevel -> Text -> IO ()
myCustomLogger logger source level msg = do
    mReq <- getRequest
    let clientIp = case mReq of
            Just req -> 
                -- First check X-Real-IP, fall back to direct remote host if missing
                case lookup "X-Real-IP" (W.requestHeaders req) of
                    Just ipBs -> unpack ipBs
                    Nothing -> show (W.remoteHost req)
            Nothing -> "unknown"
    -- Prepend the real IP to the log message
    let loggedMsg = pack $ "[" ++ clientIp ++ "] " ++ unpack msg
    defaultLogger logger source level loggedMsg

Then update your Yesod instance to use this logger:

instance Yesod App where
    -- ... other Yesod settings
    logger = myCustomLogger defaultLogger

3. Verify the Fix

After making either change, restart your Yesod application. Send a test request to your site, and check your Yesod logs—they should now show the real client IP instead of 127.0.0.1.

Note for Multi-Level Proxies

If you ever have multiple reverse proxies in front of Yesod, X-Forwarded-For will contain a comma-separated list of IPs. The forwardedHeadersMiddleware automatically handles this by selecting the first non-proxy IP in the list, which is more reliable than manual header parsing.

内容的提问来源于stack exchange,提问作者bg2000 Reinstate Monica

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 14:58:12