Skip to main content

Nginx configuration

Demo install nginx configuration example

    # Demo install
server {
listen *:80;
server_name demo.livehelperchat.com;
root /<path_to_folder>/livehelperchat_demo_com;
access_log /var/log/nginx/access_demo_livehelperchat.log main;

location ~* (^(?!(?:(?!(php)).)*/(albums|bin|var|lib|cache|doc|settings|pos|modules)/).*?(index\.php|upgrade\.php)$) {
include /etc/nginx/fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param PATH_INFO $query_string;
fastcgi_param SCRIPT_FILENAME /<path_to_folder>/livehelperchat_demo_com/$fastcgi_script_name;
}

# Do not allow to hotlink full size images except our self and major search engines
location ~* \.(gif|jpe?g?|png|bmp|swf|css|js|svg|otf|eot|ttf|woff|woff2|swf|mp3|map|ogg|wasm|wav|pdf|ico|txt)$ {

if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
#
# Custom headers and headers various browsers *should* be OK with but aren't
#
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
#
# Tell client that this pre-flight info is valid for 20 days
#
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}

# We don't want to allow bot to load our stuff. No point...
# If you are using Cloudflare or any other CDN Cache make sure you have rule so it won't cache empty.
# In general I suggest do not use this in case you have CDN because of complexity it brings.
if ($http_user_agent ~* "(google|baidu|bing|msn|duckduckbot|teoma|slurp|yandex|Chrome-Lighthouse)" ) {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
return 200;
}

if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
}

aio on;
directio 512;

expires max;
root /<path_to_folder>/livehelperchat_demo_com;
}

# This is required if you are running nodeJs extension
location /socketcluster/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;

proxy_pass http://127.0.0.1:8000;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}

set $track_pixel "N";

if ($uri ~ /mailconv\/tpx/?) {
set $track_pixel "T";
}

if ($http_user_agent ~* "(Google-RCS-Conversation)" ) {
set $track_pixel "T";
}

if ($http_user_agent ~* "(google|baidu|bing|msn|duckduckbot|teoma|slurp|yandex|Chrome-Lighthouse)" ) {
set $track_pixel "${track_pixel}Y";
}

location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
#
# Om nom nom cookies
#
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
#
# Custom headers and headers various browsers *should* be OK with but aren't
#
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
#
# Tell client that this pre-flight info is valid for 20 days
#
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}

# We don't want to allow bot to load our stuff. No point...
# If you are using Cloudflare or any other CDN Cache make sure you have rule so it won't cache empty.
# In general I suggest do not use this in case you have CDN because of complexity it brings.
if ($track_pixel = "NY") {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, FETCH, PUT, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
return 200;
}

rewrite "^(.*)$" "/index.php?$1" last;
}
}

Example for subdirectory (not tested by me)

    location /lhc_directory {
try_files $uri $uri/ /lhc_directory/index.php?q=$uri&$args;
}

fastcgi_params file​

    fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;

fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param HTTPS $https if_not_empty;

fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;

fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;

fastcgi_buffers 256 16k;
fastcgi_max_temp_file_size 0;
fastcgi_buffer_size 64k;

Sound messages support​

Also if you are enabling voice messages make sure your nginx sends proper header for wasm file type.
/etc/nginx/mime.types

You might need to add this
application/wasm wasm;

http section example​

http {
log_format main '$remote_addr $http_host - [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

sendfile on;
tcp_nopush on;
tcp_nodelay on;
types_hash_max_size 2048;

server_tokens off;

gzip on;
gzip_static on;
gzip_comp_level 1;
gzip_min_length 0;
gzip_types text/css image/x-icon image/bmp application/x-javascript application/javascript text/javascript application/json;
gzip_proxied any;
gzip_http_version 1.1;
gzip_disable "MSIE [1-6]\.";
gzip_vary on;

keepalive_timeout 10 10;

client_max_body_size 128m;
client_body_buffer_size 128k;

client_header_buffer_size 128k;
large_client_header_buffers 4 64k;
server_names_hash_max_size 4112;
server_names_hash_bucket_size 128;

# Rest of default config

Running apache under nginx proxy​

If your SSL connection is terminated under nginx, php app has to know that it was served through https. Here is an example how nginx configuration section might look like

location / {
proxy_hide_header Access-Control-Allow-Origin;
add_header 'Access-Control-Allow-Origin' 'domain.example.com';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
proxy_pass http://lhc/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}

Using Nginx as a proxy server to LHC installation with limiting exposed URL's​

Please adjust to your needs.

  • Only chat module is exposed without mobile app support.
  • NodeJS Extension and websockets taken into account.
server {
server_name chat.example.com; # Change to your domain

# Logging configuration
access_log /var/log/nginx/lhc_proxy_access.log;
error_log /var/log/nginx/lhc_proxy_error.log;

# Default deny all locations
location / {
return 403;
}

# Remove or keep. Based on our backend server address.
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;

# LiveHelperChat backend server
set $backend_server "http://127.0.0.1:88"; # Change to your actual backend

# SSL settings for backend connections - ignore certificate errors
proxy_ssl_verify off;
proxy_ssl_server_name on;
proxy_ssl_session_reuse on;

# Critical proxy header settings - ensure these are applied to all locations
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

# Connection timeouts
proxy_connect_timeout 300;
proxy_send_timeout 300;
proxy_read_timeout 300;

# Buffer settings
proxy_buffer_size 16k;
proxy_buffers 4 16k;
proxy_busy_buffers_size 16k;
client_max_body_size 10m;

# Allow only specific URLs
location /socketcluster/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass $backend_server;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}

##############
# NodeJS module.

location ~ "^/([a-z]{2,3}/)?nodejshelper/tokenvisitor(/.*)?$" {
proxy_pass $backend_server;
}

##############
# Webhooks module. Required for third party chats to work.

location ~ "^/([a-z]{2,3}/)?webhooks/incoming(/.*)?$" {
proxy_pass $backend_server;
}

##############
# CHAT Module

# Widget REST API module - all allowed paths. Main widget calls.
location ~ "^/([a-z]{2,3}/)?widgetrestapi/(.*)$" {
proxy_pass $backend_server;
}

# Captcha module
location ~ "^/([a-z]{2,3}/)?captcha/captchastring(/.*)?$" {
proxy_pass $backend_server;
}

# Chat module - all allowed paths
location ~ "^/([a-z]{2,3}/)?chat/(verifytoken|updateattribute|updatejsvars|logevent|setnewvid|refreshcustomfields|addmsguser|editprevioususer|updatemsguser|getmessage|voteaction|syncuser|transfertohuman|editnick|usertyping|checkchatstatus|getstatus|htmlsnippet|chatcheckstatus|getstatusembed|startchat|start|begin|modal|chatwidget|reopen|readoperatormessage|chatcheckoperatormessage|extendcookie|logpageview|chatwidgetclosed|chat|printchat|downloadtxt|readchatmail|chatpreview|bbcodeinsert|chatwidgetchat|userclosechat|accept|confirmleave|reacttomessagemodal|sendchat)(/.*)?$" {
proxy_pass $backend_server;
}

# File module
location ~ "^/([a-z]{2,3}/)?file/(uploadfile|fileoptions|uploadfileonline|downloadfile|storescreenshot)(/.*)?$" {
proxy_pass $backend_server;
}

# Generic Bot module
location ~ "^/([a-z]{2,3}/)?genericbot/(buttonclicked|updatebuttonclicked)(/.*)?$" {
proxy_pass $backend_server;
}

# Form module. Used if forms are embede in the wdiget.
location ~ "^/([a-z]{2,3}/)?form/formwidget(/.*)?$" {
proxy_pass $backend_server;
}

# Survey module
location ~ "^/([a-z]{2,3}/)?survey/(fillwidget|fill|fillinline|backtochat|isfilled)(/.*)?$" {
proxy_pass $backend_server;
}

# Theme module
location ~ "^/([a-z]{2,3}/)?theme/(admincss|gethash)(/.*)?$" {
proxy_pass $backend_server;
}

# Notifications module. Allows visitor to subscribe to chat notifications. Optional.
# location ~ "^/([a-z]{2,3}/)?notifications/(subscribe|read)(/.*)?$" {
# proxy_pass $backend_server;
# }

# Paid Chat module
# location ~ "^/([a-z]{2,3}/)?paidchat/(expiredchat|removedpaidchat|invalidhash)(/.*)?$" {
# proxy_pass $backend_server;
# }

# Product module. Uncomment if you are using products feature in the chat.
# location ~ "^/([a-z]{2,3}/)?product/getproducts(/.*)?$" {
# proxy_pass $backend_server;
# }

# Voice Video module. Uncomment if you are using voice calls feature.
# location ~ "^/([a-z]{2,3}/)?voicevideo/(call|join)(/.*)?$" {
# proxy_pass $backend_server;
# }

###############

# MAIL Conversion module

# Tracking pixel
location ~ "^/([a-z]{2,3}/)?mailconv/tpx(/.*)?$" {
proxy_pass $backend_server;
}

# Mail Conversation Authentication module
location ~ "^/([a-z]{2,3}/)?mailconvoauth/msoauth(/.*)?$" {
proxy_pass $backend_server;
}

# CoBrowse module. Uncomment if you are using co browsing
# location ~ "^/([a-z]{2,3}/)?cobrowse/(storenodemap|finishsession)(/.*)?$" {
# proxy_pass $backend_server;
# }

# Other optional public endpoints

# Audit module. Just test url for debuging purposes.
# location ~ "^/([a-z]{2,3}/)?audit/test(/.*)?$" {
# proxy_pass $backend_server;
# }

# ChatBox module
# location ~ "^/([a-z]{2,3}/)?chatbox/(syncuser|addmsguser|chatwidget|chatwidgetclosed|configuration)(/.*)?$" {
# proxy_pass $backend_server;
# }

# Export module
# location ~ "^/([a-z]{2,3}/)?export/(getchat|getcount)(/.*)?$" {
# proxy_pass $backend_server;
# }

# FAQ module
# location ~ "^/([a-z]{2,3}/)?faq/faqwidget(/.*)?$" {
# proxy_pass $backend_server;
# }

# Group Chat module. Can be used as API endpoint also.
# location ~ "^/([a-z]{2,3}/)?groupchat/(addmessage|sync)(/.*)?$" {
# proxy_pass $backend_server;
# }

# Install module
# location ~ "^/([a-z]{2,3}/)?install/install(/.*)?$" {
# proxy_pass $backend_server;
# }

# REST API module - all allowed paths
# location ~ "^/([a-z]{2,3}/)?restapi/(.*)$" {
# proxy_pass $backend_server;
# }

# System module. Responsible for confirm dialog content.
# location ~ "^/([a-z]{2,3}/)?system/(autodbupdate|confirmdialog)(/.*)?$" {
# proxy_pass $backend_server;
# }

# User module - all allowed paths
# location ~ "^/([a-z]{2,3}/)?user/(login|autologin|autologinuser|logout|loginasuser|forgotpassword|remindpassword|setsetting|setsettingajax|setsettingajaxraw)(/.*)?$" {
# proxy_pass $backend_server;
# }

# XML module - all allowed paths. Mobile application endpoints
# location ~ "^/([a-z]{2,3}/)?xml/(checklogin|closedchats|lists|getuseronlinestatus|setonlinestatus|deletechat|chatdata|cannedresponses|chatssynchro|closechat|addmsgadmin|transferchat|transferuser|accepttransfer|accepttransferbychat|sendnotice)(/.*)?$" {
# proxy_pass $backend_server;
# }

# Disable access to doc folder
location ~ "^/doc(.*)?$" {
return 403;
}

# Static file handling - only if needed
location ~* \.(eot|woff|ttf|svg|gif|jpe?g?|png|bmp|swf|css|js|swf|mp3|ogg|map|wav|pdf|ico|txt|wasm|woff2)$ {
proxy_pass $backend_server;
expires 30d;
}

listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/chat.example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/chat.example.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
if ($host = chat.example.com) {
return 301 https://$host$request_uri;
} # managed by Certbot


listen 80;
server_name chat.example.com;
return 404; # managed by Certbot
}