• Login
  • Register
  • Login Register
    Login
    Username/Email:
    Password:
    Or login with a social network below
  • Forum
  • Website
  • GitHub
  • Status
  • Translation
  • Features
  • Team
  • Rules
  • Help
  • Feeds
User Links
  • Login
  • Register
  • Login Register
    Login
    Username/Email:
    Password:
    Or login with a social network below

    Useful Links Forum Website GitHub Status Translation Features Team Rules Help Feeds
    Jellyfin Forum Support Guides, Walkthroughs & Tutorials HAproxy on PFsense (websocket working) Updated Dec24

     
    • 0 Vote(s) - 0 Average

    HAproxy on PFsense (websocket working) Updated Dec24

    Haproxy GUI config on Pfsense with screenshots and examples
    gaming09
    Offline

    Member

    Posts: 67
    Threads: 24
    Joined: 2023 Jun
    Reputation: 1
    Country:United States
    #1
    2024-12-01, 07:21 PM (This post was last modified: 2024-12-02, 03:00 AM by gaming09. Edited 4 times in total.)
    **This is an update to my original step by step, which is no longer valid on the last few versions of haproxy/pfsense
    ****Basic config and setup for HAproxy on Pfsense I suggest to follow Tom Lawrence's YT video here. The base config from that video will need to be in place before adding/editing my step by step

    Overview Text Only:
    Code:
    **Summary of HAProxy Configuration for WebSocket Support on pfSense (Obfuscated Version)**

    Below is a detailed breakdown of the HAProxy configuration to get WebSocket connections working for your specific services like Jellyfin and Emby. This setup includes ACLs, actions, advanced settings, backend timeout settings, and backend passthrough settings.

    ### ACL Configuration (Access Control Lists)

    To support WebSocket upgrades for the domains used, create the following ACLs:

    1. **ACL Name: hdr\_connection\_upgrade**

      - **Expression**: Custom ACL
      - **Value**: `hdr(Connection) -i upgrade`

    2. **ACL Name: hdr\_upgrade\_websocket**

      - **Expression**: Custom ACL
      - **Value**: `hdr(Upgrade) -i websocket`

    3. **ACL Name: service1** (e.g., **jf** for Jellyfin)

      - **Expression**: Host Matches
      - **Value**: `service1.yourdomain.com`

    4. **ACL Name: service2** (e.g., **emby** for Emby)

      - **Expression**: Host Matches
      - **Value**: `service2.yourdomain.com`

    ### Actions Configuration

    Specify the actions to route the requests based on ACLs and headers:

    1. **Set WebSocket Headers** (Place these before backend routing actions)

      - **Action**: `http-request set-header`

        - **Name**: `Connection`
        - **Fmt**: `upgrade`
        - **Condition ACL Names**: `{ req.hdr(Upgrade) -i websocket }`

      - **Action**: `http-request set-header`

        - **Name**: `Upgrade`
        - **Fmt**: `%[req.hdr(Upgrade)]`
        - **Condition ACL Names**: `{ req.hdr(Upgrade) -i websocket }`

    2. **Use Backend (Routing based on ACLs)**

      - **Backend: service1-backend** (e.g., **jellyfin-backend**)
        - **Condition ACL Names**: `hdr_connection_upgrade hdr_upgrade_websocket or service1`
      - **Backend: service2-backend** (e.g., **emby-backend**)
        - **Condition ACL Names**: `hdr_connection_upgrade hdr_upgrade_websocket or service2`

    ### Advanced Settings for Frontend

    To support stable WebSocket connections, configure advanced settings as follows:

    1. **Client Timeout**: Set a longer client timeout to prevent premature disconnections.

      - **Value**: `3600000` ms (1 hour)

    2. **HTTP Close Option**: Set to `http-keep-alive (default)` to maintain persistent connections.

    3. **Advanced Pass Thru** (Paste into the advanced pass-through field for the frontend):

      ```
    http-request set-header X-Forwarded-Port %[dst_port]
    http-request add-header X-Forwarded-Proto https if { ssl_fc }
    http-response set-header Strict-Transport-Security max-age=31536000;includeSubDomains;preload
    http-response set-header X-Frame-Options "SAMEORIGIN"
    http-response set-header X-Content-Type-Options "nosniff"
    http-response set-header Referrer-Policy no-referrer-when-downgrade
    http-request set-header X-Real-IP %[src]
    http-response set-header X-XSS-Protection "1; mode=block"
    http-response set-header Feature-Policy "geolocation 'none'"
    http-response set-header Permissions-Policy "geolocation=(), microphone=(), camera=()"
    http-response set-header X-Download-Options "noopen"
    http-request set-header Connection "upgrade" if { req.hdr(Upgrade) -i WebSocket }
    http-request set-header Upgrade %[req.hdr(Upgrade)]
    http-response set-header Connection "keep-alive"
    http-response set-header Expect-CT "enforce, max-age=86400"
    http-response del-header Server
    http-response del-header X-Powered-By
    option http-server-close
    option forwardfor

      ```

    ### Backend Timeout and Retry Settings

    Configure the backend with appropriate timeout values to handle long-running streams.

    1. **Connection Timeout**: `60000` ms (1 minute)
    2. **Server Timeout**: `3600000` ms (1 hour)
    3. **Retries**: `5000` (Number of attempts to reconnect after failures)

    ### Backend Pass Through Settings

    Paste the following configuration in the **Backend Pass Thru** box:

    ```
    http-request set-header X-Forwarded-Port %[dst_port]
    http-request add-header X-Forwarded-Proto https if { ssl_fc }
    server service1 192.168.XXX.XXX:8096
    http-response set-header Cache-Control "no-cache, no-store, must-revalidate"
    http-response del-header Server
    option http-server-close
    option forwardfor
    ```

    Examples and screenshots (screenshot is below example):

    Settings Tab:
    General Settings:
    Max Connections 10000
    [Image: TAG8iy8.png]
    Logging:
    /var/run/log
    local0
    Debugging
    [Image: fHTjU2O.png]


    Backend Tab:
    Edit HAPRroxy Backend server pool:
    Create your Backend
    Name Jellyfin
    Server: can be anything but needs to be consistent mine is 'jf' (lowercase) | include your address and port (default 8096) | No SSL  | No SSL checks
    Timeout / retry settings:
    Connection timeout 60000
    Server timeout  3600000
    Retries  5000
    [Image: 25ApO5m.png]
    Advanced Settings:
    Backend pass thru (change 192.168.x.x to your JF IP)
    Code:
    http-request set-header X-Forwarded-Port %[dst_port]
    http-request add-header X-Forwarded-Proto https if { ssl_fc }
    server jf 192.168.X.X:8096
    http-response set-header Cache-Control "no-cache, no-store, must-revalidate"
    http-response del-header Server
    option http-server-close
    option forwardfor
    [Image: EhneyNY.png]


    Frontend Tab:
    Edit HAProxy Frontend:Your listening address (should be wan or whatever gw you're using)Port 443 with SSL offloading checked
    [Image: 1s8WfzP.png]
    Access Control lists: (Order Matters)
    Have these entries first
    hdr_connection_upgrade | Custom acl: | hdr(Connection) -i upgrade
    hdr_upgrade_websocket  | Custom acl: | hdr(Upgrade) -i websocket
    Then your host matches second
    name jf (or whatever the backend server name was (not the list name) )
    jf | host matches | jf.yourdomain.com
    [Image: Gdeq2W0.png]
    Actions: (Order Matters) 
    http-request header set  | { req.hdr(Upgrade) -i websocket }
    • name: Connection
    • fmt: upgrade
    http-request header set | { req.hdr(Upgrade) -i websocket } 
    • name: Upgrade
    • fmt: %[req.hdr(Upgrade)]
    Use Backend | jf hdr_connection_upgrade hdr_upgrade_websocket or jf
    • backend: jellyfin
    [Image: waKgu1O.png]
    Advanced Settings:
    Client timeout: 7200000
    Advanced pass thru:
    Code:
    http-request set-header X-Forwarded-Port %[dst_port]
    http-request add-header X-Forwarded-Proto https if { ssl_fc }
    http-response set-header Strict-Transport-Security max-age=31536000;includeSubDomains;preload
    http-response set-header X-Frame-Options "SAMEORIGIN"
    http-response set-header X-Content-Type-Options "nosniff"
    http-response set-header Referrer-Policy no-referrer-when-downgrade
    http-request set-header X-Real-IP %[src]
    http-response set-header X-XSS-Protection "1; mode=block"
    http-response set-header Feature-Policy "geolocation 'none'"
    http-response set-header Permissions-Policy "geolocation=(), microphone=(), camera=()"
    http-response set-header X-Download-Options "noopen"
    http-request set-header Connection "upgrade" if { req.hdr(Upgrade) -i WebSocket }
    http-request set-header Upgrade %[req.hdr(Upgrade)]
    http-response set-header Connection "keep-alive"
    http-response set-header Expect-CT "enforce, max-age=86400"
    http-response del-header Server
    http-response del-header X-Powered-By
    option http-server-close
    option forwardfor
    [Image: 5GUcJa6.png]

    Helpful additions internal DNS settings/config:  This is assuming your using your pfsense for local DNS
    Code:
    Just an FYI if you have a TV or something static(non mobile) I wouldn't use the host name just use the IP. This routes the traffic through the pfsense box to your device instead of Server -> Device it becomes Server ->pfsense -> device


    If you're using your hostname (jf.yourdomain.com) internally (you need to add your LAN address to your front end or have a separate fronted just for internal)

    Frontend:
    [Image: JD1c1sO.png]

    Go To Services>DNS Resolver:
    [Image: qPBVTJZ.png]

    Go to the bottom of the page to "Host Overides"
    and click "Add" and fill in the following
    https://i.imgur.com/Gf2YZWh.png
    https://i.imgur.com/pQfevpI.png

    click save add another entry for your jellyfin
    for JF.YOURDOMAIN.com you would put it like this:
    https://i.imgur.com/Vk99iY9.png
    1
    jaillybelly
    Offline

    Junior Member

    Posts: 3
    Threads: 0
    Joined: 2024 Dec
    Reputation: 0
    Country:Canada
    #2
    2024-12-11, 05:05 AM
    Thanks for another fantastic guide gaming09! A question I have is: what HAproxy version is this guide appropriate for, and what versions was the previous guide made for? I noticed in your screenshots that you appear to be on HAproxy-devel and not stable HAproxy. The reason I ask is that I have been encountering various websocket errors in the logs that seem to coincide with playback stuttering. I realized I may be using the wrong guide for my HAproxy version but I don't know which one to use.

    Thanks again for the incredibly detailed guide.
    1
    gaming09
    Offline

    Member

    Posts: 67
    Threads: 24
    Joined: 2023 Jun
    Reputation: 1
    Country:United States
    #3
    2024-12-11, 05:46 PM (This post was last modified: 2024-12-11, 05:47 PM by gaming09.)
    (2024-12-11, 05:05 AM)jaillybelly Wrote: Thanks for another fantastic guide gaming09! A question I have is: what HAproxy version is this guide appropriate for, and what versions was the previous guide made for? I noticed in your screenshots that you appear to be on HAproxy-devel and not stable HAproxy. The reason I ask is that I have been encountering various websocket errors in the logs that seem to coincide with playback stuttering. I realized I may be using the wrong guide for my HAproxy version but I don't know which one to use.

    Thanks again for the incredibly detailed guide.

    Hey, Im running 2.9.10, dlevel is haproxy-devel-2.9.d11 but this guide should work on both since they were merged a version or two back.  What errors have you been getting.
    Stats:
    JF running on Unraid Docker
    Library Transcoded to h265 mkv via TDARR
    Ram Drive Transcode
    5950x
    a770
    60tb
    jaillybelly
    Offline

    Junior Member

    Posts: 3
    Threads: 0
    Joined: 2024 Dec
    Reputation: 0
    Country:Canada
    #4
    2024-12-14, 09:25 PM
    Great, thanks for filing that in gaming09. I've narrowed down the issue actually to a bug in the interaction between jellyfin and wireguard tunnels. Discussion is here: https://forum.jellyfin.org/t-websockets-...g-playback
    shm0
    Offline

    Junior Member

    Posts: 3
    Threads: 1
    Joined: 2025 Jan
    Reputation: 0
    #5
    2025-01-18, 02:19 AM (This post was last modified: 2025-01-19, 07:51 PM by shm0. Edited 5 times in total.)
    Hi!
    Thank you for this guide.
    I have some questions!
    Doesn't
    http-response set-header Connection "keep-alive"
    override
    http-request set-header Connection "upgrade" if { req.hdr(Upgrade) -i WebSocket }
    ?
    I tried to add some conditional checks (if !{ req.hdr(Upgrade) -i WebSocket }) to the set keep-alive rule but it still breaks websockets on some Smart TV apps.
    And X-Frame-Options "SAMEORIGIN" breaks the LG WebOS App (Black screen after login).

    And these acls:
    jf hdr_connection_upgrade hdr_upgrade_websocket or jf -> will always evaluate to jf
    emby hdr_connection_upgrade hdr_upgrade_websocket or emby -> will always evaluate to emby
    making hdr_connection_upgrade hdr_upgrade_websocket obsolete.

    I'm not sure if it is actually needed to set:
    option http-server-close
    option forwardfor
    and some other options, in both the fronted and backend.
    Is it actually a good idea to enable http-server-close for non websocket traffic?
    The timeouts also seem quite high?

    //edit
    http-server-close actually seems to break websockets for me...
    The only relevant timeout for websockets seems to be the tunnel one.
    dreunion61
    Offline

    Junior Member

    Posts: 7
    Threads: 1
    Joined: 2024 Nov
    Reputation: 0
    Country:Germany
    #6
    2025-02-15, 01:20 PM
    I don't get it, why is the old tutorial not valid anymore? I'm running the server with your old tutorial since November.

    Jellyfin 10.10.5
    pfSense Plus 24.11

    Not once I had a problem.
    « Next Oldest | Next Newest »

    Users browsing this thread: 1 Guest(s)


    • View a Printable Version
    • Subscribe to this thread
    Forum Jump:

    Home · Team · Help · Contact
    © Designed by D&D - Powered by MyBB
    L


    Jellyfin

    The Free Software Media System

    Linear Mode
    Threaded Mode