I'm hosting an ajax site (AngularJS) and it's assets/partials reside on my static asset servers running nginx behind haproxy.

My setup looks as follows:

Web -> Haproxy -> App (custom) and Static (nginx) servers

Now it happens from time to time that specific assets (HTML partials) just don't load, or only begin loading after one or more browser refreshes (Chrome's network devtools shows 'pending' for that request).

I don't know what's causing this, since the assets/partials are all in the same folder, and others are just loaded fine.

Here are the 2 configs, maybe you can spot something fishy?
How can I debug such issues efficiently?

haproxy.cfg.j2: (this is a Jinja2 template, so don't be confused by template syntax)

# requires haproxy 1.5+

  log local0
  log local1 notice
  maxconn 4096
  user haproxy
  group haproxy

  log global
  mode http
  maxconn 4096

  # Add x-forwarded-for header
  option forwardfor
  option redispatch
  option dontlognull
  option http-server-close

  timeout connect         5s
  timeout client          30s
  timeout server          30s
  timeout tunnel          15m
  timeout http-keep-alive 1s
  timeout http-request    15s
  timeout queue           30s
  timeout tarpit          60s

frontend public
  mode http

  bind :80
  bind :443 ssl crt /etc/ssl/haproxy.pem

  acl is_app    hdr_end(Host) -i api.example.com api-stage.example.com
  acl is_static hdr_end(Host) -i example.com stage.example.com
  acl is_io     hdr_end(Host) -i example.io stage.example.io
  acl is_ws     hdr(Upgrade)  -i WebSocket

  # Redirect HTTP to HTTPS
  # Make sure we don't redirect WebSocket requests otherwise
  # the browser might complain because of the returned 302 status
  redirect scheme https if !{ ssl_fc } is_app !is_ws
  redirect scheme https if !{ ssl_fc } is_static !is_ws

  # To force example.io on SSL we'd need a 2nd certificate
  # redirect scheme https if !{ ssl_fc } is_io !is_ws

  use_backend bk_notify if is_ws
  use_backend bk_app if is_app
  use_backend bk_files if is_io

  default_backend bk_static

backend bk_static
  reqadd X-Forwarded-Proto:\ https
  balance leastconn
  {% for m in servers.static %}
  server {{ m.name }} {{ m.private_ip_address }}:80 weight 1 maxconn 1024 check
  {% endfor %}

backend bk_app
  reqadd X-Forwarded-Proto:\ https
  balance hdr(Authorization)
  {% for m in servers.app %}
  server {{ m.name }} {{ m.private_ip_address }}:8001 weight 1 maxconn 1024 check
  {% endfor %}

backend bk_notify
  balance leastconn
  {% for m in servers.app %}
  server {{ m.name }} {{ m.private_ip_address }}:8001 weight 1 maxconn 1024 check
  {% endfor %}

backend bk_files
  reqadd X-Forwarded-Proto:\ https
  balance leastconn
  {% for m in servers.app %}
  server {{ m.name }} {{ m.private_ip_address }}:8002 weight 1 maxconn 1024 check
  {% endfor %}

listen stats :1936
  mode http
  stats enable
  stats hide-version
  stats realm Haproxy\ Statistics
  stats uri /
  stats auth iwant:thestats


user www-data;
worker_processes 4;
pid /var/run/nginx.pid;

events {
    worker_connections 768;
    # multi_accept on;

http {    
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    gzip on;
    gzip_disable "msie6";
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

    server {
        #listen   80; ## listen for ipv4; this line is default and implied
        #listen   [::]:80 default ipv6only=on; ## listen for ipv6

        root /usr/share/nginx/www;
        index index.html index.htm;
        server_name localhost;

        location / {
            try_files $uri $uri/ /index.html;

        # This block will catch static file requests, such as images, css, js
        # The ?: prefix is a 'non-capturing' mark, meaning we do not require
        # the pattern to be captured into $1 which should help improve performance
        location ~* \.(?:ico|css|js|gif|jpe?g|png|woff|eot|ttf|svg)$ {
            expires max;
            add_header Pragma public;
            add_header Cache-Control "public, must-revalidate, proxy-revalidate";

        # Content that should not be cached
        location ~* \.(?:html|htm|txt)$ {
            expires 0;
            add_header Cache-Control "private, must-revalidate, proxy-revalidate";

        # this prevents hidden files (beginning with a period) from being served
        location ~ /\. {
            access_log off;
            log_not_found off;
            deny all;
  • Is nginx loging something when this happens?
    – B14D3
    Dec 5 '13 at 12:45
  • nginx access and error logs do not show anything regarding this. error log is empty. I suspect haproxy is the culprit.
    – Era
    Dec 5 '13 at 13:30
  • 1
    Well, then how about the HAproxy log?
    – GomoX
    Dec 5 '13 at 14:12
  • Well, if there were log contents to post, I'd have posted them :)
    – Era
    Dec 5 '13 at 14:15
  • You're logging to syslog -- is it definitely configured to accept logs from haproxy? Dec 6 '13 at 18:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.